Giovanni Piero Deretta
On Wed, Mar 18, 2015 at 4:08 AM, Jeremy Maitin-Shepard
wrote:
[snip]
I complely removed the current then/next implementation in favor of a generic then that works with any waitable. This makes it explicit that you can compose future types (and in fact any waitable) as long as they provide a get_event.
I removed 'next' completely to simplify the implementation. The optimization opportunity to save an allocation on the except case is probably not worth the complexity.
I think it is good to have, even if it is implemented using `then', since it has the advantage that the function passed does not have to deal in futures. It seems like implementing it more efficiently as you did could make sense in a more polished version, though, since futures are a basic primitive and it is best to minimize the overhead as much as possible.
3. Since invoking future::then and future::next release ownership, it is worth considering making them only work on rvalues. Whether the extra compile-time error checking this buys is worth the added verbosity of having to add std::move in some cases is not clear, though.
the new free function then takes its waitable by value (so you have to move it). I'll leave the current signature of then as is for now for compatibility with the standard proposal and boost::future.
The required RMW are definitely non neglegible. I tried to keep them at a minimum but they still have a cost. My original future implementation actually had thread safety switch but it would be terribly error prone.
Certainly it would be more error-prone than the thread-safe future, since there would be no compile-time way to verify that the user isn't incorrectly accessing it from multiple threads without external locking, but I'm not sure that is a reason not to provide it. It seems it would be possible to upgrade a thread-unsafe future to a thread-safe future, provided that there is no race between the upgrade and marking the future ready. I think the implementation would be pretty simple, in fact.
Instead I'm studying a way for future to start as simply a deferred synchronous computation, but that can become asynchronous on request of another thread (via work stealing or work requesting for example).
This would be pretty useful functionality to add support for; I guess it would complicate your approach a bit, though. One other issue that came to mind: your current approach of using a singly-linked list of waiters seems to pose a problem for a timed wait or other "recovable" waits, since you can only remove a waiter if it is at the front of the list. With only unique-ownership futures this might be okay, if you prohibit waiting on a future from multiple threads at once, since then the waiter for be guaranteed to remain at the front of the list. However, with shared-ownership futures the problem would be unavoidable. Therefore it might make sense to switch to a doubly-linked list; I guess this will make the lock-free algorithms trickier. [snip]
Benchmarks (and unit tests) would also be particularly helpful.
I have yet to find a good, fair, small and realistic benchmark for futures. Ideas are welcome.
I was thinking microbenchmarks, that would at least show the cost of individual operations and allow comparing different design choices/implementations. For instance, cost of getting value from ready future, cost of waiting on ready future, cost of calling then on ready or not-yet-ready future, etc.