Boost logo

Boost :

Subject: Re: [boost] Common future base class (was Re: Boost.Fiber mini-review September 4-13)
From: Vicente J. Botet Escriba (vicente.botet_at_[hidden])
Date: 2015-09-05 20:34:02


Le 05/09/15 02:04, Giovanni Piero Deretta a écrit :
> On Fri, Sep 4, 2015 at 11:56 PM, Vicente J. Botet Escriba
> <vicente.botet_at_[hidden]> wrote:
>> Hi Giovanni,
>>
>> boost::future has a lot of design issues. I will welcome and base class from
>> which boost::future can inherit as it is quite difficult to maintain it by
>> myself :(. I'm a little bit skeptical about the approach, but who knows.
> what are your concerns in particular?
>
> Basically what I would like is to decouple the signalling of a future
> shared_state, as done by e.g. the promise, from the action to be
> taken. The intuition is that most efficient implementations of
> synchronization primitives are based on a fast lock free user space
> signalling path plus a slow kernel path in case there are waiters. My
> idea is to decouple the slow path from the actual signalling
> implementation. The decoupling is done via an interface like this:
>
> struct signalable {
> virtual void signal() = 0;
> atomic<signalable *> next = 0;
> };
>
> An actual signalable implementation could invoke a continuation
> (unifying then and wait), signalling a condition variable, an event, a
> file descriptor, switching to another fiber etc.
>
> The signaler side has something like this:
>
> struct signaler {
> void signal();
>
> bool try_wait(signalable *);
> bool try_unwait(signalable*);
> };
>
> Note this is not virtual. In fact it could simply be a concept.
> Try_wait attempts to add the signalable to the wait list for the
> signaler. Failure means that the signaler was signaled in the
> meantime. try_unwait attempts to remove the signalable from the wait
> list. Failure also means that the signaler was signaled in the
> meantime.
>
> The signaler can be implemented lock_free except in the case in which
> try_unwait is called *and* there are multiple waiters. I believe this
> case can be handled with a spinlock just for mutual exclusion between
> unsignalers (try_waiters and signal can still be lock free), but I
> have yet to implement it
>
> With this interface a future can be implemented without any additional
> synchronization.
>
> For example the default implementation of 'then' would simply allocate
> a new shared state for the returned future which inherit from
> signalable and register it to the shared_state of the previous
> continuation. The callback is allocated inline in the shared_state.
> When signal is called, the callback is invoked.
>
> The default wait of course would create a wait object on the stack (or
> have a thread local one) which also implements the signalable
> interface (a portable implementation would be based on a condition
> variable + boolean flat + mutex). A timed wait need to use the
> try_unwait interface if the clock times out.
>
> Sorry for the rambling, hopefully the idea is a bit clearer.
>
> I have shared my implementation previously. This is the link again:
>
> https://github.com/gpderetta/libtask/blob/master/future.hpp
>
> Note: incomplete, buggy and definitely not production ready.
Giovanni, I'm all for adapting Boost.Thread to any global solution to
this problem.
What we need first is some one working on it, and showing that we are
able to efficiently implement when_all/when_any for some kind of future
concept.
Then we can try to see how to make boost::future/std::future ......
models of this concept.
If you have ideas on how this can work, a POC and time, I propose you
to work on it and propose such a library to Boost.
I don't believe it is fair to add a link to a ongoing work
implementation during the review of a library having a different scope.
>> Note that boost::future and boost::thread have some interactions (at thead
>> exit family functions) that need to be taken in account
>>
> I imagine that can be tricky. Is it much more complex than the thread
> holding a pointer to the shared state to be made ready on exit?
>
>
It is not a question of how complex it can be. It is a matter of public
interfaces. We don't have such public interface on the standard and I
believe that we need something like that. It is not a good thing that
futures and threads be so dependent.
The difficulty is defining the interface that makes them
(futures/thread) independent without falling to a specific implementation.

Thanks for re-rasing these interaction problems, but I believe that
these should be handled outside the scope of this review.

I suggest you to start a new thread.
Vicente


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