pub struct JoinSet<T> { /* private fields */ }Expand description
A collection of spawned local tasks that yields results as tasks complete.
JoinSet owns each task’s JoinHandle. Dropping the set aborts any tasks
that are still running, making it a structured-concurrency primitive: child
tasks cannot silently outlive the owner unless detach_all
is called first.
Like all futures in this runtime, tasks in a JoinSet are !Send and stay on
the runtime thread where they were spawned. This differs from Tokio’s
multithreaded JoinSet: runite owns local tasks on one event-loop thread,
dropping the set aborts remaining tasks, and dropping an individual
JoinHandle detaches that task instead of cancelling it.
§Examples
use std::cell::RefCell;
use std::rc::Rc;
let results = Rc::new(RefCell::new(Vec::new()));
let results_task = Rc::clone(&results);
runite::spawn(async move {
let mut set = runite::task::JoinSet::new();
set.spawn(async { 1usize });
set.spawn(async { 2usize });
while let Some(result) = set.join_next().await {
results_task.borrow_mut().push(result.expect("task should finish"));
}
});
runite::run();
results.borrow_mut().sort_unstable();
assert_eq!(&*results.borrow(), &[1, 2]);Implementations§
Source§impl<T> JoinSet<T>
impl<T> JoinSet<T>
Sourcepub fn new() -> Self
pub fn new() -> Self
Creates an empty JoinSet.
§Examples
use std::rc::Rc;
use std::cell::Cell;
let was_empty = Rc::new(Cell::new(false));
let was_empty_task = Rc::clone(&was_empty);
runite::spawn(async move {
let set = runite::task::JoinSet::<usize>::new();
was_empty_task.set(set.is_empty());
});
runite::run();
assert!(was_empty.get());Sourcepub fn spawn<F>(&mut self, future: F)where
F: Future<Output = T> + 'static,
T: 'static,
pub fn spawn<F>(&mut self, future: F)where
F: Future<Output = T> + 'static,
T: 'static,
Spawns future on the current runtime thread and adds it to the set.
The task is scheduled through crate::spawn as a microtask. Its output
can later be retrieved by awaiting join_next.
§Examples
use std::rc::Rc;
use std::cell::Cell;
let result = Rc::new(Cell::new(0));
let result_task = Rc::clone(&result);
runite::spawn(async move {
let mut set = runite::task::JoinSet::new();
set.spawn(async { 42 });
result_task.set(set.join_next().await.unwrap().unwrap());
});
runite::run();
assert_eq!(result.get(), 42);Sourcepub fn join_next(
&mut self,
) -> impl Future<Output = Option<Result<T, JoinError>>> + '_
pub fn join_next( &mut self, ) -> impl Future<Output = Option<Result<T, JoinError>>> + '_
Waits for the next task in the set to complete.
Resolves to Some(Ok(value)) for a completed task,
Some(Err(JoinError::Aborted)) for an aborted task, or None when the
set is empty. abort_all keeps handles in the set, so aborted tasks are
reported by subsequent calls to join_next. detach_all removes handles,
so detached tasks will not be reported.
Each call returns one ready task result. If several tasks are ready at once, selection follows the set’s internal scan order and is not a stable completion-order guarantee.
§Performance
This implementation linearly scans stored handles and registers the same waker with every pending task; smarter ready-queue wake bookkeeping is a follow-up optimization.
§Examples
use std::rc::Rc;
use std::cell::Cell;
let result = Rc::new(Cell::new(None));
let result_task = Rc::clone(&result);
runite::spawn(async move {
let mut set = runite::task::JoinSet::new();
set.spawn(async { "done" });
result_task.set(Some(set.join_next().await.unwrap().unwrap()));
});
runite::run();
assert_eq!(result.get(), Some("done"));Sourcepub fn abort_all(&mut self)
pub fn abort_all(&mut self)
Aborts all tasks currently owned by the set.
Aborted handles remain in the set. Drain them with
join_next to observe their
JoinError::Aborted results.
§Examples
use std::rc::Rc;
use std::cell::Cell;
let aborted = Rc::new(Cell::new(false));
let aborted_task = Rc::clone(&aborted);
runite::spawn(async move {
let mut set = runite::task::JoinSet::<()>::new();
set.spawn(async { std::future::pending::<()>().await });
set.abort_all();
let err = set.join_next().await.unwrap().unwrap_err();
aborted_task.set(err.is_aborted());
});
runite::run();
assert!(aborted.get());Sourcepub fn detach_all(&mut self)
pub fn detach_all(&mut self)
Detaches all tasks from the set without aborting them.
After this call the set is empty, and the detached tasks continue running
to completion in the background. Dropping the set after detach_all will
not abort those tasks.
§Examples
use std::rc::Rc;
use std::cell::Cell;
let completed = Rc::new(Cell::new(false));
let completed_task = Rc::clone(&completed);
runite::spawn(async move {
let mut set = runite::task::JoinSet::new();
set.spawn(async move { completed_task.set(true); });
set.detach_all();
});
runite::run();
assert!(completed.get());Sourcepub fn len(&self) -> usize
pub fn len(&self) -> usize
Returns the number of task handles currently owned by the set.
§Examples
use std::rc::Rc;
use std::cell::Cell;
let len = Rc::new(Cell::new(0));
let len_task = Rc::clone(&len);
runite::spawn(async move {
let mut set = runite::task::JoinSet::new();
set.spawn(async { 1 });
len_task.set(set.len());
});
runite::run();
assert_eq!(len.get(), 1);Sourcepub fn is_empty(&self) -> bool
pub fn is_empty(&self) -> bool
Returns true when the set owns no task handles.
§Examples
use std::rc::Rc;
use std::cell::Cell;
let empty = Rc::new(Cell::new(false));
let empty_task = Rc::clone(&empty);
runite::spawn(async move {
let set = runite::task::JoinSet::<usize>::new();
empty_task.set(set.is_empty());
});
runite::run();
assert!(empty.get());