SleepProvider is_inserted is wrong
In crates/tor-rtmock/src/time.rs
, there is a flag per sleeping future, is_inserted
, which is supposed to record whether the future is enqueued in SleepSchedule.sleepers
. This ought to mean, whether its Waker
is properly enrolled in our data structure so that we will wake it up.
But it is wrong to retain an old Waker
from a previous poll
call.
There is no guarantee that a future will be always polled on the same task. It might be moved to a different task, and polled there. When that happens, poll
will get a fresh Waker
for that other task. With MockSleepProvider
, this will go wrong: the recorded Waker
in sleepers
will be stale, and when .wake()
is called on it, the executor will treat this as an instruction to wake the wrong task (pointlessly but generally harmlessly) or no task at all (this is not an error). I don't know if we ever move futures between tasks.
This behaviour has another practical consequence. Since !1591 (merged), MockExecutor
can print out a dump of the sleeping locations of every task (in the form of backtraces). The reuse of an old Waker
can mean that MockExecutor
's way of detecting sleep locations is defeated. The task, which is waiting on a timeout will show as stuck!
(even though, if the future didn't move tasks, it would wake up when mock time advances). (I think this can only happen if certain combinators are used, eg select
.) This is quite confusing.
So, I think each call to poll
must clone the Waker
.
(I'm not particularly enamoured of the code in time.rs
. The implementation seems complex and I find the semantics confusing. I might have a go at making a simpler mock time implementation which doesn't have block_advance
etc.; IMO MockExecutor
is a better place for that.)