mirror of
https://github.com/tokio-rs/tokio.git
synced 2026-08-26 00:00:16 +02:00
Remove Send from Future/Stream
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.
This commit is contained in:
+27
-3
@@ -193,10 +193,10 @@ impl Loop {
|
||||
pub fn run<F: Future>(&mut self, f: F) -> Result<F::Item, F::Error> {
|
||||
let (tx_res, rx_res) = mpsc::channel();
|
||||
let handle = self.handle();
|
||||
f.then(move |res| {
|
||||
self.add_loop_data(f.then(move |res| {
|
||||
handle.shutdown();
|
||||
tx_res.send(res)
|
||||
}).forget();
|
||||
})).forget();
|
||||
|
||||
self._run();
|
||||
|
||||
@@ -780,6 +780,30 @@ impl<A: 'static> LoopData<A> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: Future> Future for LoopData<A> {
|
||||
type Item = A::Item;
|
||||
type Error = A::Error;
|
||||
|
||||
fn poll(&mut self, task: &mut Task) -> Poll<A::Item, A::Error> {
|
||||
// If we're on the right thread, then we can proceed. Otherwise we need
|
||||
// to go and get polled on the right thread.
|
||||
if let Some(inner) = self.get_mut() {
|
||||
return inner.poll(task)
|
||||
}
|
||||
task.poll_on(self.executor());
|
||||
Poll::NotReady
|
||||
}
|
||||
|
||||
fn schedule(&mut self, task: &mut Task) {
|
||||
// If we're on the right thread, then we're good to go, otherwise we
|
||||
// need to get poll'd to tell the task to move somewhere else.
|
||||
match self.get_mut() {
|
||||
Some(inner) => inner.schedule(task),
|
||||
None => task.notify(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: 'static> Drop for LoopData<A> {
|
||||
fn drop(&mut self) {
|
||||
// The `DropBox` we store internally will cause a memory leak if it's
|
||||
@@ -944,7 +968,7 @@ struct LoopFuture<T, U> {
|
||||
}
|
||||
|
||||
impl<T, U> LoopFuture<T, U>
|
||||
where T: Send + 'static,
|
||||
where T: 'static,
|
||||
{
|
||||
fn poll<F>(&mut self, f: F) -> Poll<T, io::Error>
|
||||
where F: FnOnce(&Loop, U) -> io::Result<T>,
|
||||
|
||||
Reference in New Issue
Block a user