Skip to content

Commit 81bcdac

Browse files
Add BoundLocalPool and BoundLocalSpawner
Adds derivatives of LocalPool and BoundLocalSpawner with a generic lifetime parameter that replaces the previously used 'static, and aliases both types with a 'static parameter to the originals. Add a BoundLocalSpawn trait in a similar fashion. At the same time simplify the implementation of Spawn, LocalSpawn and BoundLocalSpawn to just work on any type implementing Deref to a type implementing a spawn trait.
1 parent 48b58c0 commit 81bcdac

File tree

8 files changed

+175
-112
lines changed

8 files changed

+175
-112
lines changed

futures-executor/src/lib.rs

+4-1
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,10 @@ extern crate std;
5454
#[cfg(feature = "std")]
5555
mod local_pool;
5656
#[cfg(feature = "std")]
57-
pub use crate::local_pool::{block_on, block_on_stream, BlockingStream, LocalPool, LocalSpawner};
57+
pub use crate::local_pool::{
58+
block_on, block_on_stream, BlockingStream, BoundLocalPool, BoundLocalSpawner, LocalPool,
59+
LocalSpawner,
60+
};
5861

5962
#[cfg(feature = "thread-pool")]
6063
#[cfg_attr(docsrs, doc(cfg(feature = "thread-pool")))]

futures-executor/src/local_pool.rs

+52-17
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use crate::enter;
22
use futures_core::future::Future;
33
use futures_core::stream::Stream;
44
use futures_core::task::{Context, Poll};
5-
use futures_task::{waker_ref, ArcWake};
5+
use futures_task::{waker_ref, ArcWake, BoundLocalSpawn};
66
use futures_task::{FutureObj, LocalFutureObj, LocalSpawn, Spawn, SpawnError};
77
use futures_util::pin_mut;
88
use futures_util::stream::FuturesUnordered;
@@ -17,6 +17,36 @@ use std::sync::{
1717
use std::thread::{self, Thread};
1818
use std::vec::Vec;
1919

20+
/// A single-threaded task pool with bound lifetime for polling futures to
21+
/// completion.
22+
///
23+
/// This executor allows you to multiplex any number of tasks onto a single
24+
/// thread. It's appropriate to poll strictly I/O-bound futures that do very
25+
/// little work in between I/O actions. The lifetime of the executor is bound by
26+
/// a generic parameter. Futures associated with the executor need only outlive
27+
/// this lifetime. That uncompleted futures are dropped when the lifetime of the
28+
/// executor expires.
29+
///
30+
/// To get a handle to the pool that implements [`Spawn`](futures_task::Spawn),
31+
/// use the [`spawner()`](BoundLocalPool::spawner) method. Because the executor
32+
/// is single-threaded, it supports a special form of task spawning for
33+
/// non-`Send` futures, via
34+
/// [`spawn_local_obj`](futures_task::LocalSpawn::spawn_local_obj).
35+
/// Additionally, tasks with a limited lifetime can be spawned via
36+
/// [`spawn_bound_local_obj`](futures_task::BoundLocalSpawn::spawn_bound_local_obj).
37+
#[derive(Debug)]
38+
pub struct BoundLocalPool<'a> {
39+
pool: FuturesUnordered<LocalFutureObj<'a, ()>>,
40+
incoming: Rc<Incoming<'a>>,
41+
}
42+
43+
/// A handle to a [`BoundLocalPool`] that implements
44+
/// [`BoundLocalSpawn`](futures_task::BoundLocalSpawn).
45+
#[derive(Clone, Debug)]
46+
pub struct BoundLocalSpawner<'a> {
47+
incoming: Weak<Incoming<'a>>,
48+
}
49+
2050
/// A single-threaded task pool for polling futures to completion.
2151
///
2252
/// This executor allows you to multiplex any number of tasks onto a single
@@ -28,19 +58,13 @@ use std::vec::Vec;
2858
/// [`spawner()`](LocalPool::spawner) method. Because the executor is
2959
/// single-threaded, it supports a special form of task spawning for non-`Send`
3060
/// futures, via [`spawn_local_obj`](futures_task::LocalSpawn::spawn_local_obj).
31-
#[derive(Debug)]
32-
pub struct LocalPool {
33-
pool: FuturesUnordered<LocalFutureObj<'static, ()>>,
34-
incoming: Rc<Incoming>,
35-
}
61+
pub type LocalPool = BoundLocalPool<'static>;
3662

37-
/// A handle to a [`LocalPool`] that implements [`Spawn`](futures_task::Spawn).
38-
#[derive(Clone, Debug)]
39-
pub struct LocalSpawner {
40-
incoming: Weak<Incoming>,
41-
}
63+
/// A handle to a [`LocalPool`] that implements [`Spawn`](futures_task::Spawn)
64+
/// and [`LocalSpawn`](futures_task::LocalSpawn).
65+
pub type LocalSpawner = BoundLocalSpawner<'static>;
4266

43-
type Incoming = RefCell<Vec<LocalFutureObj<'static, ()>>>;
67+
type Incoming<'a> = RefCell<Vec<LocalFutureObj<'a, ()>>>;
4468

4569
pub(crate) struct ThreadNotify {
4670
/// The (single) executor thread.
@@ -107,15 +131,15 @@ fn woken() -> bool {
107131
CURRENT_THREAD_NOTIFY.with(|thread_notify| thread_notify.unparked.load(Ordering::Acquire))
108132
}
109133

110-
impl LocalPool {
134+
impl<'a> BoundLocalPool<'a> {
111135
/// Create a new, empty pool of tasks.
112136
pub fn new() -> Self {
113137
Self { pool: FuturesUnordered::new(), incoming: Default::default() }
114138
}
115139

116140
/// Get a clonable handle to the pool as a [`Spawn`].
117-
pub fn spawner(&self) -> LocalSpawner {
118-
LocalSpawner { incoming: Rc::downgrade(&self.incoming) }
141+
pub fn spawner(&self) -> BoundLocalSpawner<'a> {
142+
BoundLocalSpawner { incoming: Rc::downgrade(&self.incoming) }
119143
}
120144

121145
/// Run all tasks in the pool to completion.
@@ -362,7 +386,7 @@ impl<S: Stream + Unpin> Iterator for BlockingStream<S> {
362386
}
363387
}
364388

365-
impl Spawn for LocalSpawner {
389+
impl Spawn for BoundLocalSpawner<'_> {
366390
fn spawn_obj(&self, future: FutureObj<'static, ()>) -> Result<(), SpawnError> {
367391
if let Some(incoming) = self.incoming.upgrade() {
368392
incoming.borrow_mut().push(future.into());
@@ -381,7 +405,7 @@ impl Spawn for LocalSpawner {
381405
}
382406
}
383407

384-
impl LocalSpawn for LocalSpawner {
408+
impl LocalSpawn for BoundLocalSpawner<'_> {
385409
fn spawn_local_obj(&self, future: LocalFutureObj<'static, ()>) -> Result<(), SpawnError> {
386410
if let Some(incoming) = self.incoming.upgrade() {
387411
incoming.borrow_mut().push(future);
@@ -399,3 +423,14 @@ impl LocalSpawn for LocalSpawner {
399423
}
400424
}
401425
}
426+
427+
impl<'a> BoundLocalSpawn<'a> for BoundLocalSpawner<'a> {
428+
fn spawn_bound_local_obj(&self, future: LocalFutureObj<'a, ()>) -> Result<(), SpawnError> {
429+
if let Some(incoming) = self.incoming.upgrade() {
430+
incoming.borrow_mut().push(future);
431+
Ok(())
432+
} else {
433+
Err(SpawnError::shutdown())
434+
}
435+
}
436+
}

futures-task/src/lib.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ extern crate alloc;
1616
extern crate std;
1717

1818
mod spawn;
19-
pub use crate::spawn::{LocalSpawn, Spawn, SpawnError};
19+
pub use crate::spawn::{BoundLocalSpawn, LocalSpawn, Spawn, SpawnError};
2020

2121
#[cfg_attr(target_os = "none", cfg(target_has_atomic = "ptr"))]
2222
#[cfg(feature = "alloc")]

futures-task/src/spawn.rs

+30-87
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,22 @@ pub trait LocalSpawn {
5151
}
5252
}
5353

54+
/// The `BoundLocalSpawn` is similar to [`LocalSpawn`], but allows spawning
55+
/// futures that don't implement `Send` and have a lifetime that only needs to
56+
/// exceed that of the associated executor.
57+
pub trait BoundLocalSpawn<'a> {
58+
/// Spawns a future that will be run to completion or until the executor is
59+
/// dropped.
60+
///
61+
/// # Errors
62+
///
63+
/// The executor may be unable to spawn tasks. Spawn errors should
64+
/// represent relatively rare scenarios, such as the executor
65+
/// having been shut down so that it is no longer able to accept
66+
/// tasks.
67+
fn spawn_bound_local_obj(&self, future: LocalFutureObj<'a, ()>) -> Result<(), SpawnError>;
68+
}
69+
5470
/// An error that occurred during spawning.
5571
pub struct SpawnError {
5672
_priv: (),
@@ -83,17 +99,10 @@ impl SpawnError {
8399
}
84100
}
85101

86-
impl<Sp: ?Sized + Spawn> Spawn for &Sp {
87-
fn spawn_obj(&self, future: FutureObj<'static, ()>) -> Result<(), SpawnError> {
88-
Sp::spawn_obj(self, future)
89-
}
90-
91-
fn status(&self) -> Result<(), SpawnError> {
92-
Sp::status(self)
93-
}
94-
}
95-
96-
impl<Sp: ?Sized + Spawn> Spawn for &mut Sp {
102+
impl<T, Sp: ?Sized + Spawn> Spawn for T
103+
where
104+
T: std::ops::Deref<Target = Sp>,
105+
{
97106
fn spawn_obj(&self, future: FutureObj<'static, ()>) -> Result<(), SpawnError> {
98107
Sp::spawn_obj(self, future)
99108
}
@@ -103,17 +112,10 @@ impl<Sp: ?Sized + Spawn> Spawn for &mut Sp {
103112
}
104113
}
105114

106-
impl<Sp: ?Sized + LocalSpawn> LocalSpawn for &Sp {
107-
fn spawn_local_obj(&self, future: LocalFutureObj<'static, ()>) -> Result<(), SpawnError> {
108-
Sp::spawn_local_obj(self, future)
109-
}
110-
111-
fn status_local(&self) -> Result<(), SpawnError> {
112-
Sp::status_local(self)
113-
}
114-
}
115-
116-
impl<Sp: ?Sized + LocalSpawn> LocalSpawn for &mut Sp {
115+
impl<T, Sp: ?Sized + LocalSpawn> LocalSpawn for T
116+
where
117+
T: std::ops::Deref<Target = Sp>,
118+
{
117119
fn spawn_local_obj(&self, future: LocalFutureObj<'static, ()>) -> Result<(), SpawnError> {
118120
Sp::spawn_local_obj(self, future)
119121
}
@@ -123,70 +125,11 @@ impl<Sp: ?Sized + LocalSpawn> LocalSpawn for &mut Sp {
123125
}
124126
}
125127

126-
#[cfg(feature = "alloc")]
127-
mod if_alloc {
128-
use super::*;
129-
use alloc::{boxed::Box, rc::Rc};
130-
131-
impl<Sp: ?Sized + Spawn> Spawn for Box<Sp> {
132-
fn spawn_obj(&self, future: FutureObj<'static, ()>) -> Result<(), SpawnError> {
133-
(**self).spawn_obj(future)
134-
}
135-
136-
fn status(&self) -> Result<(), SpawnError> {
137-
(**self).status()
138-
}
139-
}
140-
141-
impl<Sp: ?Sized + LocalSpawn> LocalSpawn for Box<Sp> {
142-
fn spawn_local_obj(&self, future: LocalFutureObj<'static, ()>) -> Result<(), SpawnError> {
143-
(**self).spawn_local_obj(future)
144-
}
145-
146-
fn status_local(&self) -> Result<(), SpawnError> {
147-
(**self).status_local()
148-
}
149-
}
150-
151-
impl<Sp: ?Sized + Spawn> Spawn for Rc<Sp> {
152-
fn spawn_obj(&self, future: FutureObj<'static, ()>) -> Result<(), SpawnError> {
153-
(**self).spawn_obj(future)
154-
}
155-
156-
fn status(&self) -> Result<(), SpawnError> {
157-
(**self).status()
158-
}
159-
}
160-
161-
impl<Sp: ?Sized + LocalSpawn> LocalSpawn for Rc<Sp> {
162-
fn spawn_local_obj(&self, future: LocalFutureObj<'static, ()>) -> Result<(), SpawnError> {
163-
(**self).spawn_local_obj(future)
164-
}
165-
166-
fn status_local(&self) -> Result<(), SpawnError> {
167-
(**self).status_local()
168-
}
169-
}
170-
171-
#[cfg_attr(target_os = "none", cfg(target_has_atomic = "ptr"))]
172-
impl<Sp: ?Sized + Spawn> Spawn for alloc::sync::Arc<Sp> {
173-
fn spawn_obj(&self, future: FutureObj<'static, ()>) -> Result<(), SpawnError> {
174-
(**self).spawn_obj(future)
175-
}
176-
177-
fn status(&self) -> Result<(), SpawnError> {
178-
(**self).status()
179-
}
180-
}
181-
182-
#[cfg_attr(target_os = "none", cfg(target_has_atomic = "ptr"))]
183-
impl<Sp: ?Sized + LocalSpawn> LocalSpawn for alloc::sync::Arc<Sp> {
184-
fn spawn_local_obj(&self, future: LocalFutureObj<'static, ()>) -> Result<(), SpawnError> {
185-
(**self).spawn_local_obj(future)
186-
}
187-
188-
fn status_local(&self) -> Result<(), SpawnError> {
189-
(**self).status_local()
190-
}
128+
impl<'a, T, Sp: ?Sized + BoundLocalSpawn<'a>> BoundLocalSpawn<'a> for T
129+
where
130+
T: std::ops::Deref<Target = Sp>,
131+
{
132+
fn spawn_bound_local_obj(&self, future: LocalFutureObj<'a, ()>) -> Result<(), SpawnError> {
133+
Sp::spawn_bound_local_obj(self, future)
191134
}
192135
}

futures-util/src/stream/futures_unordered/mod.rs

+8-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ use core::sync::atomic::{AtomicBool, AtomicPtr};
1717
use futures_core::future::Future;
1818
use futures_core::stream::{FusedStream, Stream};
1919
use futures_core::task::{Context, Poll};
20-
use futures_task::{FutureObj, LocalFutureObj, LocalSpawn, Spawn, SpawnError};
20+
use futures_task::{BoundLocalSpawn, FutureObj, LocalFutureObj, LocalSpawn, Spawn, SpawnError};
2121

2222
mod abort;
2323

@@ -78,6 +78,13 @@ impl LocalSpawn for FuturesUnordered<LocalFutureObj<'_, ()>> {
7878
}
7979
}
8080

81+
impl<'a> BoundLocalSpawn<'a> for FuturesUnordered<LocalFutureObj<'a, ()>> {
82+
fn spawn_bound_local_obj(&self, future_obj: LocalFutureObj<'a, ()>) -> Result<(), SpawnError> {
83+
self.push(future_obj);
84+
Ok(())
85+
}
86+
}
87+
8188
// FuturesUnordered is implemented using two linked lists. One which links all
8289
// futures managed by a `FuturesUnordered` and one that tracks futures that have
8390
// been scheduled for polling. The first linked list allows for thread safe

futures-util/src/task/mod.rs

+4-2
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,9 @@
1313
#[doc(no_inline)]
1414
pub use core::task::{Context, Poll, RawWaker, RawWakerVTable, Waker};
1515

16-
pub use futures_task::{FutureObj, LocalFutureObj, LocalSpawn, Spawn, SpawnError, UnsafeFutureObj};
16+
pub use futures_task::{
17+
BoundLocalSpawn, FutureObj, LocalFutureObj, LocalSpawn, Spawn, SpawnError, UnsafeFutureObj,
18+
};
1719

1820
pub use futures_task::noop_waker;
1921
pub use futures_task::noop_waker_ref;
@@ -37,4 +39,4 @@ pub use futures_task::{waker_ref, WakerRef};
3739
pub use futures_core::task::__internal::AtomicWaker;
3840

3941
mod spawn;
40-
pub use self::spawn::{LocalSpawnExt, SpawnExt};
42+
pub use self::spawn::{BoundLocalSpawnExt, LocalSpawnExt, SpawnExt};

0 commit comments

Comments
 (0)