Unable to use asynchronous actors

585 views Asked by At

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

There are 2 answers

1
b.moyet On

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();

    });
}
0
X... On

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.