Boost logo

Boost :

Subject: Re: [boost] [thread] terminating destructor
From: Vicente J. Botet Escriba (vicente.botet_at_[hidden])
Date: 2012-10-13 08:40:26


Le 13/10/12 13:59, Andrzej Krzemienski a écrit :
> 2012/10/13 Rob Stewart <robertstewart_at_[hidden]>
>
>> On Oct 12, 2012, at 4:24 PM, "Vicente J. Botet Escriba" <
>> vicente.botet_at_[hidden]> wrote:
>>
>>> Le 10/10/12 14:56, Andrzej Krzemienski a écrit :
>>>> 2012/10/10 Vicente J. Botet Escriba <vicente.botet_at_[hidden]>
>>>>
>>>>> 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?
>>
> So are you saying the thread should detach on scope exit rather than
> terminate?
>
>
> >> 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.
>>
> 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.
I would really prefer if Boost.Thread behaves like std::threads as much
as possible so that moving from one to the other don't introduce many
surprises.
I don't know yet if this is doable but I would like that the
interruption mechanism is defined on top of the design of std::threads,
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.
> 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.
Well, I guess the Boost.thread documentation could help on this sense,
but I'm not a big writer. I will see however what I can do.
>
> Interrupting and joining destructor would be good for novices. And it
> enables the scoped (RAII-like) reasoning about threads.
I don't think this default behavior is the good one, even for beginners.
Of course all this is quite subjective.
> 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?
async and futures are good tools that are easier to use, yes. Anyway it
is not up to Boost.Thread documentation to state by what a user should
or should not start.
> std::future does
> join without interruption. What does boost::future do? I think it should
> interrupt and join (or perhaps detach as per N3451).
>
I don't understand this. Could you clarify?

Best,
Vicente


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk