-
Notifications
You must be signed in to change notification settings - Fork 13
Async and Future basics
When I started to write my Future implementation, I found that the official document is not clear about some basic rules. I created an issue in the official Rust repo https://github.com/rust-lang/rust/issues/73002. Although my problems are still not getting a clear answer. I hereby summarize some rules we should follow:
-
Waker is a token to register events to your event manager (timer, io poller, or synchronize counterpart), most likely a heap structure. What's inside and the relationship to RawWaker, the document is not clear enough; you can ignore the details if not trying to implement an async runtime. std::task::Waker is a 16B size structure, which is larger than a pointer.
-
Whether or not the same Waker can be registered and
wake()twice, the behavior is not specified by the async runtime, nor by the official document. I observe that the memory address of Wakers created by the same Future is the same. But you should not rely on this unspecified behavior. -
Once Waker is woken by some event, the async runtime will guarantee that poll() will be triggered eventually.
-
If a Future's poll() returns Ready() before the Waker gets woken. It's an OK and common behavior.
-
Before your Future goes to sleep, you register a Waker. And your counterpart may set the condition. These two operations may happen at the same time. If you decide to go the lockless way, you should check the condition after registering the Waker; otherwise, you will get a deadlock. (The counterpart might not see the Waker). But doing "register -> try -> sleep" also means there will be a meaningless Waker left to clean up on try Ok. you should consider this in the algorithm.
-
If your Future is woken and the condition is ready, you miss it because of forgetting to check, your Future will not be woken again. This will incur a deadlock.
-
On Poll::Ready returns, Future will not be called again, and will be dropped.
-
On Future being aborted (things like futures abortable or tokio::time::timeout), the future will be dropped.
-
So that inside Drop, no matter after ready or aborted, you should clean up some resources.
-
When you use Timeout to wrap a Future, I think timeout or ready checking on the poller side is sequential. So it's possible to ensure your message is not lost due to a timeout occurring. Most async channel implementations do not specify this behavior.
-
When you write one parent Future combining multiple children Futures, you can poll the included Futures in a certain order (some event prioritizing others), or randomized order (to ensure fairness). Eventually, only one condition can be returned by the parent Poll. Other children Future's condition might also be ready but ignored. This might be viewed as aborts. (Refer to the section before)
-
When you write a stream to includes multiple Futures, you have multiple chances of returning different items. Multiple conditions can have different Wakers to register. You should be aware of not ignoring all the events. When multiple conditions are met at the same time. If you have Wakers (like the
LockedWakerin Crossfire) persist in your Stream object, you can always check them when the next time poll_next() is called by next().await. This idea might get you some speed by avoiding the cost of re-registering the events.