Struct Executor

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

Source

pub fn new() -> Self

Create new executor

Source

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.

Source

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

pub fn spawn_rc<T: 'a, Fut: Future<Output = T> + 'a, F>( self: Rc<Self>, f: F, ) -> TaskHandle<T>
where F: FnOnce(Rc<Self>) -> Fut + 'a,

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

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.

Source

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

Trait Implementations§

Source§

impl Default for Executor<'_>

Source§

fn default() -> Self

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

Auto Trait Implementations§

§

impl<'a> !Freeze for Executor<'a>

§

impl<'a> !RefUnwindSafe for Executor<'a>

§

impl<'a> !Send for Executor<'a>

§

impl<'a> !Sync for Executor<'a>

§

impl<'a> Unpin for Executor<'a>

§

impl<'a> !UnwindSafe for Executor<'a>

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, 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.