Topic 1: An RAII class for std::thread objects
In my GoingNative 2013 talk, I explained how destruction of joinable std::thread objects leads to program termination, and I introduced an RAII class, ThreadRAII, to make sure that joinable std::threads aren't destroyed. The details of ThreadRAII aren't important for this post, so here's a stripped-down version that always does a join before permitting a joinable std::thread to be destroyed:class ThreadRAII { public: ThreadRAII(std::thread&& thread): t(std::move(thread)) {} ~ThreadRAII() { if (t.joinable()) (t.join(); } private: std::thread t; };Using an RAII class to make sure that a std::thread object is brought into an unjoinable state on every path out of a block seems like an obviously-reasonable thing to do.
Topic 2: Using void promises/futures to "start threads suspended"
This Q&A at StackOverflow explains why it might be useful to start threads in a suspended state. There's no direct support for that in the C++11 threading API, but one way to implement it is to create a std::thread running a lambda that waits on a std::future<void>std::promise<void> p; std::thread t([&p]{ p.get_future().wait(); // start t and suspend it funcToRun(); } // (conceptually) ); ... // t is "suspended" waiting for p to be set p.set_value(); // t may now continueThis isn't the only way to suspend thread execution after creation of the thread and before execution of the work it's supposed to do, but it seems reasonable. In contrast to having the lambda spin on an atomic bool waiting for the flag to be set, for example, there is no need for the lambda to poll.
Putting the two together
The use of a std::thread object in that last code example leads to the possibility of there being some flow of control that could cause that object to be destroyed in a joinable state, and that would lead to program termination. So it seems natural to use RAIIThread:std::promise<void> p; // as before std::thread t([&p]{ p.get_future().wait(); // as before funcToRun(); } ); ThreadRAII tr(std::move(t)); // USE RAII CLASS HERE ... // as before p.set_value(); // as beforeThe problem is that if an exception is thrown in the "...", we'll never set the value of the std::promise, and that means that the destructor of the RAII object (tr) will block forever. That's no better than program termination, and it might be worse.
My question
I'm trying to figure out what the fundamental problem is here. Is there something wrong with using an RAII class to keep joinable threads from being destroyed (hence causing program termination)? Is there something wrong with using void promises/futures to emulate starting thread execution in a suspended state?If the two techniques are independently valid, am I combining them incorrectly, or are they somehow fundamentally incompatible?
I'd like to write about this in Effective C++11/14, but I want to offer my readers good advice. What should that advice be?
Thanks,
Scott