[thread] terminating destructor

Hi, I can see in the release notes that in Boost 1.52 boost::thread's destructor calls terminate if joinable, in order to conform to C++11 specification. I am not sure if this is the best course of action. My understanding -- form the C++ Committee papers and informal presentations -- is that the reason for introducing a 'terminating destructor' was the lack of thread cancellation/interruption functionality. Thread interruption is supposed to be the preferred behavior for thread's destructor. std::thread does not support interruption (for some reasons), but boost::thread does (this is already a departure from C++11), so shouldn't the latter prefer to interrupt a joinable thread in the destructor? Regards, &rzej

2012/10/10 Vicente J. Botet Escriba <vicente.botet@wanadoo.fr>
My preference would be to join after the interruption. If I remember correctly, the argument against joining for std::thread is that there would be an unexpected hang upon reaching the end of the scope. The argument against detaching for std::thread is that the detached thread may be holding references to automatic variables defined in the scope of the forking thread that we are now exiting. I believe that with thread interruption in place the argument against joining is mitigated, while the argument against detaching still holds. Regards, &rzej

Andrzej Krzemienski wrote
2012/10/10 Vicente J. Botet Escriba <
vicente.botet@
Please, could you create a Track ticket? Thanks, Vicente -- View this message in context: http://boost.2283326.n4.nabble.com/thread-terminating-destructor-tp4636872p4... Sent from the Boost - Dev mailing list archive at Nabble.com.

Please, could you create a Track ticket?
Here it is: https://svn.boost.org/trac/boost/ticket/7496 Regards, &rzej

Le 10/10/12 14:56, Andrzej Krzemienski a écrit :
Hi, I've though more about your suggestion, and it seems to me that it has the same drawbacks as joining directly. The point is that interrupting a thread doesn't ensures that the thread will finish soon, as the thread could not call any interruption point, so that joining them later could take an undefined time. What do you think? Best, Vicente

On Oct 12, 2012, at 4:24 PM, "Vicente J. Botet Escriba" <vicente.botet@wanadoo.fr> wrote:
That seems a reasonable argument.
That seems like too much nannyism. C++ already has many ways to do similar things, so why be so concerned with this one case?
I agree. Hanging like that during stack unwinding would be painful. At least with terminate, you'd realize the problem. MT requires knowledge and skill. Too much handholding gets in the way and encourages naive users to get in over their head. If someone wants a thread to interrupt and join on destruction, they need only put the thread object in another object and have that destructor interrupt and join. ___ Rob

2012/10/13 Rob Stewart <robertstewart@comcast.net>
So are you saying the thread should detach on scope exit rather than terminate?
The approach to Boost libraries' interface I encountered so far is that they provide the simple interface for the newbies (that does a bit of nanying) and a more complicate interface for the professionals. To me, the interruption in boost::thread is like a good default behavior for the beginners. The professionals would surely not use naked std::thread. I would propose just the opposite. If you are sure you want your thread handle to terminate the program in destructor, write your wrapper class yourself, and terminate there, and use the joining destructor for the default boost::thread interface (for beginners). I believe that for beginners, termination may not be of use to identify the source of the problem (I may be wrong though). Yes, interrupting does not guarantee that you will not hang, however, it *mitigates* the hang argument. It puts different balance in the choice between joining and terminating. Perhaps the choice to terminate is still the best one. My problem with terminating destructor is that there is no safe way to use it other than wrap it in another class that takes a different approach in the destructor. This makes std::thread (and now boost::thread) like a pair of operators new and delete, which require that you do not use them directly, but write some wrapper instead. Well, this might be an acceptable situation after all. What bothers me is that I have seen many introductions to using threads in C++ that give such example: int work( Operation op, Input i1, Input i2 ) { int ans1 thread th{ [&]{ans1 = op(i1);} }; int ans2 = op(i2); return ans1 + ans2; } It looks like this code makes sense. But it is wrong. You could argue that one should start teaching multi-threading with async(), but somehow anyone chooses to start with thread. Interrupting and joining destructor would be good for novices. And it enables the scoped (RAII-like) reasoning about threads. People well familiar with boost::thread would know never to use it naked. Unless boost::thread itself should be considered a tool for professionals only; and novices should be encouraged to use async and futures? std::future does join without interruption. What does boost::future do? I think it should interrupt and join (or perhaps detach as per N3451). Regards, &rzej

Le 13/10/12 13:59, Andrzej Krzemienski a écrit : that is non cancelable threads, so that the user don't pay for what he don't use. There is already a feature request to provide a condition variable that is as efficient as the user could have using the pthread interface.
I don't understand this. Could you clarify? Best, Vicente

Sorry, I tried to say too much in one sentence. While std::thread's destructor terminates for joinable thread, std::future's destructor sort-of joins with the (implied) thread: it waits until the job is done. So we already have a potentially surprising suspension upon leaving the scope. I guess this is more acceptable for a higher level abstraction. Now, for boost::future, I could not figure out what it does in the destructor, but if it tries to follow std::future, it probably joins. In the case of the future, interruption appears even more appealing because you join anyway, and you can only speed the waiting up. Then I referred to paper N3451 ("async and ~future"): http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3451.pdf Herb Sutter observes that because future's destructor blocks, the following code: { async( []{ f(); } ); async( []{ g(); } ); } surprisingly, is executed synchronously (i.e., we do not launch the task executing g() until the task executing f() has finished). He proposes a change to std::future to detach in destructor. I just mention it because if boost::future tries to follow std::future, this may become necessary one day. Regards, &rzej

Le 13/10/12 16:08, Andrzej Krzemienski a écrit :
0. If the implementation chooses the launch::async policy, * — a call to a waiting function on an asynchronous return object that shares the shared state created by this async call shall block until the associated thread has completed, as if joined (30.3.1.5); C++ International Standard Otherwise ~std::future(); Effects: — releases any shared state (30.6.4); — destroys *this. Could you explain me what waiting function is called on the future destructor that needs to block until the completion of the associated thread? the committee had to do it this way. Maybe you could help me.
Maybe yes, maybe not. Note that N3451 is a proposal and has not been adopted. I'm not sure this will be accepted (see the thread [c++std-lib-33127] Re: Herb's paper N3451 on std::async and ~future). Anyway I need to understand better why the standard recommends to join the associated thread for the waiting operations. Best, Vicente

2012/10/13 Vicente J. Botet Escriba <vicente.botet@wanadoo.fr>
It is not any waiting function mentioned explicitly. It is the requirement in 30.6.8 para 5: "If the implementation chooses the launch::async policy, [...] the associated thread completion synchronizes with the return from the first function that successfully detects the ready status of the shared state or with the return from the last function that releases the shared state, whichever happens first." Regards, &rzej

2012/10/14 Vicente J. Botet Escriba <vicente.botet@wanadoo.fr>
Indeed, it doesn't have to be the destructor. I misinterpret this requirement. Sorry. I no longer claim that future's destructor blocks in this case. Although, others (like Herb) seem to claim that. I will investigate that. Regards, &rzej

2012/10/14 Andrzej Krzemienski <akrzemi1@gmail.com>
Or perhaps future destructor IS the last function that release the shared state. When we call async() two threads are involved: our 'master' thread and a newly launched thread. Whatever function(s) releases the shared state it has to do it from one of the two threads. The last release cannot be made from the 'launched' thread because 'launched' thread completion synchronizes with the last release. So the last release has to be performed from the 'master' thread. And what other operation in the 'master' thread apart from future's destructor can release the state? Regards, &rzej

On Wed, Oct 24, 2012 at 7:35 AM, Andrzej Krzemienski <akrzemi1@gmail.com> wrote:
I don't know the details, but it was clear from the discussions at the standard's meeting, that std::future blocks in its destructor - when originating from std::async(). But not in other cases. Which is completely inconsistent, and makes it hard to have a function that accepts a std::future - you no longer know where it came from and whether it blocked. The committee would really like to resolve that issue. Either always block, or never block. Possibly deprecating std::async() and replacing it with something that returns a non-blocking future. Or some other solution. As for whether users should use std::thread or something higher level - I think something higher. But not just a wrapper - a different mechanism. I think users should be encouraged to use an "executor". ie a thread-pool where you give it std::function object that it runs on other threads. Like std::async() but with more control of the threading, yet avoiding actual thread management. Also very similar to Apple's Grand Central Dispatch. I know a number of Adobe apps switched completely from using threads to using executors. An executor has been proposed for C++1y. Maybe someone should add one to boost? Tony

Le 24/10/12 19:47, Gottlob Frege a écrit : point me to the details of the problem. thread_pool proposal. I didn't understood never why they didn't let the launch policy a template parameter. I would like to understand hwat is the bug on the async/future standard. Anthony, maybe you can clarify it. think some wrappers could make easier the work for high library writers. Note that I'm not proposing them for C++1y standardization, but to possible tools a Boost author can use.
I stated some years ago Boost.Async (sandbox) that is based on the concept of asynchronous executors and asynchronous completion tokens. Given the interest that there in now for executors I should resurrect the project ;-) Best, Vicente

2012/10/24 Vicente J. Botet Escriba <vicente.botet@wanadoo.fr>
I believe that when you are saying "there is no problem", you are referring to the following situation: master thread | launched thread -------------------------+--------------------------- async call >-- sync --> start executing ... | ... ... (other stuff) | ... (other stuff) ... | ... future dtor start | release state release state <-- sync -- finished execution future dtor end | (please use fixed-size font to display this ASCII-Art diagram) In such case, where the the launched thread finishes faster than the master thread the blocking in ~future will be a no-time. But if the launched thread takes longer, we have the following situation (and a noticable block): master thread | launched thread -------------------------+--------------------------- async call >-- sync --> start executing ... | ... ... (other stuff) | ... (other stuff) ... | ... future dtor start | ... (blocked) | ... | ... | ... | release state release state <-- sync -- finished execution future dtor end | The third situation below shows a non blocking ~future. But such execution is *disallowed *by the standard because the completion of launched thread would not synchronize with the last release of the shared state. master thread | launched thread -------------------------+--------------------------- async call >-- sync --> start executing ... | ... ... (other stuff) | ... (other stuff) ... | ... future dtor start | ... release state | ... future dtor end | ... | ... | release state | finished execution Regards, &rzej

2012/10/25 Olaf van der Spek <ml@vdspek.org>
According to my (limited) understanding of the standard: no. Because, as per requirement in Sect 30.6.8 paragraph 5, the "launched" thread completion must synchronize with (happen before) the last function that releases the shared state. If the launched thread finishes before the release, the thread cannot perform this release. Regards, &rzej

2012/10/25 Olaf van der Spek <ml@vdspek.org>
Basically, as with the thread's destructor: you have to do something: join, detach, signal run-time error. Your preference is clearly to detach. I could only found this document: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2802.html That gives some rationale against the detachment. My guess is that termination was a good choice for a low-level primitive, and join was a good candidate for a higher-level primitive like async. async (owing to the joining destructor) gives you the ability to think in C-style scoped manner. Or in RAII way. You exit the scope, you are done with the resource (thread in this case). One may not like this async behaviour. One may write one's own multi-threading library using low-level threads. I guess it was the idea of the Kona compromise. Regards, &rzej

Hi,
That is what I thought in the first place as well. Yet, the object to destroy will live on the stack of the main thread. Before the function can return, the stack frame has to be released and therefore the objects in it have to be destroyed. When the destruction is done by the launched thread, either the main thread has to wait and there is nothing you can gain that way, or the stack would need to be forkable. That would break almost all C++ ABIs around the world, I guess. Christof -- okunah gmbh Software nach Maß Zugspitzstr. 211 www.okunah.de 86165 Augsburg cd@okunah.de Registergericht Augsburg Geschäftsführer Augsburg HRB 21896 Christof Donat UStID: DE 248 815 055

Le 25/10/12 09:56, Andrzej Krzemienski a écrit :
Yes, this is the current behavior of Boost.Thread, which as I said is not C++ compliant. I understand that the launched thread can not synchronize with itself. I would say just that the completion of the thread is just after the last release of the shared state. More on the replay to other post. Thanks for the detailed explanation, Vicente

on Sat Oct 13 2012, Andrzej Krzemienski <akrzemi1-AT-gmail.com> wrote:
Just so you know, that paper is already controversial in the committee mailing lists. It will be interesting to see where consensus eventually settles, but at this point I wouldn't bet on any particular outcome. -- Dave Abrahams BoostPro Computing Software Development Training http://www.boostpro.com Clang/LLVM/EDG Compilers C++ Boost

Le 13/10/12 14:40, Vicente J. Botet Escriba a écrit :
What do you think of adding a thread_guard class that could interrupt and join on the destructor? Example taken from "C++ Concurrency in Action" from A. Williams #include <thread> class thread_guard { std::thread& t; public: explicit thread_guard(std::thread& t_): t(t_) {} ~thread_guard() { if(t.joinable()) { t.join(); } } thread_guard(thread_guard const&)=delete; thread_guard& operator=(thread_guard const&)=delete; }; void do_something(int& i) { ++i; } struct func { int& i; func(int& i_):i(i_){} void operator()() { for(unsigned j=0;j<1000000;++j) { do_something(i); } } }; void do_something_in_current_thread() {} void f() { int some_local_state; func my_func(some_local_state); std::thread t(my_func); thread_guard g(t); do_something_in_current_thread(); } The book describes also a scoped_thread that do something similar #include <thread> #include <utility> class scoped_thread { std::thread t; public: explicit scoped_thread(std::thread t_): t(std::move(t_)) { if(!t.joinable()) throw std::logic_error("No thread"); } ~scoped_thread() { t.join(); } scoped_thread(scoped_thread const&)=delete; scoped_thread& operator=(scoped_thread const&)=delete; }; void f() { int some_local_state; scoped_thread t(std::thread(func(some_local_state))); do_something_in_current_thread(); } Would something like these simple classes respond to your needs? Best, Vicente

2012/10/20 Vicente J. Botet Escriba <vicente.botet@wanadoo.fr> What do you think of adding a thread_guard class that could interrupt and
join on the destructor?
By "adding", do you mean adding it to Boost? I suggested this interruption because I believed (apparently incorrectly) that class thread represents a tool ready to be used by "end-user" programmers. After this discussion I realize that thread is a low-level primitive that you use for building high-level concurrency constructs, but would rather not use it directly. Following this view, anyone can build their own abstraction atop boost::thread. I do not think the above thread_guard should be added into Boost. If I need it I can write it myself (and I would probably write it differently; e.g. using variadic forwarding constructor). Someone else may introduce another wrapper that detaches in destructor. Would something like these simple classes respond to your needs? Actually, it was no particular need that raised my proposal; I just thought (now I do not anymore) it was a generally good idea for a general purpose tool. Regards, &rzej

Le 20/10/12 20:41, Andrzej Krzemienski a écrit : them as examples of use in the documentation could help the user.
I see, I'll then close the ticket you created with some extracts from this thread https://svn.boost.org/trac/boost/ticket/7496 Thanks for starting this discussion. Best, Vicente

On Oct 21, 2012, at 2:28 AM, "Vicente J. Botet Escriba" <vicente.botet@wanadoo.fr> wrote:
I don't agree with the it's-easy-to-write-so-don't-add-it-to-Boost philosophy. By adding such a class to Boost, you highlight the idea to those that otherwise hadn't thought of it, and you standardize the I/F and semantics. ___ Rob

2012/10/23 Rob Stewart <robertstewart@comcast.net>
I understand your reasoning, but the situation with boost::thread (and std::thread) is very particular: 'end-user' programmers will not use naked threads; they will also not use "thread guards" (as described above) because they are still too low-level, aren't they? 'end-user' programmers (IMHO) would prefer higher abstractions, e.g "do another task in parallel" (use async() for that), or perform some algorithm on a Range in parallel (they will use some 'concurrent algorithms library' for that). The only use of naked threads (again, IMHO) is to build these higher level abstractions, but in this case a guard that interrupts and joins is likely not the thing you would want to use: first, you are already inventing something on your own and have to write a custom wrapper, second, there are too many possible semantics of such wrapper: "interrupt and join", "interrupt and detach", "detach without interruption" -- should Boost library provide wrappers for all these possible needs? On the other hand, giving such a wrapper as an example in documentation already highlights the idea to some extent. Regards, &rzej

Am 23.10.2012 11:44, schrieb Andrzej Krzemienski:
Even Bjarne Stroustup doesn't pretend to know what 'end-users' do. Most likely, nobody does. Looking at the code base in our company (VC10 or VC11 only) I can say that none of my colleagues ever used something higher-level than boost::thread - mostly for historical reasons and an 'it ain't broke so leave it alone' attitude. If a tool like 'thread_guard' or the like came pre-packaged with Boost, they'd happily swallow it rather than rolling their own. Ciao Dani

Le 23/10/12 17:56, Daniela Engert a écrit :
Hi, I have created two tickets to track this possible additions: https://svn.boost.org/trac/boost/ticket/7540: Add a helper class that interrupts a thread and join it on destruction https://svn.boost.org/trac/boost/ticket/7541: Add a thread wrapper class that interrupts and join on destruction While the scoped_thread class defined in C++ Concurrency in Action is a strict scoped class that doesn't allows any change in the wrapped thread, I think that making the interface thread compatible is also a good option. This doesn't means that a strict scoped thread has no use. Best, Vicente

2012/10/23 Vicente J. Botet Escriba <vicente.botet@wanadoo.fr>
Hi Vicente, The description in the two tickets above do not mention any interruption. thread_guard and scoped_thread only join w/o interruption. Do you intend both to interrupt before join? If "yes", will the names of the two would not be confusing? Name "scoped_thread" is likely (IMHO) to be interpreted that we want to finish for the thread to end in a normal (non-interrupted) way. Perhaps the name should indicate that we want an interruption. If "no", is scoped_thread different from the functionality offered by async? (Given that future's destructor blocks.) Regards, &rzej

Le 25/10/12 11:13, Andrzej Krzemienski a écrit :
I've been working a little bit these classes and I have not find that interrupt-and-join on the destructor could be a a good compromise. As others, maybe you, have signaled, each developer could need a specific action on the destruction. As always been more generic means making the class parameterized with a policy. A possible implementation could be /** * Non-copyable RAII strict thread guard joiner which join the thread if joinable when destroyed. */ class strict_thread_joiner { thread& t; public: BOOST_THREAD_MOVABLE_ONLY( strict_thread_joiner) explicit strict_thread_joiner(thread& t_) : t(t_) { } ~strict_thread_joiner() { if (t.joinable()) { t.join(); } } }; /** * RAI @c thread wrapper adding a specific StrictThreadGuard allowing to master what can be done at destruction time. * * StrictThreadGuard: A NonCopyable class that takes a @c thread& as constructor parameter. * The default is a strict thread joiner guard. * * thread std::thread destructor terminates the program if the thread is not joinable. * Having a wrapper that can join the thread before destroying it seems a natural need. * * Example: * * boost::strict_scoped_thread<> t((boost::thread(F))); * */ template <class StrictThreadGuard = strict_thread_joiner> class strict_scoped_thread { thread t_; StrictThreadGuard m_; public: BOOST_THREAD_NO_COPYABLE( strict_scoped_thread ) /// non copyable /** * Constructor from the thread to own. * * @param t: the thread to own. * * Effects: move the thread to own @c t and pass the stored thread to an internal Destroyer. */ explicit strict_scoped_thread(BOOST_THREAD_RV_REF(thread) t) : t_(boost::move(t)), m_(t_) { } }; Now it is up to the user to change the default strict_thread_joiner policy. I've implemented also a non-strict thread wrapper that behaves a thread except that for the destruction. Maybe need another name. /** * RAI @c thread wrapper adding a specific destroyer allowing to master what can be done at destruction time. * * ThreadGuard: A MoveOnly class that takes a @c thread& as constructor parameter. * The default is a thread_joiner guard. * * thread std::thread destructor terminates the program if the thread is not joinable. * Having a wrapper that can join the thread before destroying it seems a natural need. * * Remark: @c scoped_thread is not a @c thread as @c thread is not designed to be derived from as a polymorphic type. * Anyway @c scoped_thread can be used in most of the contexts a @c thread could be used as it has the * same non-deprecated interface with the exception of the construction. * * Example: * * boost::scoped_thread<> t((boost::thread(F))); * t.interrupt(); * */ template <class ThreadGuard = thread_joiner> class scoped_thread; Does this respond to your question? Best, Vicente

2012/10/25 Vicente J. Botet Escriba <vicente.botet@wanadoo.fr>
It definitely does! Just one suggestion, wouldn't it be better if the policy was only required to be callable with the argument of type thread& ? I.g.: struct strict_thread_joiner { void operator()( thread& t ) const { if (t.joinable()) { t.join(); } } }; Regards, &rzej

On Oct 13, 2012, at 7:59 AM, Andrzej Krzemienski <akrzemi1@gmail.com> wrote:
Yes
To me, the interruption in boost::thread is like a good default behavior for the beginners.
I see it merely as Boost.Thread offers functionality not in std::thread.
The professionals would surely not use naked std::thread.
I disagree. If cancellation is required, then additional logic and synchronization is required, but that hardly precludes the use of std::thread directly. (Is that, perhaps what you were trying to say?)
I would propose just the opposite. If you are sure you want your thread handle to terminate the program in destructor, write your wrapper class yourself, and terminate there, and use the joining destructor for the default boost::thread interface (for beginners). I believe that for beginners, termination may not be of use to identify the source of the problem (I may be wrong though).
Termination quickly makes one aware of a problem. Setting a breakpoint on raising an exception can reveal the thread's destructor in the call stack. With your approach, the program can hang indefinitely, before the user recognizes the problem, and the user must rerun the program in the debugger long enough to, hopefully, hit the block, and then interrupt to find the program. Now which seems easier to debug?
My problem with terminating destructor is that there is no safe way to use it other than wrap it in another class that takes a different approach in the destructor.
Well, it does mean that you have to manage them carefully.
This makes std::thread (and now boost::thread) like a pair of operators new and delete, which require that you do not use them directly, but write some wrapper instead. Well, this might be an acceptable situation after all.
I understand your point, but the good news is that the user is forced to establish the EOL policy rather than getting something that is likely not appropriate in any case.
Such code doesn't look right at all. There is no synchronization.
That just means those authors must present more complicated examples from the start if they avoid async.
Interrupting and joining destructor would be good for novices.
You keep claiming that, but I don't think it is helpful behavior.
And it enables the scoped (RAII-like) reasoning about threads.
The only viable argument I see is less dangerous behavior during stack unwinding during exception propogation.
Any naive use of threads is likely wrong. ___ Rob
participants (9)
-
Andrzej Krzemienski
-
Christof Donat
-
Daniela Engert
-
Dave Abrahams
-
Gottlob Frege
-
Olaf van der Spek
-
Rob Stewart
-
Vicente Botet
-
Vicente J. Botet Escriba