Diggsey / act-zero

Apache License 2.0
122 stars 7 forks source link

Kill or stop Actor #2

Closed praveenperera closed 3 years ago

praveenperera commented 3 years ago

Is there currently a way to kill or stop the actor?

Here is what I tried so far:

struct HelloWorldActor;

impl Actor for HelloWorldActor {}

impl HelloWorldActor {
    async fn say_hello(&mut self) {
        loop {
            println!("Hello, world!");
        }
    }
}

#[tokio::main]
async fn main() -> Result<()> {
    color_eyre::install()?;
    env_logger::init();

    let addr = spawn_actor(HelloWorldActor);
    send!(addr.say_hello());

    drop(addr);

    Ok(())
}
Diggsey commented 3 years ago

Yes, there are several ways to do this: 1) If any actor method returns an Err(_), the default behaviour is to stop the actor. You can override this by implementing the error method on the Actor trait.

2) If there are no more strong references to the actor, then the actor will stop.

In your example, the issue is that the say_hello method never returns, so there is no opportunity for the actor to stop. Instead of using an infinite loop, you could have the say_hello method call itself:

    async fn say_hello(&mut self) {
        println!("Hello, world!");
        send!(self.addr.say_hello());
    }

This will require you to add an addr: WeakAddr<Self> field to the actor, and implement the started() method of the actor trait to receive that address.

praveenperera commented 3 years ago

I wasn't able to do that: I get

note: ...which requires type-checking `<impl at cluster-manager/src/main.rs:25:1: 30:2>::say_hello`...
  --> cluster-manager/src/main.rs:26:5
   |
26 |     async fn say_hello(&mut self) {
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   = note: ...which again requires computing type of `<impl at cluster-manager/src/main.rs:25:1: 30:2>::say_hello::{{opaque}}#0`, completing the cycle
note: cycle used when checking item types in top-level module
  --> cluster-manager/src/main.rs:1:1
#[derive(Default)]
struct HelloWorldActor {
    addr: WeakAddr<Self>,
}

#[async_trait]
impl Actor for HelloWorldActor {
    async fn started(&mut self, addr: Addr<Self>) -> ActorResult<()> {
        self.addr = addr.downgrade();
        Produces::ok(())
    }
}

impl HelloWorldActor {
    async fn say_hello(&mut self) {
        println!("Hello, world!");
    }
}

#[tokio::main]
async fn main() -> Result<()> {
    color_eyre::install()?;
    env_logger::init();

    let addr = spawn_actor(HelloWorldActor::default());
    send!(addr.say_hello());

    println!("Continue");

    drop(addr);

    Ok(())
}

But that's okay because the infinite loop was just to try it out what I care about is:

If there are no more strong references to the actor, then the actor will stop.

Thanks for this great library!

Diggsey commented 3 years ago

Ah yes, it looks like you ran into a limitation of the compiler 😦 - it's relatively easy to workaround if you did want to do this kind of thing:

//! This example shows how you can use the Tokio runtime with act-zero.

use act_zero::runtimes::tokio::spawn_actor;
use act_zero::*;
use async_trait::async_trait;

#[derive(Default)]
struct HelloWorldActor {
    addr: WeakAddr<Self>,
}

#[async_trait]
impl Actor for HelloWorldActor {
    async fn started(&mut self, addr: Addr<Self>) -> ActorResult<()> {
        self.addr = addr.downgrade();
        Produces::ok(())
    }
}

impl HelloWorldActor {
    fn say_hello_again(&mut self) {
        send!(self.addr.say_hello());
    }
    async fn say_hello(&mut self) {
        println!("Hello, world!");
        self.say_hello_again();
    }
}

#[tokio::main]
async fn main() -> Result<(), ActorError> {
    let addr = spawn_actor(HelloWorldActor::default());
    call!(addr.say_hello()).await?;
    Ok(())
}