Actor doesn't fully stops when `stopping` returns `Running::continue` at least once
I have been experimenting the current actix's Async Context implementation, Context, and I've come across a characteristic that I don't know if its a bug or the expected behavior. Check the following code:
// [dependencies]
// actix = "0.9"
// actix-rt = "1"
// tokio = { version = "0.2", features = ["rt-core"] }
use actix::prelude::*;
use std::sync::{Arc, Mutex};
struct Foo(Arc<Mutex<usize>>);
impl Actor for Foo {
type Context = Context<Self>;
fn stopping(&mut self, _: &mut Context<Self>) -> Running {
let mut data = self.0.lock().unwrap();
*data = 6;
Running::Stop
}
}
#[actix_rt::main]
async fn main() {
let data = Arc::new(Mutex::new(0));
let act = Foo(data.clone());
act.start();
loop {
if dbg!(*data.lock().unwrap()) > 5 {
break;
}
// Allows the Actor's future to be processed
tokio::task::yield_now().await;
}
}This exemple naturally stops, since the Actor will have no live Address holders and no futures to process, when stopping the stopping method is called setting the shared data which unlocks the main function.
Now if we change the stopping method to the following:
fn stopping(&mut self, _: &mut Context<Self>) -> Running {
let mut data = self.0.lock().unwrap();
if *data < 6 {
*data += 1;
return Running::Continue;
}
Running::Stop
}I would expect that the stopping method wold be called multiple times, and that eventually the Actor would fully stop. What happens in fact is that the Actor's future will be pending when it first returns Running::Continue, but then it won't ever be waken. Running this on gdb makes it easy to see that the Future stops being polled.
I have a simple solution for it which wold be adding a cx.waker().wake_by_ref(); to the Future when it becomes Pending due to Running::Continue returned by the stopping method. That way the Future wold immediately be available to be polled by the runtime again and this example would finish.
What do you guys think? Should the Actor future be waking itself in case of stopping returning a Running::Continue, or should it only be waken by external sources, e.g, new message being received.
Source: actix/actix