Struct TaskExecutor

Source
pub struct TaskExecutor { /* private fields */ }
Expand description

A TaskExecutor provides a simple interface for spawning tasks onto a Tokio Runtime.

It holds a static reference to a Runtime, which acts as the task executor. It also manages background workers for different types of batchable tasks, creating them on demand. This design encourages treating the runtime as a shared, long-lived resource.

For creation, see TaskExecutor::new.

Implementations§

Source§

impl TaskExecutor

Source

pub fn new(rt: &'static Runtime) -> Self

Creates a new TaskExecutor instance bound to a statically-lived Tokio runtime.

An application will typically create a single Runtime instance that lives for the duration of the program. To pass a reference of this runtime to the TaskExecutor, it must have a 'static lifetime. This ensures the TaskExecutor can never outlive the runtime it depends on.

§Arguments
  • rt: A static reference to a tokio::runtime::Runtime.
§Example
use mini_executor::TaskExecutor;
use tokio::runtime::Runtime;
use std::sync::OnceLock;

// Use OnceLock to create a static runtime reference.
static RT: OnceLock<Runtime> = OnceLock::new();

fn main() {
    let rt = RT.get_or_init(|| Runtime::new().unwrap());
    let executor = TaskExecutor::new(rt);
    // The task executor is now ready to execute tasks.
}
Source

pub async fn execute_waiting<T: Task>( &self, task: T, ) -> Result<T::Output, JoinError>

Spawns an individual task and asynchronously waits for its result.

This method submits the task to the executor’s runtime and suspends the current async context until the task has finished.

§Arguments
  • task: An instance of a type that implements the Task trait.
§Returns

A Result containing either:

  • Ok(T::Output): The successful output of the task.
  • Err(JoinError): An error indicating that the task panicked during execution.
§Example
struct MyTask;
impl Task for MyTask {
    type Output = u32;
    async fn run(self) -> Self::Output { 42 }
}

let rt = RT.get_or_init(|| Runtime::new().unwrap());
let executor = TaskExecutor::new(rt);

rt.block_on(async {
    let result = executor.execute_waiting(MyTask).await?;
    assert_eq!(result, 42);
    Ok::<(), Box<dyn std::error::Error>>(())
})?;
Source

pub fn execute_detached<T: Task>(&self, task: T) -> JoinHandle<T::Output>

Spawns an individual task and immediately returns a JoinHandle without awaiting it.

This is useful for “fire-and-forget” style execution, where the task runs in the background. The returned JoinHandle can still be used to await the task’s completion at a later point if its result is needed.

§Arguments
  • task: An instance of a type that implements the Task trait.
§Returns

A JoinHandle representing the spawned task. Awaiting the handle will yield a Result<T::Output, JoinError>.

§Example
struct BackgroundTask;
impl Task for BackgroundTask {
    type Output = String;
    async fn run(self) -> Self::Output { "done".to_string() }
}
let rt = RT.get_or_init(|| Runtime::new().unwrap());
let executor = TaskExecutor::new(rt);
rt.block_on(async {
    // Spawn the task but don't wait for it yet.
    let handle = executor.execute_detached(BackgroundTask);

    // We can do other work here...
    println!("Task is running in the background.");

    // Later, await the handle to get the result.
    let result = handle.await?;
    assert_eq!(result, "done");
    Ok::<(), Box<dyn std::error::Error>>(())
})?;
Source

pub fn execute_batch_detached<BT: BatchTask + Clone>(&self, batch_task: BT)

Executes a batch task asynchronously without waiting for its completion.

This method submits a task to its corresponding batch processor. If a processor for this task type does not exist, one is spawned. The task will be collected with other pending tasks of the same type and executed in a single batch.

This is a “fire-and-forget” operation.

§Arguments
  • batch_task: An instance of a type that implements BatchTask.
§Example
#[derive(Clone)]
struct LogMessage(String);
impl BatchTask for LogMessage {
    async fn batch_run(list: Vec<Self>) {
        // In a real app, this might write to a file or database.
        println!("-- Logging batch of {} messages --", list.len());
        for msg in list {
            println!("{}", msg.0);
        }
    }
}
let rt = RT.get_or_init(|| Runtime::new().unwrap());
let executor = TaskExecutor::new(rt);

rt.block_on(async {
    // Fire-and-forget these logging tasks.
    executor.execute_batch_detached(LogMessage("User logged in".to_string()));
    executor.execute_batch_detached(LogMessage("Data processed".to_string()));

    // Give the batch processor a moment to run.
    sleep(Duration::from_millis(50)).await;
});
Source

pub async fn execute_batch_waiting<BT: BatchTask + Clone>( &self, batch_task: BT, ) -> Result<(), RecvError>

Executes a batch task and waits for its containing batch to complete.

This method submits a task to its batch processor and waits for a completion signal. The signal is sent after the entire batch (which includes this task) has finished processing.

§Arguments
  • batch_task: An instance of a type that implements BatchTask.
§Returns

A Result which is Ok(()) on successful completion of the batch. It returns Err(oneshot::error::RecvError) if the batch processor panics or is terminated before sending a completion signal.

§Example
#[derive(Clone)]
struct DatabaseInsert(u32);
impl BatchTask for DatabaseInsert {
    async fn batch_run(list: Vec<Self>) {
        let ids: Vec<u32> = list.into_iter().map(|item| item.0).collect();
        println!("BATCH INSERT: Simulating inserting IDs: {:?}", ids);
        // In a real app, you'd perform the batched database query here.
    }
}
let rt = RT.get_or_init(|| Runtime::new().unwrap());
let executor = TaskExecutor::new(rt);

rt.block_on(async {
    // Detach a few tasks first.
    executor.execute_batch_detached(DatabaseInsert(1));
    executor.execute_batch_detached(DatabaseInsert(2));
     
    // Now, execute a task and wait for its batch to complete.
    // This task will be batched with the two above.
    let result = executor.execute_batch_waiting(DatabaseInsert(3)).await;
     
    assert!(result.is_ok());
    println!("Batch confirmed as complete.");
});

Auto Trait Implementations§

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.