
We've been discussing the need to implement a mechanism to wait for multiple futures in a separate thread: www.nabble.com/Review-Request%3A-future-library-%28Gaskill-version%29-to16600402.html I've come up with some interfaces which I find intuitive and useful. I've never implemented thread waiting, so please let me know if you think something like this is viable. ------------------------------ Proposal ------------------------------ A future can be constructed and thereafter wait on four different "future conditions" * a promise * a future_switch * a future_barrier * another future Each condition has a specified return type and the futures you create from it must match this type. They all have the same possible states - isn't ready, has value or an expression. I have left these and alot of other details out of the interface below for conciseness. ------------------------------ Interface drafts ------------------------------ /// Future condition which is ready when any of the added conditions is ready. Has an arbitrary return-type template<class ReturnType> class future_switch { public: /// Direct return template<class FutureConditionWithMatchingReturnType> void add_case(FutureConditionWithMatchingReturnType f); /// Possibility to perform custom functor, bind state, perform some logic etc before fulfilling it's condition template<class FutureCondition, class FutureTypeToReturnTypeFunctor> void add_case(FutureCondition f, FutureTypeToReturnTypeFunctor case); /// Only valid in is_ready state. Removes last fulfilled futurecondition and enables new futures to be /// constructed. Existing futures will have copied the value or exception already and will still work. void remove_ready_case(); bool empty() const; }; /// Future condition which is ready when all added futures are. /// This interface is less promising and has some flaws. Maybe you can get inspired by it though. template<class ReturnType> class future_barrier { public: future_barrier(const function<ReturnType()>& onReady); template<class FutureCondition> void add_condition(const future<FutureType>& f); }; ------------------------------ Example usage ------------------------------ template<class T> future_switch<T> operator||(const future<T>& lhs, const future<T>& rhs) { future_switch<T> sw; sw.add_case(lhs); sw.add_case(rhs); return sw; } template<class T> T and_impl(const future<T>& lhs, const future<T>& rhs) { return lhs.get() && rhs.get(); } template<class T> future_barrier<T> operator&&(const future<T>& lhs, const future<T>& rhs) { // Rather large runtime insecurity, we might forget to add lhs and rhs as conditions to the barrier future_barrier b(bind(&and_impl, lhs, rhs)); b.add_condition(lhs); b.add_condition(rhs); return b; } // This is an experimental use case for using switch as a dynamic set of future conditions // It handles three future responses and calls the three functions on the next lines bool handle_response_a(ResponseA r); bool handle_response_b(ResponseB r); bool handle_response_c(ResponseC r); void handle_pending_requests(const future<ResponseA>& a, const future<ResponseB >& b, const future<ResponseC >& c) { future_switch<void> sw; sw.add_case(a, &handle_repsonse_a); sw.add_case(b, &handle_repsonse_b); sw.add_case(c, &handle_repsonse_c); bool all_ok = true; while (!sw.empty()) { // Will automatically remove ok:ed futures from sw future<bool> next_response_was_ok(sw); bool all_ok = all_ok && next_response_was_ok.get(); sw.remove_ready_case(); } return all_ok; } ------------------------------ Some comments ------------------------------ There is a lot to improve here. I'd appreciate it if we could focus on determining whether this is a viable solution and the way we want to go before diving into details. These abstractions has the following property: A Users can compose conditions and poll them without the need of waiting. B We do not need to spawn extra threads for waiting - we only wait on the "outmost" future C Threads needn't awake and poll readiness - assuming the and/or logic is handled by waitingcode. D No "user logic" from the future-blocking threads propagate to the promise-fulfilling thread. E It's possible to implement efficient laziness by keeping readiness/waiting logic internal in the future library "wait for all" can be implemented without adding future_barrier, by simply calling wait() on all futures. We lose property A and E for "wait for all" if we do it this way. The blocking thread will awake unnecessarily many times. Property C means promise-fullfilling threads need to do some basic and/or logic. I think this can be implemented in a safe and efficient manner, O(log(N)) for barrier/all and O(1) for switch/any. I also think it is much preferable to waking and switching to future-blocking threads which re-check readiness conditions. Note property D. Whatever mechanism we choose I think it's desirable to be able to easily "lift" existing functions to futures; R foo(A1 a1, A2 a2, A3 a3) should be easily rewritable as; future<R> foo(future<A1> a1, A2 a2, future<A3> a3) // Some parameters need not be lifted Johan -- View this message in context: http://www.nabble.com/-future--Early-draft-of-wait-for-multiple-futures-inte... Sent from the Boost - Dev mailing list archive at Nabble.com.