Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Streams: wake may not poll futures if two streams are combined (and only one future is polled) #2775

Open
DragonDev1906 opened this issue Oct 4, 2023 · 0 comments

Comments

@DragonDev1906
Copy link

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.

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:

  • 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().

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant