Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

std::thread.join() deadlock

I don't understand why this simple snippet has a dead lock:

#include <atomic>
#include <thread>
#include <memory>

using namespace std;
class Test {
public:

    Test() : mExit( false )
    {
        mThread = thread( bind( &Test::func, this ) );
    }

    ~Test()
    {
        if ( mThread.joinable() )
        {
            mExit = true;
            mThread.join();
        }
    }

private:
    void func() 
    {
        while ( !mExit )
        {
            // do something
        }
    }

private:
    atomic< bool > mExit;
    thread mThread;
};

typedef unique_ptr< Test > TestPtr;
TestPtr gTest;

int main()
{
    gTest = TestPtr( new Test );
    return 0;
}

Edit I typed wrong the contstructor set mExit = true

Edit 2 I'm using msvc2012 with v110_xp toolset.

Edit 3 The issue disappear if I explicity call gTest.release() inside main

like image 358
Elvis Dukaj Avatar asked Jun 19 '13 13:06

Elvis Dukaj


2 Answers

I have just had this issue, so I post the real answer for others.

In visual studio at least, there is an "exit lock", that is locked when a thread enters the exit code (ie. after main() for the main thread, and after f() for std::thread(f)).

As your Test class is only destructed after main() completes, the "exit lock" is locked. Only then you set mExit = true; and the other thread is allowed to complete. This other thread then waits to obtain the "exit lock" which is already taken by the main thread, while the main thread waits in mThread.join(); resulting in the deadlock.

So yes, you need to join all your threads before the main thread has completed.

like image 195
god Avatar answered Nov 15 '22 18:11

god


To me the code looks okay, if its okay with local dut bad with global I'd suspect class related to deinit sequence. The join happens deeply in exit, and implementation might have eliminated some structures already. It should not be the case, but possible.

In any case I always avoid starting thread before main, and leaving any reaching end of main. I consider that only asking for trouble. If you can rearrange it to force join at little erlier point the whole problem might evaporate.

Also you should probably use atomic_flag over atomic.

like image 38
Balog Pal Avatar answered Nov 15 '22 18:11

Balog Pal