1518. Waiting for deferred functions

Section: 32.10 [futures] Status: C++11 Submitter: Alberto Ganesh Barbati Opened: 2010-09-14 Last modified: 2016-01-28

Priority: Not Prioritized

View all other issues in [futures].

View all issues with C++11 status.

Discussion:

The current WP N3126 contains ambiguous statements about the behaviour of functions wait_for/wait_until in case the future refers to a deferred function. Moreover, I believe it describes a disputable intent, different from the one contained in the original async proposals, that may have been introduced inadvertently during the "async cleanup" that occurred recently. Consider the following case:

int f();  
future<int> x = async(launch::deferred, f);
future_status s = x.wait_for(chrono::milliseconds(100));

This example raises two questions:

  1. is f invoked?
  2. what is the value of s?

According to the current WP, the answer to question 1 is yes, because 30.6.9/3 says "The first call to a function waiting for the associated asynchronous state created by this async call to become ready shall invoke the deferred function in the thread that called the waiting function". The answer to question 2, however, is not as clear. According to 30.6.6/23, s should be future_status::deferred because x refers to a deferred function that is not running, but it should also be future_status::ready because after executing f (and we saw that f is always executed) the state becomes ready. By the way, the expression "deferred function that is not running" is very unfortunate in itself, because it may apply to both the case where the function hasn't yet started, as well as the case where it was executed and completed.

While we clearly have a defect in the WP answering to question 2, it is my opinion that the answer to question 1 is wrong, which is even worse. Consider that the execution of the function f can take an arbitrarily long time. Having wait_for() invoke f is a potential violation of the reasonable expectation that the execution of x.wait_for(chrono::milliseconds(100)) shall take at most 100 milliseconds plus a delay dependent on the quality of implementation and the quality of management (as described in paper N3128). In fact, previous versions of the WP clearly specified that only function wait() is required to execute the deferred function, while wait_for() and wait_until() shouldn't.

The proposed resolution captures the intent that wait_for() and wait_until() should never attempt to invoke the deferred function. In other words, the P/R provides the following answers to the two questions above:

  1. no
  2. future_status::deferred

In order to simplify the wording, the definition of deferred function has been tweaked so that the function is no longer considered deferred once its evaluation has started, as suggested by Howard.

Discussions in the reflector questioned whether wait_for() and wait_until() should return immediately or actually wait hoping for a second thread to execute the deferred function. I believe that waiting could be useful only in a very specific scenario but detrimental in the general case and would introduce another source of ambiguity: should wait_for() return future_status::deferred or future_status::timeout after the wait? Therefore the P/R specifies that wait_for/wait_until shall return immediately, which is simpler, easier to explain and more useful in the general case.

[ Post-Rapperswil ]

Moved to Tentatively Ready after 5 positive votes on c++std-lib.

[ Adopted at 2010-11 Batavia ]

Proposed resolution:

The proposed wording changes are relative to the Final Committee Draft, N3126.

Note to the editor: the proposed wording is meant not be in conflict with any change proposed by paper N3128 "C++ Timeout Specification". Ellipsis are deliberately used to avoid any unintended overlapping.

  1. In [futures.unique_future] 30.6.6/22:

    Effects: none if the associated asynchronous state contains a deferred function (30.6.9), otherwise blocks until the associated asynchronous state is ready or [...].

  2. In [futures.unique_future] 30.6.6/23 first bullet:

    — future_status::deferred if the associated asynchronous state contains a deferred function that is not running.

  3. In [futures.unique_future] 30.6.6/25:

    Effects: none if the associated asynchronous state contains a deferred function (30.6.9), otherwise blocks until the associated asynchronous state is ready or [...].

  4. In [futures.unique_future] 30.6.6/26 first bullet:

    — future_status::deferred if the associated asynchronous state contains a deferred function that is not running.

  5. In [futures.shared_future] 30.6.7/27

    Effects: none if the associated asynchronous state contains a deferred function (30.6.9), otherwise blocks until the associated asynchronous state is ready or [...].

  6. In [futures.unique_future] 30.6.7/28 first bullet:

    — future_status::deferred if the associated asynchronous state contains a deferred function that is not running.

  7. In [futures.shared_future] 30.6.6/30:

    Effects: none if the associated asynchronous state contains a deferred function (30.6.9), otherwise blocks until the associated asynchronous state is ready or [...].

  8. In [futures.unique_future] 30.6.7/31 first bullet:

    — future_status::deferred if the associated asynchronous state contains a deferred function that is not running.

  9. In [futures.atomic_future] 30.6.8/23

    Effects: none if the associated asynchronous state contains a deferred function (30.6.9), otherwise blocks until the associated asynchronous state is ready or [...].

  10. In [futures.unique_future] 30.6.8/24 first bullet:

    — future_status::deferred if the associated asynchronous state contains a deferred function that is not running.

  11. In [futures.atomic_future] 30.6.8/27:

    Effects: none if the associated asynchronous state contains a deferred function (30.6.9), otherwise blocks until the associated asynchronous state is ready or [...].

  12. In [futures.unique_future] 30.6.8/28 first bullet:

    — future_status::deferred if the associated asynchronous state contains a deferred function that is not running.

  13. In [futures.async] 30.6.9/3 second bullet:

    [...] The first call to a function waitingrequiring a non-timed wait for the associated asynchronous state created by this async call to become ready shall invoke the deferred function in the thread that called the waiting function; once evaluation of INVOKE(g, xyz) begins, the function is no longer considered deferred all other calls waiting for the same associated asynchronous state to become ready shall block until the deferred function has completed.