Elvis Dukaj
Elvis Dukaj

Reputation: 7368

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

Upvotes: 10

Views: 6414

Answers (3)

Alexei Valyaev
Alexei Valyaev

Reputation: 31

Changing code from

gTest = TestPtr( new Test );

to

auto gTest = std::make_unique();

eliminate the problem.

the problem as point above with global object.

Upvotes: 0

god
god

Reputation: 1698

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.

Upvotes: 8

Balog Pal
Balog Pal

Reputation: 17163

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.

Upvotes: 3

Related Questions