Unless I'm mistaken, the combinators listed below (and likely others, too), which all combine two features, break the contract of futures::task::Waker. Is this a problem?
As long as the executor keeps running and the task is not finished, it is guaranteed that each invocation of wake() (or wake_by_ref()) will be followed by at least one poll() of the task to which this Waker belongs. This makes it possible to temporarily yield to other tasks while running potentially unbounded processing loops.
Note that the above implies that multiple wake-ups may be coalesced into a single poll() invocation by the runtime.
Also note that yielding to competing tasks is not guaranteed: it is the executor’s choice which task to run and the executor may choose to run the current task again.
The combinators below combine two (or more) streams by polling one of them in each call to poll/poll_next. This is generally fine, if the executor regularly polls the futures/streams. However when the futures inside the stream relies on the above cited guarantee of invocation after waking something could break in the futures inside the stream.
For example consider the following situation:
Stream A contains futures that call wake and rely on this guarantee of invocation
Stream B contains some other futures
Stream C is a combination of A and B, which are combined using select (the easiest one to describe this issue for), which does round-robin
Executor polls C, which polls A
A does something and causes a call to wake (for example after finishing some computation in a separate thread)
Executor reacts to this wake call and polls C again, which polls B (due to using round-robin as the strategy)
Result: The executor is still running, the task is not finished and the Executor will not poll C (and thus A) again unless it receives another wake call or has nothing to do and just polls C (out of boredom).
As far as I can tell, this could lead to unnecessary delays in future completion and breaks (or at least loosens) the guarantees given by Waker. From the viewpoint of the futures in A, this basically breaks the guarantees of wake().
Unless I'm mistaken, the combinators listed below (and likely others, too), which all combine two features, break the contract of futures::task::Waker. Is this a problem?
Source: https://docs.rs/futures/latest/futures/task/struct.Waker.html
Why might this be relevant
The combinators below combine two (or more) streams by polling one of them in each call to
poll
/poll_next
. This is generally fine, if the executor regularly polls the futures/streams. However when the futures inside the stream relies on the above cited guarantee of invocation after waking something could break in the futures inside the stream.For example consider the following situation:
select
(the easiest one to describe this issue for), which does round-robinwake
(for example after finishing some computation in a separate thread)wake
call or has nothing to do and just polls C (out of boredom).As far as I can tell, this could lead to unnecessary delays in future completion and breaks (or at least loosens) the guarantees given by
Waker
. From the viewpoint of the futures in A, this basically breaks the guarantees ofwake()
.Affected combinators (likely incomplete list)
Not affected (as it polls all futures/streams)
If this is not a problem please explain why, I'm trying to understand the details of how executor, streams and futures interact.