From: Braddock Gaskill (braddock_at_[hidden])
Date: 2008-04-13 18:33:09


On Thu, 10 Apr 2008 21:53:33 +0200, vicente.botet wrote:
> How do your library positions with respect to the standard proposals?
> N2561 Anthony Williams: An Asynchronous Future Value

I've now taken a good look at the recent C++0X N2561 "An Asynchronous Future
Value" proposal.

Both my library and N2561 are heavily based on Peter Dimov's earlier API... I
dare say a few method name changes would make N2561 a proper subset of my
future and promise classes and future_wrapper helper.

I'm disregarding the future C++ move semantics, of course.
We need a future<C++0x>::wait() for that... :)

unique_future<T> is an interesting concept. Still wrapping my head around that.

My base classes offer two features which N2561 does not (and which I would
really hope to see in any C++0X standard).

--
1) First - future<T>::add_callback(f).  
add_callback is a hook called when the future is fulfilled.  End
users probably shouldn't have to touch it, but framework authors (who
write schedulers, asio, co-routines, etc) will DEFINITELY need it.  
add_callback() enables the following:
-Future Operators ((f1 && f2) || (f3 && f2) etc) - With add_callback, custom
future_operators can be written by users.
-Guarded Schedulers (See my library doc "Future Concept Tutorial" section at
http://braddock.com/~braddock/future) - Guards are a fundamental concept in the
academic future languages I've studied (see some of Ian Foster's papers on
Strand and PCN, for example).  I have found you can do some amazing things with
a guarded task scheduler (I've implemented one outside the future lib).
-Basic "future fulfillment" event notification (ie, just use add_callback as an
event handler).  Gives you essentially a signals/slot capability. Not a fan.
An alternative to add_callback() would be to provide just the operators
themselves - Guards can reasonably be derived from the combination ops.
Any of these mechanisms solve the busy wait in the N2561 motivating example.
--
2) Second - Lazy Futures.  
This is something I picked up from the Oz language future implementation, and
have found very useful.  A Lazy Future essentially flags itself when it is
"needed" - ie, when someone is blocked on a f.wait(), and f.get(), or has
explicitly called "f.set_is_needed()".  This allows, for example, a task to
only process _IF_ the result is actually needed.  
Again, see my library doc Tutorial where I show how to easily create a "Lazy
Job Queue".  Permits nice Memoization patterns as well.
Lazy Futures are also needed for "Lazy Future Streams".  A Stream is the
primary means of inter-task communication in future-related academic languages.
It permits producer/consumer patterns and one-to-many channels.  A Lazy stream
allows the producer task to produce new items only as fast as his fastest
consumer needs them (See Ian T. Foster's work again - or my
test_future.hpp unit test code).
Note I provide an easy-to-use future stream library with std iterator interface.
I also provide a future::cancel(), which has been discussed in the other
posts and which I'm not terribly attached to.
I really like seeing the N2561 C++ proposal uses a split
future/promise. Broken_promise exceptions have saved me many times in
real-world applications. And I like that you aren't using the implicit
type conversion (which has bitten me many times in real-world
applications! I only use my future::get() method now).
(I still provide implicit conversion, but would take it out very fast if others
are no longer attached to it).
Braddock Gaskill