
On Fri, Sep 4, 2015 at 11:56 PM, Vicente J. Botet Escriba <vicente.botet@wanadoo.fr> 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.
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? -- gpd