We can't find the internet
Attempting to reconnect
Something went wrong!
Attempting to reconnect
← Async From First Principles step 7 of 25
Queue another poll with Wake
Repair a task waker so a future can request another poll through a shared ready queue. Keep the supplied Polite and Forgetful futures unchanged.
pub struct TaskWaker { pub id: usize, pub queue: Arc<Mutex<VecDeque<usize>>> }
pub fn drive<F: Future<Output = u32>>(fut: F) -> Option<u32>
pub fn run(polite: bool, pends: u32) -> Option<u32>
Replace the starter's Rc<RefCell<_>> queue with Arc<Mutex<_>>. Implement
Wake for TaskWaker so both wake and wake_by_ref append its id to the queue.
Override wake_by_ref to enqueue directly without cloning the Arc. Each call
appends one id; do not deduplicate requests in this exercise.
Drive one task
Seed the queue with id zero, create a Waker for that id and pin the future. Repeat at most 10,000 times:
- Pop one id. If the queue is empty, return None immediately.
- Release the queue lock before polling the future.
- Poll once. Return Some(value) on Ready; continue on Pending.
Return None if the poll budget is exhausted. The driver has only one future,
so the id does not select between tasks. Keep the generic signature: callers
must not need an Unpin bound. Box::pin provides the required pinned storage.
This driver does not wait for timers, I/O or another thread to enqueue work. An empty queue means it stops now, not that a wakeup could never happen later. It is a teaching driver, not a general executor for externally awakened futures.
Expected results
Polite requests another poll before each Pending return. Forgetful makes no such request. Both return their total poll count when their pending count reaches zero. The harness serializes None as JSON null.
| Future | Pending count | Result |
|---|---|---|
| Polite | 0 |
Some(1) |
| Polite | 3 |
Some(4) |
| Polite | 9999 |
Some(10000) |
| Polite | 10000 or larger |
None |
| Forgetful | 0 |
Some(1) |
| Forgetful | any positive count | None |
pends is a u32. Even a very large count must stop at the driver budget;
the driver never needs to compute pends + 1 for those inputs.
Wake schedules work
A wakeup requests another poll; it does not itself poll the future. The executor
decides when to perform that work. The standard Wake trait provides a safe way
to construct a Waker from an Arc. Its default wake_by_ref clones the Arc and
calls wake; the override here avoids that reference-count update.
Waker::from(Arc<W>) requires W to implement Wake, Send, Sync and 'static.
The starter's Rc-backed state cannot satisfy those thread bounds. Arc protects
shared ownership and Mutex protects access to the queue. RefCell is not Sync,
though it can be Send when its contents are Send; it is not accurate to say
that every interior-mutable type lacks both traits.
See the Wake documentation.
Release the lock before a self-wakeup
Avoid polling inside this loop body:
while let Some(id) = queue.lock().unwrap().pop_front() {
// Polling here can try to lock the same queue again.
}
The temporary guard remains held in the successful while-let body. A future that wakes itself then tries to acquire the same mutex again, which is not supported as a recursive lock. Use a separate statement instead:
let id = queue.lock().unwrap().pop_front()?;
The guard is dropped at the statement boundary, before the subsequent poll. These loop fragments illustrate lock scope; integrate the separate pop into the supplied drive function.
Passing requires compilation, tests and Clippy with warnings denied. The None result can mean queue exhaustion or budget exhaustion; it is not a general diagnosis that a future can never complete.
Your code, saved here
Sign in to access your accepted solutions and saved draft history. You can keep reading the lesson without an account.
Sign in to your accountStuck?
Rust reference solution
Sign in to track your attempts toward unlocking the reference solution.