Boost logo

Boost :

Subject: Re: [boost] [thread] Do we can a non backward compatible version with non-blocking futures?
From: Lee Clagett (forum_at_[hidden])
Date: 2015-05-30 10:55:55


On Thu, May 28, 2015 at 1:53 AM, Vicente J. Botet Escriba <
vicente.botet_at_[hidden]> wrote:

> Le 27/05/15 18:55, Lee Clagett a écrit :
>
> On Wed, May 27, 2015 at 1:57 AM, Vicente J. Botet Escriba <
>> vicente.botet_at_[hidden]> wrote:
>>
>> Le 26/05/15 23:43, Lee Clagett a écrit :
>>>
>>> On Sun, May 24, 2015 at 7:35 AM, Vicente J. Botet Escriba <
>>>
>>>> vicente.botet_at_[hidden]> wrote:
>>>>
>>>> Le 23/05/15 19:05, Lee Clagett a écrit :
>>>>
>>>>> On Thu, May 21, 2015 at 5:13 PM, Vicente J. Botet Escriba <
>>>>>
>>>>>> vicente.botet_at_[hidden]> wrote:
>>>>>>
>>>>>> Le 21/05/15 17:11, Lee Clagett a écrit :
>>>>>>
>>>>>> A non-blocking future::then() is convenient for cases like the one
>>>>>>>
>>>>>>> Vicente
>>>>>>>> described, but can the executor framework simulate the same
>>>>>>>> behavior?
>>>>>>>>
>>>>>>>> Please , could you elaborate?
>>>>>>>>
>>>>>>>> boost::async and boost::future::then seem error prone due to
>>>>>>> ownership
>>>>>>>
>>>>>> of
>>>>>> the execution handle.
>>>>>>
>>>>>> Are talking here of blocking or non-blocking futures?
>>>>>>
>>>>> I was referring to non-blocking futures.
>>>>>
>>>>
>>>> If the destructor of the boost::future automatically
>>>>
>>>>> releases ownership, the execution handle cannot be managed.
>>>>>>
>>>>>> Why?
>>>>>>
>>>>> It was incorrect of me to say that the execution handle cannot be
>>>>>
>>>> managed
>>>> (see below). I thought the separate executor argument to boost::async
>>>> could
>>>> make it more obvious that the executor object would control the lifetime
>>>> of
>>>> the execution thread, and that it could prevent possible data races. I
>>>> doubt it will make data races to stack references easier to identify,
>>>> but
>>>> it should help prevent threads after main, since the executor object
>>>> could
>>>> force all execution threads to stop on its destruction instead. It
>>>> wasn't
>>>> obvious previously, but I was wondering whether the detach on future
>>>> destruction should only be enabled when boost::async takes an executor
>>>> overload.
>>>>
>>>>
>>>> It should
>>>>
>>>>> prevent threads after main, which never seem like a good idea.
>>>>>>
>>>>>> Sorry, I don't understand, it should prevent what?
>>>>>>
>>>>> Static destruction could be complicated if there were multiple
>>>>> execution
>>>>>
>>>> threads after main. That was another issue brought up about not blocking
>>>> on
>>>> destruction for the std async/future versions.
>>>>
>>>> So, you are saying that async() should return a blocking future if
>>> there
>>> is no executor parameters, and a non-blocking when the executor is given,
>>> isn't it?
>>>
>>>
>>> Yes, that is what I am saying. My thinking was that the behavior for the
>> corresponding std functions were chosen as the least worst alternative,
>> and
>> that boost should follow that behavior. When an executor is provided as an
>> argument, the situation is slightly different, and the problems that led
>> to
>> the blocking decision _might_ go away. The behavior differences between
>> overloads might provide more confusion though.
>>
>>
>> From your and Gor comments I should preserve the async blocking future
> behavior when no executor is given, deprecated the interface taking an
> executor and let the the function taking the executor return a future
> non-blocking at destruction. I would need a new name for this function,
> what do you prefer, submit/spawn/post?
>

post or dispatch seems appropriate.

>From Gor comments, then() must return a non-blocking future, that will have
> all the troubles you are mentioning, so there will be not coherency.
>
> I believe that misunderstood the blocking future feature from the
> begining. Currently only the destructor of the last future pointing to the
> shared state block. Before this it was the destructor of the shared state
> that blocked. The RAII you suggested will block on the destructor of all
> the futures (which is much more simple). Is this what people expect from a
> blocking future.
>

Assuming boost::async behavior doesn't change - I don't think of the
blocking_future class as a future. I think of it as a utility to guarantee
work completion in a specific scoping (just like future did previously with
boost::async). The use case would be preventing reference(s) held by a
callback from going bad, or guaranteeing that work completed in a specific
scope (even with exceptions). So I wouldn't suggest a shared version of
this. If shared_future was needed, and the callback contained references,
you'd likely group a blocking future with the destruction of the object
being referenced. So, I see two reasons to block: (1) the computed result
of the future is needed, or (2) preventing undefined/racy behavior. If
there are multiple points that need to be blocked for (2), you need to
block/wait when any of the points are reached, just like with (1). So
blocking on last shared_future really only prevents a runaway thread after
main (which is only needed in the non-executor design).

Ideally code wouldn't even need to be concerned about (2), because the
callback has pure semantics. Things don't always work out that way
(unfortunately).

> Should the shared futures coming from a blocking future block on
> destruction as well?

Are you asking about changing the behavior to make the shared future
blocking instead of the shared state? That way the first shared future to
destruct must wait for work completion? I don't see a reason to change that
behavior for boost::async, and I didn't expect blocking_future to be
convertible to a shared_future of any kind (I didn't see it as a future).

Lee


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