Struct async_task::Builder
source · pub struct Builder<M> { /* private fields */ }
Expand description
A builder that creates a new task.
Implementations§
source§impl Builder<()>
impl Builder<()>
sourcepub fn new() -> Builder<()>
pub fn new() -> Builder<()>
Creates a new task builder.
By default, this task builder has no metadata. Use the [metadata
] method to
set the metadata.
Examples
use async_task::Builder;
let (runnable, task) = Builder::new().spawn(|()| async {}, |_| {});
sourcepub fn metadata<M>(self, metadata: M) -> Builder<M>
pub fn metadata<M>(self, metadata: M) -> Builder<M>
Adds metadata to the task.
In certain cases, it may be useful to associate some metadata with a task. For instance,
you may want to associate a name with a task, or a priority for a priority queue. This
method allows the user to attach arbitrary metadata to a task that is available through
the Runnable
or the Task
.
Examples
This example creates an executor that associates a “priority” number with each task, and then runs the tasks in order of priority.
use async_task::{Builder, Runnable};
use once_cell::sync::Lazy;
use std::cmp;
use std::collections::BinaryHeap;
use std::sync::Mutex;
/// A wrapper around a `Runnable<usize>` that implements `Ord` so that it can be used in a
/// priority queue.
struct TaskWrapper(Runnable<usize>);
impl PartialEq for TaskWrapper {
fn eq(&self, other: &Self) -> bool {
self.0.metadata() == other.0.metadata()
}
}
impl Eq for TaskWrapper {}
impl PartialOrd for TaskWrapper {
fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
Some(self.cmp(other))
}
}
impl Ord for TaskWrapper {
fn cmp(&self, other: &Self) -> cmp::Ordering {
self.0.metadata().cmp(other.0.metadata())
}
}
static EXECUTOR: Lazy<Mutex<BinaryHeap<TaskWrapper>>> = Lazy::new(|| {
Mutex::new(BinaryHeap::new())
});
let schedule = |runnable| {
EXECUTOR.lock().unwrap().push(TaskWrapper(runnable));
};
// Spawn a few tasks with different priorities.
let spawn_task = move |priority| {
let (runnable, task) = Builder::new().metadata(priority).spawn(
move |_| async move { priority },
schedule,
);
runnable.schedule();
task
};
let t1 = spawn_task(1);
let t2 = spawn_task(2);
let t3 = spawn_task(3);
// Run the tasks in order of priority.
let mut metadata_seen = vec![];
while let Some(TaskWrapper(runnable)) = EXECUTOR.lock().unwrap().pop() {
metadata_seen.push(*runnable.metadata());
runnable.run();
}
assert_eq!(metadata_seen, vec![3, 2, 1]);
assert_eq!(t1.await, 1);
assert_eq!(t2.await, 2);
assert_eq!(t3.await, 3);
source§impl<M> Builder<M>
impl<M> Builder<M>
sourcepub fn propagate_panic(self, propagate_panic: bool) -> Builder<M>
pub fn propagate_panic(self, propagate_panic: bool) -> Builder<M>
Propagates panics that occur in the task.
When this is true
, panics that occur in the task will be propagated to the caller of
the Task
. When this is false, no special action is taken when a panic occurs in the
task, meaning that the caller of Runnable::run
will observe a panic.
This is only available when the std
feature is enabled. By default, this is false
.
Examples
use async_task::Builder;
use futures_lite::future::poll_fn;
use std::future::Future;
use std::panic;
use std::pin::Pin;
use std::task::{Context, Poll};
fn did_panic<F: FnOnce()>(f: F) -> bool {
panic::catch_unwind(panic::AssertUnwindSafe(f)).is_err()
}
let (runnable1, mut task1) = Builder::new()
.propagate_panic(true)
.spawn(|()| async move { panic!() }, |_| {});
let (runnable2, mut task2) = Builder::new()
.propagate_panic(false)
.spawn(|()| async move { panic!() }, |_| {});
assert!(!did_panic(|| { runnable1.run(); }));
assert!(did_panic(|| { runnable2.run(); }));
let waker = poll_fn(|cx| Poll::Ready(cx.waker().clone())).await;
let mut cx = Context::from_waker(&waker);
assert!(did_panic(|| { let _ = Pin::new(&mut task1).poll(&mut cx); }));
assert!(did_panic(|| { let _ = Pin::new(&mut task2).poll(&mut cx); }));
sourcepub fn spawn<F, Fut, S>(
self,
future: F,
schedule: S
) -> (Runnable<M>, Task<Fut::Output, M>)
pub fn spawn<F, Fut, S>( self, future: F, schedule: S ) -> (Runnable<M>, Task<Fut::Output, M>)
Creates a new task.
The returned Runnable
is used to poll the future
, and the Task
is used to await its
output.
Method run()
polls the task’s future once. Then, the Runnable
vanishes and only reappears when its Waker
wakes the task, thus scheduling it to be run
again.
When the task is woken, its Runnable
is passed to the schedule
function.
The schedule
function should not attempt to run the Runnable
nor to drop it. Instead, it
should push it into a task queue so that it can be processed later.
If you need to spawn a future that does not implement Send
or isn’t 'static
, consider
using spawn_local()
or spawn_unchecked()
instead.
Examples
use async_task::Builder;
// The future inside the task.
let future = async {
println!("Hello, world!");
};
// A function that schedules the task when it gets woken up.
let (s, r) = flume::unbounded();
let schedule = move |runnable| s.send(runnable).unwrap();
// Create a task with the future and the schedule function.
let (runnable, task) = Builder::new().spawn(|()| future, schedule);
sourcepub fn spawn_local<F, Fut, S>(
self,
future: F,
schedule: S
) -> (Runnable<M>, Task<Fut::Output, M>)
pub fn spawn_local<F, Fut, S>( self, future: F, schedule: S ) -> (Runnable<M>, Task<Fut::Output, M>)
Creates a new thread-local task.
This function is same as spawn()
, except it does not require Send
on future
. If the
Runnable
is used or dropped on another thread, a panic will occur.
This function is only available when the std
feature for this crate is enabled.
Examples
use async_task::{Builder, Runnable};
use flume::{Receiver, Sender};
use std::rc::Rc;
thread_local! {
// A queue that holds scheduled tasks.
static QUEUE: (Sender<Runnable>, Receiver<Runnable>) = flume::unbounded();
}
// Make a non-Send future.
let msg: Rc<str> = "Hello, world!".into();
let future = async move {
println!("{}", msg);
};
// A function that schedules the task when it gets woken up.
let s = QUEUE.with(|(s, _)| s.clone());
let schedule = move |runnable| s.send(runnable).unwrap();
// Create a task with the future and the schedule function.
let (runnable, task) = Builder::new().spawn_local(move |()| future, schedule);
sourcepub unsafe fn spawn_unchecked<'a, F, Fut, S>(
self,
future: F,
schedule: S
) -> (Runnable<M>, Task<Fut::Output, M>)
pub unsafe fn spawn_unchecked<'a, F, Fut, S>( self, future: F, schedule: S ) -> (Runnable<M>, Task<Fut::Output, M>)
Creates a new task without Send
, Sync
, and 'static
bounds.
This function is same as spawn()
, except it does not require Send
, Sync
, and
'static
on future
and schedule
.
Safety
- If
Fut
is notSend
, itsRunnable
must be used and dropped on the original thread. - If
Fut
is not'static
, borrowed non-metadata variables must outlive itsRunnable
. - If
schedule
is notSend
andSync
, all instances of theRunnable
’sWaker
must be used and dropped on the original thread. - If
schedule
is not'static
, borrowed variables must outlive all instances of theRunnable
’sWaker
.
Examples
use async_task::Builder;
// The future inside the task.
let future = async {
println!("Hello, world!");
};
// If the task gets woken up, it will be sent into this channel.
let (s, r) = flume::unbounded();
let schedule = move |runnable| s.send(runnable).unwrap();
// Create a task with the future and the schedule function.
let (runnable, task) = unsafe { Builder::new().spawn_unchecked(move |()| future, schedule) };