pub struct Executor<'a> { /* private fields */ }
Expand description
An async executor that can spawn tasks
§Example
Run a future that spawns tasks and captures the outside environment.
use local_runtime::{block_on, Executor};
// Run future on current thread
block_on(async {
let n = 10;
let ex = Executor::new();
let out = ex.run(async {
// Spawn an async task that captures from the outside environment
let handle = ex.spawn(async { &n });
// Wait for the task to complete
handle.await
}).await;
assert_eq!(*out, 10);
});
Implementations§
Source§impl<'a> Executor<'a>
impl<'a> Executor<'a>
Sourcepub fn with_capacity(capacity: usize) -> Self
pub fn with_capacity(capacity: usize) -> Self
Create new executor with a pre-allocated capacity
The executor will be able to hold at least capacity
concurrent tasks without reallocating
its internal storage.
Sourcepub fn spawn<T: 'a>(&self, fut: impl Future<Output = T> + 'a) -> TaskHandle<T> ⓘ
pub fn spawn<T: 'a>(&self, fut: impl Future<Output = T> + 'a) -> TaskHandle<T> ⓘ
Spawn a task on the executor, returning a TaskHandle
to it
The provided future will run concurrently on the current thread while Executor::run
runs, even if you don’t await on the TaskHandle
. If it’s not awaited, there’s no
guarantee that the task will run to completion.
To spawn additional tasks from inside of a spawned task, see Executor::spawn_rc
.
use std::net::TcpListener;
use local_runtime::{io::Async, Executor, block_on};
let ex = Executor::new();
block_on(ex.run(async {
let listener = Async::<TcpListener>::bind(([127, 0, 0, 1], 8080))?;
loop {
let mut stream = listener.accept().await?;
let task = ex.spawn(async move {
// Process each connection concurrently
});
}
Ok(())
}))
Sourcepub fn spawn_rc<T: 'a, Fut: Future<Output = T> + 'a, F>(
self: Rc<Self>,
f: F,
) -> TaskHandle<T> ⓘ
pub fn spawn_rc<T: 'a, Fut: Future<Output = T> + 'a, F>( self: Rc<Self>, f: F, ) -> TaskHandle<T> ⓘ
Spawn a task using a Rc
pointer to the executor, rather than a reference. This allows
for spawning more tasks inside spawned tasks.
When attempting “recursive” task spawning using Executor::spawn
, you will encounter
borrow checker errors about the lifetime of the executor:
use local_runtime::Executor;
let ex = Executor::new();
// -- binding `ex` declared here
ex.block_on(async {
// ----- value captured here by coroutine
let outer_task = ex.spawn(async {
// ^^ borrowed value does not live long enough
let inner_task = ex.spawn(async { 10 });
inner_task.await;
});
});
// -
// |
// `ex` dropped here while still borrowed
// borrow might be used here, when `ex` is dropped and runs the destructor for type `Executor<'_>`
This happens because the future associated with the task is stored in the executor. So if
outer_task
contains a reference to the executor, then the executor will be storing a
reference to itself, which is not allowed. To circumvent this issue, we need to put the
executor behind a Rc
pointer and clone it into every task that we want to spawn more
tasks in. This is where Executor::spawn_rc
comes in.
Rather than taking a future, spawn_rc
accepts a closure that takes a Rc
to executor
and returns a future. This allows the future to capture the executor by value rather than
by reference, getting rid of the borrow error.
§Example
use std::rc::Rc;
use local_runtime::Executor;
let ex = Rc::new(Executor::new());
ex.block_on(async {
let outer_task = ex.clone().spawn_rc(|ex| async move {
let inner_task = ex.spawn(async { 10 });
inner_task.await;
});
});
Sourcepub fn block_on<T>(&self, fut: impl Future<Output = T>) -> T
pub fn block_on<T>(&self, fut: impl Future<Output = T>) -> T
Blocking version of Executor::run
.
This is just a shorthand for calling block_on(ex.run(fut))
.
§Panic
Calling this function within a task spawned on the same executor will panic.
Sourcepub async fn run<T>(&self, fut: impl Future<Output = T>) -> T
pub async fn run<T>(&self, fut: impl Future<Output = T>) -> T
Drives the future to completion asynchronously while also driving all spawned tasks
When this function completes, it will drop all unfinished tasks that were spawned on the executor.
§Panic
Polling the future returned by this function within a task spawned on the same executor will panic.
§Example
use std::net::UdpSocket;
use std::io;
use local_runtime::{block_on, Executor, Async};
// Run future on current thread
block_on(async {
let socket = Async::<UdpSocket>::bind(([127, 0, 0, 1], 0))?;
let addr = socket.get_ref().local_addr()?;
socket.connect(addr)?;
let ex = Executor::new();
ex.run(async {
let task = ex.spawn(async {
socket.send(b"hello").await?;
socket.send(b"hello").await?;
Ok::<_, io::Error>(())
});
let mut data = [0u8; 5];
socket.recv(&mut data).await?;
socket.recv(&mut data).await?;
task.await
}).await
});