Scoped lock_guard Not Releasing Mutex in Static Method

292 views Asked by At

Platform Debian 9 | ARM

I have a class that is used to store thread IDs on a std::list and then print the list as part of the shutdown procedure. There are two static methods: AddId() and PrintIds(). Since the static methods are used by several threads, the list access is protected with a std::mutex. I have come across two issues that have me baffled and they may be related. I hope one of you can help explain what I am missing.

Note I first used scoped std::lock_guard<std::mutex>s and have temporarily settled on the std::unique_lock<std::mutex>s that are shown in the code for the reasons explained herein.

Issue 1 When the program starts, all threads call ThreadMgr::AddId(), which adds the thread id to the list along with a string name. That seems to work fine. However, when ThreadMgr::PrintIds() is called during the shutdown procedure, the std::mutex is still locked and the std::lock_guard blocks. While testing, I unlocked the std::mutex in the line preceding the std::lock_guard--which is undefined behavior if it wasn't locked by the calling thread--and then it worked as expected. The only place the std::mutex is used is in these two methods, and with the std::lock_guard being scoped, the std::mutex should have been unlocked with every method call. I went through several iterations of locking and unlocking the std::mutex directly, using std::unique_locks, etc. I eventually found a somewhat workable solution using the std::unique_locks that are in the code provided here. However, that too has me baffled.

The std::unique_locks work if I use std::adopt_lock, but only in ThreadMgr::PrintIds(). If I use std::adopt_lock in ThreadMgr::AddId(), then I get a segmentation fault whenever ThreadMgr::PrintIds() is called.

Issue 2 As previously stated, ThreadMgr::AddId() runs fine with either std::lock_guard or std::unique_lock when all threads call it on startup. However, there are UI sessions where each session spawns a thread. When ThreadMgr::AddId() is called from one of these session threads, both std::lock_guard and std::unique_lock block like the std::mutex is already locked. So, ThreadMgr::AddId() works perfectly fine for every other thread, but not for the session threads that are started later.

If there is any other information I can provide to help get to a solution, let me know.


class ThreadMgr {
 public:
    ThreadMgr();
    ~ThreadMgr();
    static void AddId(std::string);
    static void PrintIds();

 private:
    static std::list<ThreadId> t_list;
    static std::mutex list_mtx;
};

/*Definition*/
ThreadMgr::ThreadMgr() {}
ThreadMgr::~ThreadMgr() {}

/*Static Members*/
void ThreadMgr::AddId(std::string name) {
    
    ThreadId t_id = getThreadId(name);
    {
        std::unique_lock<std::mutex> lock(list_mtx);
        t_list.push_front(t_id);
    }

    std::lock_guard<std::mutex> lk(cout_mtx);
    std::cout << "---Added id: " << t_id.tid << " for the " << name << " thread." << std::endl;

    return;
}
void ThreadMgr::PrintIds() {
    
    std::ostringstream oss;
    oss << "\n**************************Thread Ids**************************\n";
    
    {   
        std::unique_lock<std::mutex> lock(list_mtx, std::adopt_lock);
        
        for (std::list<ThreadId>::iterator t = t_list.begin(); t != t_list.end(); t++) {
            oss << "---" << t->name << " : " << t->tid << '\n';
        }
    }
    oss << "************************End Thread Ids************************" << '\n';
            
    std::lock_guard<std::mutex> lk(cout_mtx);
    std::cout << oss.str() << std::endl;
    
    return;
}

std::mutex ThreadMgr::list_mtx;
std::list<ThreadId> ThreadMgr::t_list;

int Main(){

    ThreadMgr::AddId("Main");

    std::thread MbServerT(MbServer);
    std::thread UiServerT(UiServer);

    while(run){
        //do stuff
    }
    
    ThreadMgr::PrintIds();
    
    if(MbServerT.joinable())
        MbServerT.join();

    if(UiServerT.joinable())
        UiServerT.join();

    return 0;
}
0

There are 0 answers