You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Commit 024a3d6
Browse filesBrowse the repository at this point in the historyBrowse files
74: Add Pin<Box<Fut>> to remove requirement for Unpin. Box<T> has a blanket
implementation for Unpin
75: Remove soft-deprecated pin_mut! and replace with std::pin::pin!
76: Replace near-duplicated chapter 3 example with something different
Copy file name to clipboardExpand all lines: async-book/src/ch03-how-poll-works.md
+48-23Lines changed: 48 additions & 23 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -133,53 +133,78 @@ impl Future for MyFuture {
133
133
4.**Don't poll after `Ready`** — behavior is **unspecified** (may panic, return `Pending`, or repeat `Ready`). Only `FusedFuture` guarantees safe post-completion polling
134
134
135
135
<details>
136
-
<summary><strong>🏋️ Exercise: Implement a CountdownFuture</strong> (click to expand)</summary>
136
+
<summary><strong>🏋️ Exercise: Spurious-Wake-Safe Flag Future</strong> (click to expand)</summary>
137
137
138
-
**Challenge**: Implement a `CountdownFuture` that counts down from N to 0, *printing* the current count as a side-effect each time it's polled. When it reaches 0, it completes with `Ready("Liftoff!")`. (Note: a `Future` produces only **one**final value — the printing is a side-effect, not a yielded value. For multiple async values, see `Stream` in Ch. 11.)
138
+
**Challenge**: Implement a `FlagFuture` that wraps a shared `Arc<AtomicBool>` flag. When polled, it checks whether the flag is `true`. If so, it completes with `Ready(())`. If not, it stores the waker and returns `Pending`. The twist: the future must handle **spurious wakes**correctly — it must re-check the flag on every poll, never assuming the flag is set just because it was woken.
139
139
140
-
*Hint*: This doesn't need a real I/O source — it can wake itself immediately with `cx.waker().wake_by_ref()` after each decrement.
140
+
*Hint*: You'll need an `Arc<Mutex<Option<Waker>>>` (or similar) so an external thread can set the flag and wake the future. Use `poll_fn` for a concise alternative solution.
// if flag.load(Ordering::Acquire) { Poll::Ready(()) } else { Poll::Pending }
203
+
// }).await
204
+
// }
180
205
```
181
206
182
-
**Key takeaway**: Even though this future is always ready to progress, it returns `Pending` to yield control between steps. It calls `wake_by_ref()` immediately so the executor re-polls it right away. This is the basis of cooperative multitasking — each future voluntarily yields.
207
+
**Key takeaway**: The double-check pattern (check → store waker → check again) is essential to avoid a race between the condition changing and the waker being registered. This is the real-world pattern that all I/O futures use internally, and it demonstrates why handling spurious wakes matters.
**Key takeaway**: The retry future is itself a state machine: it holds the current attempt and creates new inner futures on failure. This is how combinators compose — futures all the way down.
346
+
**Key takeaway**: The retry future is itself a state machine: it holds the current attempt and creates new inner futures on failure. Wrapping the inner future in `Pin<Box<Fut>>` removes the `Fut: Unpin` bound — since `Pin<Box<T>>` is always `Unpin`, the struct remains easy to work with while supporting any future type. This is how combinators compose — futures all the way down.
0 commit comments