We can't find the internet
Attempting to reconnect
Something went wrong!
Attempting to reconnect
← Async From First Principles step 15 of 25
Race two futures with a defined polling order
Update the supplied Select::poll so it supports two polling orders. A
select returns the first ready result it observes, while a join waits for
both results. Keep the supplied children, constructor, driver and race
function unchanged.
pub enum Either<L, R> { Left(L), Right(R) }
pub fn race(a_pends: u32, b_pends: u32, biased: bool) -> (String, Vec<char>)
Each Countdown logs its tag on every poll, returns Pending the requested
number of times, then returns its value. Child A has tag 'a' and value 10;
child B has tag 'b' and value 20. Return "a:10" or "b:20" with the
exact poll log.
- If
biasedis true, poll A first on every call toSelect::poll. - Otherwise, alternate the first child: A on call 1, B on call 2, A on call 3, and so on.
- If the first child is ready, return immediately without polling the other.
- If it is pending, poll the other child. Return its ready result, or return
Pendingif both children are pending.
Increment the poll counter once per call, not once per child. It starts at
zero, so determine the order from the old counter value before incrementing.
Both pending counts are below 100_000, within the supplied driver's budget.
For example:
race(1, 1, true) = ("a:10", ['a', 'b', 'a'])
race(1, 1, false) = ("b:20", ['a', 'b', 'b'])
race(0, 0, false) = ("a:10", ['a'])
The second child is not polled after a winner is found. When both could be ready on the same call, the chosen order determines which result is observed. There is no wall-clock race or extra thread in this exercise.
What alternating order does and does not guarantee
Alternating gives each child the first position on successive polls of the
same Select. It is not a general starvation guarantee. Each newly created
Select starts with A, so repeatedly constructing one with an immediately
ready A can still prevent B from being polled.
Biased ordering can be useful when branch priority is intentional. It can also hide a lower-priority branch: if A is ready, this implementation never checks B on that call. Real combinators may use other policies, including randomized order. Randomized behavior can be tested, but deterministic alternation makes this exercise's exact logs straightforward to check.
Completion and dropping are separate
Returning Ready does not itself destroy Select. The owner decides when
to drop it. Here block_on owns the select and drops it while returning its
result, which drops both stored child futures. The winning output has already
moved out; the losing child will receive no further polls.
Dropping a future runs its normal cleanup, including destructors. It does
not undo side effects or necessarily cancel work the future started elsewhere.
The shared poll log survives because race retains an Rc handle to it.
A losing operation may have made partial progress before it is dropped. Whether abandoning and later retrying that operation is safe depends on its API: consumed bytes, queue items or external effects may need explicit handling. Keeping progress in a longer-lived object can help, but does not by itself prove cancellation safety.
Pinning and wakeups
Select stores its children as Pin<Box<A>> and Pin<Box<B>>. Moving the
parent moves the pointers, not the child allocations. Its other fields are
ordinary scalar values, so this layout is automatically Unpin even when
the children are not. It has no stored output values that would affect that
automatic implementation. Do not add extra Unpin bounds to the children.
Forward the current context to each child being polled. The supplied
countdowns request a wake whenever they return Pending. The teaching driver
instead polls repeatedly with a no-op waker, so its output tests do not prove
that a general implementation handles wakeups correctly.
Like other futures, a completed Select should not be polled again. This
exercise specifies behavior only up to its first ready result.
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.