* Remove `LoopData` as it's no longer necessary
* Add `LoopHandle::spawn` to spawn new futures onto an event loop
* Add `LoopData::spawn` to also spawn new futures onto an event loop
* Rejigger the implementation of the event loop a bit (make a slab of futures),
but otherwise everything else is pretty constant.
Split it up into a number of targeted modules for each purpose, for example loop
data, I/O sources, timeouts, and channels. No actual change is intended to be
part of this commit.
This commit contains a few refactorings, but the major goal is to remove the
`Arc` that's stored inside of each `ReadinessStream` and `Scheduled` slot in the
event loop. The original purpose of this `Arc` was to share the I/O object among
the concrete handle itself and the event loop. The event loop would then change
how the socket is registered over time and then deregister it when it gets a
"shutdown request".
Nowadays, however, once an I/O object is registered with the event loop it's
never updated. Additionally, we don't actually need to call `deregister` but can
rather just instead close the I/O object itself and let the kernel/event loop
take care of the cleanup. All we need to do on deregistering is free up the slab
entry.
The major result of this commit is that I/O objects no longer need to be `Sync`
(as they're not stored in an `Arc`). Instead they just need to be `Send +
'static` as one might otherwise expect.
Along the way this also refactors a few pieces here and there to make more sense
in this new scheme. The `ReadinessStream` type now has a type parameter
indicating an owned reference to the I/O object it wraps. This can be accessed
via the `get_ref` and `get_mut` methods. Additionally I/O tokens on the event
loop are now a full-fledged `IoToken` type which we can change in the future if
we need to.
We know that the future will never persist beyond this stack frame, so we can
just leave its ownership on the stack frame itself and receive notifications off
the event loop that we need to poll it.
The core trait and associated types no longer require the `'static` bounds, and
all associated methods have also had the `'static` bounds removed. The only
location for the `'static` bound is `forget`.
While the restriction of `'static` on `forget` is here to stay, we'll soon
enable `Loop::run` to take a non-`'static` future, allowing driving a
non-`'static` future.
Also involved yet another round of bug fixes to the timer wheel as well as an
unfortunately serious rejiggering of the level-translation into libcurl. Eew.
* Auto-register interest whenever we see WouldBlock
* Remove implementations of `Stream<Item=Ready>`, no longer needed
* Add explicit `poll_{read,write}` methods, if needed
* Remove all I/O streams, libstd ones suffice
* Update all I/O futures
A more appealing model is actually just automatically inferring what needs to be
scheduled based on what actions are done during poll. For example if during a
poll you check a oneshot channel, then the current task is registered for being
woken up if it's not ready. Similarly this will apply to I/O where if I/O is
attempted but we see EAGAIN then we'll schedule the task to get notified when
it's ready.
This may also have performance benefits in some niche situations because you
don't need to recompute where you are in the state machine both during poll and
during schedule. Instead, it now happens all at once.
This bound existed for two primary reasons, both detail below, and both of which
have now been solved.
One of the primary reasons this existed was due to the presence of `tailcall`.
Each standard combinator will call `tailcall` as appropriate, storing the
resulting trait object. Storing trait objects influences the applicatoin of the
`Send` and `Sync` bounds normally, but a key insight here is that we're not
storing trait objects but rather just pieces of otherwise internal futures.
With this insight the main storage for these futures, `Collapsed`, could simply
implement `Send` so long as the future itself originally implemented `Send`.
This in turn means that `tailcall` must be an `unsafe` method, but it seems well
worth the benefit of relaxing the `Send` bound.
The second primary reason for this bound was so the `Task` itself could be send.
This is critical for ensuring that futures can receive notifications from
multiple threads (e.g. be a future waiting on sources of multiple events).
Another key insight here, however, is that only the *outer* future needs to be
`Send`. We already have a solution, with `LoopData`, to make non-`Send` data
`Send`. By implementing `Future` directly for `LoopData<F: Future>`, this means
that it's trivial to make any future sendable by simply pinning it to an event
loop!
With these two pieces combined, it means that `Send` is no longer needed as a
bound on the `Future` and `Stream` traits. It may practically mean that
`LoopData` is used commonly in some scenarios, but that's quite a small price to
pay for relaxing the requirements of the core trait.
Some other ramifications of this change are:
* The `Future::boxed` and `Stream::boxed` methods now require `Self` to adhere
to `Send`. This is expected to be the most common case, and in the less common
case of not-`Send` `Box::new` can be used.
* Two new type aliases, `BoxFuture` and `BoxStream` have been introduced to
assist in writing APIs that return a trait object which is `Send`. Both of
these type aliases package in the `Send` bound.
* A new `LoopPin` type, added in the previous commit, can be used to easily
generate handles that can be used to pin futures to an event loop without
having a literal reference to the event loop itself.