#async #actor #async-await #runtime #distributed #framework #coerce

bin+lib coerce

Async actor runtime and distributed systems framework

8 releases

0.3.1-prerelease7 Jan 15, 2021
0.3.1-prerelease6 Jan 10, 2021
0.3.0-prerelease Dec 12, 2020
0.0.0 Dec 16, 2019

#197 in Asynchronous

Download history 1/week @ 2020-11-24 1/week @ 2020-12-01 16/week @ 2020-12-08 2/week @ 2020-12-15 119/week @ 2021-01-05 68/week @ 2021-01-12 8/week @ 2021-01-19 8/week @ 2021-01-26 2/week @ 2021-02-02 17/week @ 2021-02-09 51/week @ 2021-02-16

53 downloads per month

Apache-2.0

245KB
6.5K SLoC

Coerce-rs crates.io coerce-rs

Coerce-rs is an asynchronous (async/await) Actor runtime for Rust. It allows for extremely simple yet powerful actor-based multithreaded application development.

async/await Actors

An actor is just another word for a unit of computation. It can have mutable state, it can receive messages and perform actions. One caveat though.. It can only do one thing at a time. This can be useful because it can alleviate the need for thread synchronisation, usually achieved by locking (using Mutex, RwLock etc).

How is this achieved in Coerce?

Coerce uses Tokio's MPSC channels (tokio::sync::mpsc::channel), every actor created spawns a task listening to messages from a Receiver, handling and awaiting the result of the message. Every reference (ActorRef<A: Actor>) holds a Sender<M> where A: Handler<M>, which can be cloned.

Actors can be stopped and actor references can be retrieved by ID from anywhere in your application. IDs are String but if an ID isn't provided upon creation, a new Uuid will be generated. Anonymous actors are automatically dropped (and Stopped) when all references are dropped. Tracked actors (using global fn new_actor) must be stopped.

Example

pub struct EchoActor {}

#[async_trait]
impl Actor for EchoActor {}

pub struct EchoMessage(String);

impl Message for EchoMessage {
    type Result = String;
}

#[async_trait]
impl Handler<EchoMessage> for EchoActor {
    async fn handle(
        &mut self,
        message: EchoMessage,
        _ctx: &mut ActorContext,
    ) -> String {
        message.0.clone()
    }
}

pub async fn run() {
    let mut actor = new_actor(EchoActor {}).await.unwrap();

    let hello_world = "hello, world".to_string();
    let result = actor.send(EchoMessage(hello_world.clone())).await;
    
    assert_eq!(result, Ok(hello_world));
}

Timer Example

pub struct EchoActor {}

#[async_trait]
impl Actor for EchoActor {}

pub struct EchoMessage(String);

impl Message for EchoMessage {
    type Result = String;
}

pub struct PrintTimer(String);

impl TimerTick for PrintTimer {}

#[async_trait]
impl Handler<PrintTimer> for EchoActor {
    async fn handle(&mut self, msg: PrintTimer, _ctx: &mut ActorContext) {
        println!("{}", msg.0);
    }
}

pub async fn run() {
    let mut actor = new_actor(EchoActor {}).await.unwrap();
    let hello_world = "hello world!".to_string();

    // print "hello world!" every 5 seconds
    let timer = Timer::start(actor.clone(), Duration::from_secs(5), TimerTick(hello_world));
    
    // timer is stopped when handle is out of scope or can be stopped manually by calling `.stop()`
    sleep(Duration::from_secs(20)).await;
    timer.stop();
}

Dependencies

~8MB
~153K SLoC