ato 1.0.2

A very minimal no-std async runtime
Documentation

ATO: A Simple Task Async Runtime for no_std Environments

crates.io docs.rs no_std

ATO is a minimal asynchronous task runtime designed for no_std environments, making it suitable for embedded systems, operating system kernels, or other resource-constrained applications where the standard library is unavailable.

It provides a basic task spawner and a round-robin scheduler to run Futures to completion.

Features

  • no_std Compatible: Works in environments without the standard library (requires alloc).
  • Task Spawner: Queue multiple asynchronous tasks.
  • Round-Robin Scheduling: Tasks are polled sequentially until completion.
  • Simple Sleep Functionality: Includes an async sleep function that requires a user-provided time source.
  • Fixed-Size Task Queue: Uses heapless::Deque for a statically-sized task queue, configurable at compile time.

Motivation

In many no_std contexts, a full-fledged async runtime like Tokio or async-std is too heavy or relies on operating system features that aren't available. ATO aims to provide the bare essentials for cooperative multitasking with futures in such environments.

Installation

Add ATO to your Cargo.toml:

[dependencies]
ato = "1.0.2" # Replace with the desired version

Ensure you have an allocator set up if you're in a no_std environment, as ATO uses Box for tasks.

Usage

Here's a basic example of how to use ATO:

#![no_std]
extern crate alloc;

use ato::{Spawner, sleep, Error};
use core::time::Duration;

// --- You need to provide a time source function ---
// This function must return the current monotonic time as a Duration.
// The exact implementation will depend on your hardware/platform.
// For example, it might read a hardware timer.
fn get_platform_time() -> Duration {
    // Replace this with your actual time-keeping logic
    // For demonstration, let's assume a dummy incrementing counter in nanoseconds.
    // In a real scenario, this would interface with a hardware timer.
    static mut FAKE_TIME: u64 = 0;
    unsafe {
        FAKE_TIME += 10_000_000; // Increment by 10ms for example
        Duration::from_nanos(FAKE_TIME)
    }
}

fn main() -> Result<(), Error> {
    // Creates a spawner.
    // NOTE: The size must be power of two, e.g., 2, 4, 8, 16, etc.
    static spawner: Spawner<8> = Spawner::new();

    // Spawn a task that sleeps for 1 second
    spawner.spawn(async {
        // The first argument is the duration to sleep.
        // The second argument is a function pointer to your time source.
        sleep(Duration::from_secs(1), get_platform_time).await;
        // In a real application, you might print to a console or toggle an LED.
        // For no_std, printing requires a platform-specific implementation.
        // println!("Task 1: Slept for 1 second!");
    })?;

    // Spawn another task
    spawner.spawn(async {
        sleep(Duration::from_millis(500), get_platform_time).await;
        // println!("Task 2: Slept for 500 milliseconds!");

        // We can create another task inside this one
        spawner.spawn(async {
            sleep(Duration::from_secs(2), get_platform_time).await;
            // println!("Task 3: Slept for 2 seconds!");
        }).unwrap();
    })?;

    // Run all tasks until they are done
    // This will block and poll tasks in a round-robin fashion.
    spawner.run_until_all_done()?;

    // println!("All tasks completed!");
    Ok(())
}