Skip to main content

Crate runtime

Crate runtime 

Expand description

The Veecle OS runtime.

This crate contains the main building blocks for any Veecle OS application. Veecle OS applications are composed of Actors that communicate with each other through slots. Slots come in different implementations; see single_writer for a slot implementation with one writer and multiple readers, and mpsc for multiple writers with a single reader.

This crate is meant to be used with asynchronous programming, which means that actors are expected to be async functions. For example, it will be ensured that an actor does not update a value till all its readers had the chance to read its latest state.

§Example

The following Veecle OS application consists of two actors, PingActor and PongActor, that communicate with each other.

use std::fmt::Debug;

use veecle_os_runtime::single_writer::{Reader, Writer};
use veecle_os_runtime::{Never, Storable};

#[derive(Debug, Clone, PartialEq, Eq, Default, Storable)]
pub struct Ping {
    value: u32,
}

#[derive(Debug, Clone, PartialEq, Eq, Default, Storable)]
pub struct Pong {
    value: u32,
}

#[veecle_os_runtime::actor]
async fn ping_actor(mut ping: Writer<'_, Ping>, mut pong: Reader<'_, Pong>) -> Never {
    let mut value = 0;
    ping.write(Ping { value }).await;

    loop {
        ping.write(Ping { value }).await;
        value += 1;

        pong.read_updated(|pong| {
            println!("Pong: {}", pong.value);
        }).await;
    }
}

#[veecle_os_runtime::actor]
async fn pong_actor(mut pong: Writer<'_, Pong>, mut ping: Reader<'_, Ping>) -> Never {
    loop {
        let ping = ping.read_updated_cloned().await;
        println!("Ping: {}", ping.value);

        let data = Pong { value: ping.value };
        pong.write(data).await;
    }
}

futures::executor::block_on(
    veecle_os_runtime::execute! {
        actors: [
            PingActor,
            PongActor,
        ]
    }
)

§Output

The expected output for this example would be a sequence of ping/pong messages like the following:

Ping: 1
Pong: 1
Ping: 2
Pong: 2
Ping: 3
Pong: 3
...

§Execution

See how the PongActor waits for Ping to be written by the PingActor. If that were not the behavior, we would see Ping: 0 as the very first output, since Ping defaults to zero. The same would happen with the PingActor: if it were not waiting for Pong updates, its immediate action after writing Ping would be to display Pong: 0. Waiting for updates ensures us that only written values are read.

On the other hand, writing always yields for other woken futures to be executed before performing the write operation. The only exception is the very first write, since there is no latest value for readers to read.

Modules§

memory_pool
An interrupt/thread-safe memory pool.
mpsc
MPSC (multiple-producer, single-consumer) slot implementation.
single_writer
Single-writer slot implementation.

Macros§

execute
Execute a given set of actors without heap allocation.

Structs§

Modify
A wrapper around &mut Option<T> that allows inspecting or modifying the value.

Enums§

Never
A type that can never be constructed.

Traits§

Actor
Actor interface.
CombinableReader
A marker trait for types that can be used with CombineReaders, see that for more details.
CombineReaders
Allows combining (nearly) arbitrary amounts of Readers or ExclusiveReaders.
Storable
Marks a type as an identifier for the inner DataType, which can be transferred via a slot.
StoreRequest
Allows requesting a (nearly) arbitrary amount of readers and writers in an Actor.

Attribute Macros§

actor
Generates an Actor from a function.

Derive Macros§

Storable
Implements Storable for a struct or enum with DataType = Self.