Skip to main content

JoinSet

Struct JoinSet 

Source
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>

Source

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());
Source

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);
Source

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"));
Source

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());
Source

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());
Source

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);
Source

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());

Trait Implementations§

Source§

impl<T> Default for JoinSet<T>

Source§

fn default() -> Self

Returns the “default value” for a type. Read more
Source§

impl<T> Drop for JoinSet<T>

Source§

fn drop(&mut self)

Executes the destructor for this type. Read more
Source§

fn pin_drop(self: Pin<&mut Self>)

🔬This is a nightly-only experimental API. (pin_ergonomics)
Execute the destructor for this type, but different to Drop::drop, it requires self to be pinned. Read more

Auto Trait Implementations§

§

impl<T> !RefUnwindSafe for JoinSet<T>

§

impl<T> !Send for JoinSet<T>

§

impl<T> !Sync for JoinSet<T>

§

impl<T> !UnwindSafe for JoinSet<T>

§

impl<T> Freeze for JoinSet<T>

§

impl<T> Unpin for JoinSet<T>

§

impl<T> UnsafeUnpin for JoinSet<T>

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more