[future.then] Should deferred continuation respect status of original future?
Hi, consider this code that wraps some library returning a future: // some async library boost::future<intermediate> DoLongAsyncTask() { return myPromise.get_future(); } // intermediate translation layer boost::future<result> GetResult() { return DoLongAsyncTask().then([] (boost::future<intermediate> f) { return TranslateToResult(f.get()); }); } // client event loop void RunInClientContext() { auto myFuture = GetResult(); while (stuffToDo()) { doStuff(); if (SafeToCallFutureGetWithoutBlocking(myFuture)) ProcessResult(myFuture.get()); } } What should the implementation of 'SafeToCallFutureGetWithoutBlocking' be? Normally, one would write something like: myFuture.is_ready() or myFuture.wait_for(chrono::seconds(0)) != future_status::timeout But this doesn't work in case the implementation of intermediate translation layer calls .then with launch::deferred! In Boost 1.63 the future returned from .then(launch::deferred, ...) is always deferred, so wait_for(...) always returns deferred no matter what (and is_ready() always returns false). In fact, there is no way to implement 'SafeToCallFutureGetWithoutBlocking' if the translation is to happen in the context of the client (launch::deferred). Wouldn't it be more natural to respect the status of the original future in this case? The documentation is already very clear that the continuation is executed only after the original future is ready. So in case a client asks whether the continuation future is ready, and the original future is not ready yet, the call should forward to the original future instead. What I propose is this: promise<int> p; future<int> f1 = p.get_future(); future<int> f2 = f1.then(launch::deferred, [] (auto f) { return f.get(); }); assert(f2.wait_for(0) == future_status::timeout); p.set_value(42); assert(f2.wait_for(0) == future_status::deferred); Is there any reason for the continuation future to not behave this way? Regards Tomas
Le 31/05/2017 à 09:38, Tomas Sturm via Boost a écrit :
Hi,
consider this code that wraps some library returning a future:
// some async library boost::future<intermediate> DoLongAsyncTask() { return myPromise.get_future(); }
// intermediate translation layer boost::future<result> GetResult() { return DoLongAsyncTask().then([] (boost::future<intermediate> f) { return TranslateToResult(f.get()); }); }
// client event loop void RunInClientContext() { auto myFuture = GetResult(); while (stuffToDo()) { doStuff(); if (SafeToCallFutureGetWithoutBlocking(myFuture)) ProcessResult(myFuture.get()); } }
What should the implementation of 'SafeToCallFutureGetWithoutBlocking' be? Normally, one would write something like:
myFuture.is_ready() or myFuture.wait_for(chrono::seconds(0)) != future_status::timeout
But this doesn't work in case the implementation of intermediate translation layer calls .then with launch::deferred! In Boost 1.63 the future returned from .then(launch::deferred, ...) is always deferred, so wait_for(...) always returns deferred no matter what (and is_ready() always returns false). In fact, there is no way to implement 'SafeToCallFutureGetWithoutBlocking' if the translation is to happen in the context of the client (launch::deferred).
IRC is_ready and what_for 0 can not work for a deferred future.
Wouldn't it be more natural to respect the status of the original future in this case? The documentation is already very clear that the continuation is executed only after the original future is ready. So in case a client asks whether the continuation future is ready, and the original future is not ready yet, the call should forward to the original future instead. Humm, the future is ready or not. is_ready just tell you that.
What I propose is this:
promise<int> p; future<int> f1 = p.get_future(); future<int> f2 = f1.then(launch::deferred, [] (auto f) { return f.get(); }); assert(f2.wait_for(0) == future_status::timeout); If I remember correctly this should fail now. p.set_value(42); assert(f2.wait_for(0) == future_status::deferred); Note that f2 contains yet a deferred function the continuation. This should be the case independently of setting the promise now, isn't it?
So what you are asking is that wait_for(0) returns ::timeout " if the shared state contains a deferred function" and this function will not block, isn't it? I don't know if I have this information, and how i could obtain it yet.
Is there any reason for the continuation future to not behave this way?
I believe I understand your need: you don't want to block on your loop and you want the continuation to be executed in the loop. Using deferred futures resulting from a continuation is not the solution This use case has not been considered and I think it merits some time to consider it. Best, Vicente P.S. the doc says " - |future_status::deferred| if the shared state contains a deferred function. (Not implemented yet)" This is not true anymore. I will fix it. It says also Effects: If |*this| is associated with a shared state, waits until the result is ready, or the time specified by |wait_duration| has elapsed. If the result is not ready on entry, and the result has a /wait callback/ set, that callback is invoked prior to waiting. And it should have in addition " None if the shared state contains a deferred function" As it is the case for the C++ standard.
participants (2)
-
Tomas Sturm
-
Vicente J. Botet Escriba