> 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-tp4636872p4636901.html
Sent from the Boost - Dev mailing list archive at Nabble.com.
> Le 10/10/12 14:56, Andrzej Krzemienski a écrit :
>> 2012/10/10 Vicente J. Botet Escriba <vicent...@wanadoo.fr>
>>
>>> Le 10/10/12 10:23, Andrzej Krzemienski a écrit :
>>>
>>> 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?
>>>>
>>> yes this is a possible alternative to the standard behavior. But what to
>>> do after interrupting, joining? What others think? Anthony?
>>>
>> 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.
That seems a reasonable argument.
>> 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.
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 believe that with thread interruption in place the argument against
>> joining is mitigated, while the argument against detaching still holds.
>
> 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.
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 <robert...@comcast.net>
>
>> On Oct 12, 2012, at 4:24 PM, "Vicente J. Botet Escriba" <
>> vicent...@wanadoo.fr> wrote:
>>
>>> Le 10/10/12 14:56, Andrzej Krzemienski a écrit :
>>
>>>> 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.
>>
>> That seems like too much nannyism. C++ already has many ways to do similar
>> things, so why be so concerned with this one case?
>
> So are you saying the thread should detach on scope exit rather than
> terminate?
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.
> 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.
Such code doesn't look right at all. There is no synchronization.
> You could argue that
> one should start teaching multi-threading with async(), but somehow anyone
> chooses to start with thread.
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.
> 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?
Any naive use of threads is likely wrong.
___
Rob
> Le 20/10/12 20:41, Andrzej Krzemienski a écrit :
>> 2012/10/20 Vicente J. Botet Escriba <vicent...@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).
> I agree that these classes are easy to write by the user. Maybe adding them as examples of use in the documentation could help the user.
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
>> 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.
>
> The big question is: why?
>
> Can't the destructor detach from the shared state and let the state
> be
> cleaned up by the launched thread?
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 c...@okunah.de
Registergericht Augsburg
Geschäftsführer Augsburg HRB 21896
Christof Donat UStID: DE 248 815 055