[boost] [thread] terminating destructor

1131 views
Skip to first unread message

Andrzej Krzemienski

unread,
Oct 10, 2012, 4:23:10 AM10/10/12
to bo...@lists.boost.org
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

_______________________________________________
Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost

Vicente J. Botet Escriba

unread,
Oct 10, 2012, 7:03:23 AM10/10/12
to bo...@lists.boost.org
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?
>
>
Hi,

yes this is a possible alternative to the standard behavior. But what to
do after interrupting, joining? What others think? Anthony?

Best,
Vicente

Andrzej Krzemienski

unread,
Oct 10, 2012, 8:56:19 AM10/10/12
to bo...@lists.boost.org
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?
>>
>>
>> Hi,
>
> 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. 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

Vicente Botet

unread,
Oct 10, 2012, 1:17:06 PM10/10/12
to bo...@lists.boost.org
Andrzej Krzemienski wrote
> 2012/10/10 Vicente J. Botet Escriba &lt;

> vicente.botet@

> &gt;

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.

Andrzej Krzemienski

unread,
Oct 11, 2012, 9:35:27 AM10/11/12
to bo...@lists.boost.org
>
> Please, could you create a Track ticket?
>

Here it is:
https://svn.boost.org/trac/boost/ticket/7496

Regards,
&rzej

Vicente J. Botet Escriba

unread,
Oct 12, 2012, 4:24:19 PM10/12/12
to bo...@lists.boost.org
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

Rob Stewart

unread,
Oct 13, 2012, 5:35:19 AM10/13/12
to bo...@lists.boost.org
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 :
>> 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

Andrzej Krzemienski

unread,
Oct 13, 2012, 7:59:20 AM10/13/12
to bo...@lists.boost.org
2012/10/13 Rob Stewart <robert...@comcast.net>
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. 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

Vicente J. Botet Escriba

unread,
Oct 13, 2012, 8:40:26 AM10/13/12
to bo...@lists.boost.org
Le 13/10/12 13:59, Andrzej Krzemienski a écrit :
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

Andrzej Krzemienski

unread,
Oct 13, 2012, 10:08:46 AM10/13/12
to bo...@lists.boost.org
> 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?
>

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

Vicente J. Botet Escriba

unread,
Oct 13, 2012, 11:43:14 AM10/13/12
to bo...@lists.boost.org
Le 13/10/12 16:08, Andrzej Krzemienski a écrit :
>> 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?
> 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 you are referring to the case the std::future is created by async

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?
> 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.
I don't know if the current Boost.Thread async implementation is not
conforming to the preceding point in the standard. That is, the shared
state waiting functions don't takes in account whether the future has
been created using async or not, they just block until the shared state
is ready. I find troubling and I don't understand completely why the
committee has changed the semantic of the future operations on the
definition of a free function like async. I need to check the reason the
the committee had to do it this way. Maybe you could help me.

> 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:
I'm surely missing something in the standard. Could you explain why the
future's destructor shall block?
> {
> 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.


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

Dave Abrahams

unread,
Oct 13, 2012, 4:39:17 PM10/13/12
to bo...@lists.boost.org

on Sat Oct 13 2012, Andrzej Krzemienski <akrzemi1-AT-gmail.com> wrote:

> 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.

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

Andrzej Krzemienski

unread,
Oct 13, 2012, 5:17:23 PM10/13/12
to bo...@lists.boost.org
2012/10/13 Vicente J. Botet Escriba <vicent...@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

Rob Stewart

unread,
Oct 13, 2012, 6:48:26 PM10/13/12
to bo...@lists.boost.org
On Oct 13, 2012, at 7:59 AM, Andrzej Krzemienski <akrz...@gmail.com> wrote:

> 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

Vicente J. Botet Escriba

unread,
Oct 13, 2012, 7:12:40 PM10/13/12
to bo...@lists.boost.org
Le 13/10/12 23:17, Andrzej Krzemienski a écrit :
Why the future destructor should be the last function that release the
shared state?

Best,
Vicente

Andrzej Krzemienski

unread,
Oct 14, 2012, 8:18:00 AM10/14/12
to bo...@lists.boost.org
2012/10/14 Vicente J. Botet Escriba <vicent...@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

Vicente J. Botet Escriba

unread,
Oct 20, 2012, 5:56:27 AM10/20/12
to bo...@lists.boost.org
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?

Andrzej Krzemienski

unread,
Oct 20, 2012, 2:41:03 PM10/20/12
to bo...@lists.boost.org
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). 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

Vicente J. Botet Escriba

unread,
Oct 21, 2012, 2:28:00 AM10/21/12
to bo...@lists.boost.org
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.
> 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.
>
>
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

Rob Stewart

unread,
Oct 23, 2012, 5:23:57 AM10/23/12
to bo...@lists.boost.org
On Oct 21, 2012, at 2:28 AM, "Vicente J. Botet Escriba" <vicent...@wanadoo.fr> wrote:

> 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

Andrzej Krzemienski

unread,
Oct 23, 2012, 5:44:49 AM10/23/12
to bo...@lists.boost.org
2012/10/23 Rob Stewart <robert...@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

Daniela Engert

unread,
Oct 23, 2012, 11:56:39 AM10/23/12
to bo...@lists.boost.org
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

Vicente J. Botet Escriba

unread,
Oct 23, 2012, 12:16:15 PM10/23/12
to bo...@lists.boost.org
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

Andrzej Krzemienski

unread,
Oct 24, 2012, 7:35:04 AM10/24/12
to bo...@lists.boost.org
2012/10/14 Andrzej Krzemienski <akrz...@gmail.com>

>
>
> 2012/10/14 Vicente J. Botet Escriba <vicent...@wanadoo.fr>
>
> Le 13/10/12 23:17, Andrzej Krzemienski a écrit :
>>
>> 2012/10/13 Vicente J. Botet Escriba <vicent...@wanadoo.fr>
>>>
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?

Gottlob Frege

unread,
Oct 24, 2012, 1:47:40 PM10/24/12
to bo...@lists.boost.org
On Wed, Oct 24, 2012 at 7:35 AM, Andrzej Krzemienski <akrz...@gmail.com> wrote:
> 2012/10/14 Andrzej Krzemienski <akrz...@gmail.com>
>
>>>>
>>>> I guess you are referring to the case the std::future is created by async
>>>>>
>
> 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?
>


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

Vicente J. Botet Escriba

unread,
Oct 24, 2012, 3:12:13 PM10/24/12
to bo...@lists.boost.org
Le 24/10/12 13:35, Andrzej Krzemienski a écrit :
You are right, but in this case there is no problem to block as the
future is already ready.

Best,
Vicente

Vicente J. Botet Escriba

unread,
Oct 24, 2012, 3:47:30 PM10/24/12
to bo...@lists.boost.org
Le 24/10/12 19:47, Gottlob Frege a écrit :
> On Wed, Oct 24, 2012 at 7:35 AM, Andrzej Krzemienski <akrz...@gmail.com> wrote:
>> 2012/10/14 Andrzej Krzemienski <akrz...@gmail.com>
>>
>>>>> I guess you are referring to the case the std::future is created by async
>> 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?
>>
>
> 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.
I have started to read the minutes of the C++ standard committee but I
don't reach to find out reasons. I would appreciate if someone could
point me to the details of the problem.
> 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.
async was a compromise as there wasn't enough time to complete a
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.
> 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.
I agree end users are waiting for more high level interfaces. But I
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.
> 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?
>
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

Andrzej Krzemienski

unread,
Oct 25, 2012, 3:56:40 AM10/25/12
to bo...@lists.boost.org
2012/10/24 Vicente J. Botet Escriba <vicent...@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

Olaf van der Spek

unread,
Oct 25, 2012, 4:03:40 AM10/25/12
to bo...@lists.boost.org
On Thu, Oct 25, 2012 at 9:56 AM, Andrzej Krzemienski <akrz...@gmail.com> wrote:
> 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?

Olaf

Andrzej Krzemienski

unread,
Oct 25, 2012, 4:11:20 AM10/25/12
to bo...@lists.boost.org
2012/10/25 Olaf van der Spek <m...@vdspek.org>

> On Thu, Oct 25, 2012 at 9:56 AM, Andrzej Krzemienski <akrz...@gmail.com>
> wrote:
> > 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?


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

Olaf van der Spek

unread,
Oct 25, 2012, 4:22:17 AM10/25/12
to bo...@lists.boost.org
On Thu, Oct 25, 2012 at 10:11 AM, Andrzej Krzemienski
<akrz...@gmail.com> wrote:
> 2012/10/25 Olaf van der Spek <m...@vdspek.org>
>
>> On Thu, Oct 25, 2012 at 9:56 AM, Andrzej Krzemienski <akrz...@gmail.com>
>> wrote:
>> > 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?
>
>
> 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.

But why? What's the rationale for this requirement?
Is it the same rationale as the thread destructor of a non-detached
thread terminating (for safety)?


--
Olaf

Christof Donat

unread,
Oct 25, 2012, 4:33:24 AM10/25/12
to bo...@lists.boost.org
Hi,

>> 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