Unable to use asynchronous actors

Viewed 216

I'm trying to use the actors as documented in the actix documentation. But even the doc example is not working for me. I tried the following code which compiles but does not print the message "Received fibo message"

use actix::prelude::*;

// #[derive(Message)]
// #[rtype(Result = "Result<u64, ()>")]
// struct Fibonacci(pub u32);

struct Fibonacci(pub u32);
impl Message for Fibonacci {
    type Result = Result<u64, ()>;
}

struct SyncActor;

impl Actor for SyncActor {
    // It's important to note that you use "SyncContext" here instead of "Context".
    type Context = SyncContext<Self>;
}

impl Handler<Fibonacci> for SyncActor {
    type Result = Result<u64, ()>;

    fn handle(&mut self, msg: Fibonacci, _: &mut Self::Context) -> Self::Result {
        println!("Received fibo message");
        if msg.0 == 0 {
            Err(())
        } else if msg.0 == 1 {
            Ok(1)
        } else {
            let mut i = 0;
            let mut sum = 0;
            let mut last = 0;
            let mut curr = 1;
            while i < msg.0 - 1 {
                sum = last + curr;
                last = curr;
                curr = sum;
                i += 1;
            }
            Ok(sum)
        }
    }
}

fn main() {
    System::new().block_on(async {
        // Start the SyncArbiter with 2 threads, and receive the address of the Actor pool.
        let addr = SyncArbiter::start(2, || SyncActor);

        // send 5 messages
        for n in 5..10 {
            // As there are 2 threads, there are at least 2 messages always being processed
            // concurrently by the SyncActor.
            println!("Sending fibo message");
            addr.do_send(Fibonacci(n));
        }
    });
}

This program displays 5 times :

Sending fibo message

Two remarks, first I'm unable to use the macro rtype, I use to implement Message myself. And then the line addr.do_send(Fibonacci(n)) seems to not send anything to my actor. However if I use addr.send(Fibonacci(n)).await; my message get sent and received on the actor side. But since I'm awaiting the send function it processes the message synchronously instead of using the 2 threads I have defined theoretically.

I also tried to wait with a thread::sleep after my main loop but the messages were not arriving either.

I might be misunderstanding something but it seems strange to me.

Cargo.toml file :

[dependencies]
actix = "0.11.1"
actix-rt = "2.2.0"
2 Answers

I finally managed to make it works, though I can't understand exactly why. Simply using tokio to wait for a ctrl-C made it possible for me to call do_send/try_send and work in parallel.

fn main() {
    System::new().block_on(async {
        // Start the SyncArbiter with 4 threads, and receive the address of the Actor pool.
        let addr = SyncArbiter::start(4, || SyncActor);

        // send 15 messages
        for n in 5..20 {
            // As there are 4 threads, there are at least 4 messages always being processed
            // concurrently by the SyncActor.
            println!("Sending fibo message");
            addr.do_send(Fibonacci(n));
        }


        // This does not wotk
        //thread::spawn(move || {
        //    thread::sleep(Duration::from_secs_f32(10f32));
        //}).join();

        // This made it worked
        tokio::signal::ctrl_c().await.unwrap();
        println!("Ctrl-C received, shutting down");
        System::current().stop();

    });
}

You don't have to use crate tokio explicitly here. In your loop, just change the last line to addr.send(Fibonacci(n)).await.unwrap(). Method send returns a future and it must be awaited to resolve.

Related