Boost logo

Boost :

Subject: Re: [boost] Non-allocating future-promise
From: Niall Douglas (s_sourceforge_at_[hidden])
Date: 2014-10-18 21:18:30


On 17 Oct 2014 at 19:42, Hartmut Kaiser wrote:

> > Yep. Using my new spinlock library, the one I built the safe erasing
> > concurrent_unordered_map with.
>
> What suspension mechanism does this rely on? Does it suspend the kernel
> thread?

Nothing currently, but it will use my failed proposed C11/pthreads
permit object. Myself and Hans Boehm spent an awful lot of time back
in the day getting that permit object to behave well under all sorts
of weird load conditions. It's also portable, and is safe to destroy
concurrent to signalling. It has a few other useful features too
which makes it ideal as the standard kernel wait abstraction.

I'll be keeping around a thread safe process wide cache of unused
permit objects, so grabbing one lazily will usually be fast.

> > I can absolutely guarantee that this
> > sequence always reduces to nothing:
> ...
>
> While all of this demonstrates impressive optimization technologies, I don't
> think it's a relevant use case. In 99.9% of all use cases this is not
> applicable. So why worry?

This is a very valuable statement. It showed me that I was coming at
non-allocating future promise all wrong design wise - I, like the
present Concurrency TS, was thinking in terms of composing futures
into continuations. It has suddenly dawned on me that this is all
wrong - I should in fact be thinking in terms of *flow* of
continuations, and that future-promise is simply where a river forks,
or a vertex appears in a graph.

That utterly changes the design. We still get non-allocating
future-promise which template aliases a std::future compatible
variant. But the design inverts inside out. We now hang
future-promise onto thenables, not the other way round.

I think I now understand where Louis Dionne was coming from in a
private mail when he was trying to explain how Hana might do this
stuff. I'm getting old and slow in the mind, but I ain't quite dead
yet.

I'll see if I can come up with a new prototype next week in time for
the Urbana meeting.

> > > FWIW, N4123 adds .then to the future<> type, not the promise<>.
> >
> > Sure. That's because future.then() executes its continuations at the
> > point of someone doing a future.get().
>
> No, future.then() is called whenever somebody else sets the value (using
> promise.set_value()) - well except when using launch::deferred, but there
> all bets are off anyways.

Except launch::async | launch::deferred is the default launch policy
for futures created from std::promise or a std::packaged_task, and so
therefore it does indeed execute continuations at future.get(). See
http://www.open-std.org/jtc1/sc22/WG21/docs/papers/2014/n4107.html, I
see the pre-Urbana changes haven't changed this.

There is value in both points of launch. I think personally it should
be selectable. Anyway, the new design I have in mind makes .then()
merely a backwards compatibility shim, the new design starts with
everything being a continuation and promise-future is merely a
potential transformation rather like a Haskell monadic bind. If you
want a continuation to occur promise side and another future side,
simply insert the promise-future construct in between the two in the
overall sequence. Sorry, I'll hopefully show this in code later next
week, I am probably being a bit excited and speaking nonsense.

> > As promise.then() attaches continuations to the promise instead, it
> > would execute its continuations at the point of someone doing a
> > promise.set_value() or promise.set_exception().
> >
> > (Some might wonder why not subclass basic_promise<> and override the
> > set_value()/set_exception() with custom behaviour? Unlike promise,
> > basic_promise is designed to be customised and subclassed in a family
> > of future-ish promise-ish types. It's a very valid point, but I worry
> > about generic extension, see below).
>
> I'd be absolutely against attaching continuations through the promise. It's
> conceptually wrong.

Agreed. Promise suspends a continuations sequence, Future resumes a
sequence (optionally on another thread).

> What do you mean by 'static continuation'? What's the difference to a
> continuation as defined in N4123?

Continuations in N4123 are big fat heavy things with an implied
memory allocation and a probably std::function wrap. Continuations in
N4213 let you do:

future<int> &f;
if(i_feel_like_it)
  f.then([]);

Which is absolutely fine. However, sometimes you will always do:

promise<int> &p;
auto f=p.get_future().then([]); // always continue

There a memory allocation and std::function wrap is totally
unnecessary because the compiler has sufficient information to
construct f statically, except that the present design in N4123
requires it.

This is what I mean by static continuations - ones which always
happen unconditionally.

Niall

-- 
ned Productions Limited Consulting
http://www.nedproductions.biz/ 
http://ie.linkedin.com/in/nialldouglas/



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