Description
The I/O driver needs some restructuring in order to clean up the shutdown process.
Currently, internals are structured such that there is a cycle between ScheduledIo
(internal I/O resource state) and io::driver::Inner
. The driver's internal struct holds a slab containing all the resource's ScheduledIo
instances. Each ScheduleIo
tracks wakers that should be notified when I/O events arrive. The wakers hold a reference to the driver's inner structure in order to unpark an I/O driver blocked in poll
.
+---------------+
+----------------+ Wakers |
| +-------+-------+
| ^
v |
+------+------+ +-------+-------+
| Inner/Slab +-------->| ScheduledIo |
+------+------+ +-------+-------+
^ ^
| |
+------+------+ +-------+------+
| Driver | | Registration |
+-------------+ +--------------+
#3477 fixes the memory leak due to the cycle by ensuring wakers are dropped when a Registration
is dropped. This breaks the cycle. However, Wakers
may hold arbitrary structures. It is possible to implement a Waker
that also holds a Registration
. In this case, the cycle would not be broken leading again to a memory leak. This is a fairly convoluted scenario and probably does not happen much in practice, but it should still be fixed.
To fix this, we need the I/O driver shutdown process to clear any wakers being held by ScheduledIo
instances. To do this without introducing a race condition is a bit tricky. First, a "is_shut_down" flag needs to be set atomically in a way that ensures no further ScheduledIo
instances may be created. Then, each ScheduledIo
must be iterated and a shutdown
flag must be set on it. This can be set as part of the "readiness" field. If a waker is stored in the ScheduledIo
it should be notified & dropped.
Activity