Kolyunya
Kolyunya

Reputation: 6240

Thread joining issue

I was reading some manuals about threads and I've come to a thought that the code they show is not safe:

std::cout << "starting first helper...\n";
std::thread helper1(foo);

std::cout << "starting second helper...\n";
std::thread helper2(bar);

std::cout << "waiting for helpers to finish..." << std::endl;
helper1.join();   // #1 NOT SAFE
helper2.join();   // #2 NOT SAFE

I believe this code is not absolutely safe. If I am not mistaking there is no guarantee that helper1 and helper2 are already in joinable state when control reaches lines marked as #1 and #2. Threads could still be not launched and have no ids at this point. Which will cause an uncaught exception being thrown from std::thread::join()

I think the following code fixes the problem. Am I right?

std::cout << "starting first helper...\n";
std::thread helper1(foo);

std::cout << "starting second helper...\n";
std::thread helper2(bar);

std::cout << "waiting for helpers to finish..." << std::endl;
while ( helper1.joinable() == false ) { }
helper1.join();   // #1 SAFE
while ( helper2.joinable() == false ) { }
helper2.join();   // #2 SAFE

Upvotes: 4

Views: 4200

Answers (3)

Jonathan Wakely
Jonathan Wakely

Reputation: 171413

The std::thread::thread(F&& f, Args&&... args) constructor has this postcondition:

Postconditions: get_id() != id(). *this represents the newly started thread.

The definition of joinable() is

Returns: get_id() != id()

Therefore the constructor's postcondition is that the object is joinable, and the postcondition applies as soon as the constructor completes. It is irrelevant whether the OS has actually started the thread yet, the thread object still knows the new thread's ID and can still wait for it to complete and join it.

Upvotes: 2

Yakk - Adam Nevraumont
Yakk - Adam Nevraumont

Reputation: 275878

A std::thread is joinable if it contains a thread state that has not been joined or detatched.

A std::thread gains a thread state by being non default constructed, or having one moveed into it from another std::thread. It loses it when moveed from.

There is no delay in gaining the thread state after construction completes. And it does not go away when the threaded function finishes. So there is not that problem.

There is the problem that if code throws above, you will fail to join or detatch, leading to bad news at program shutdown. Always wrap std::thread in a RAII wrapper to avoid that, or just use std::async that returns void and wrap the resulting std::future similarly (because the standard says it blocks in the dtor, but microsofts implementation does not, so you cannot trust if it will or not).

Upvotes: 6

Paul Evans
Paul Evans

Reputation: 27577

You are perceiving threads in an overly complicated way. join is there to safely join a thread. Just use:

std::thread my_thread(my_main);
my_thread.join();

Upvotes: 3

Related Questions