-
Notifications
You must be signed in to change notification settings - Fork 36
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Deadlock when sending circular message #13
Comments
Can you send the code that reproduces the problem and one with For me, the following example prints an endless stream of use xtra::prelude::*;
struct MyActor;
impl Actor for MyActor {}
#[derive(Debug)]
enum CircularMessage {
A,
B,
}
impl CircularMessage {
fn swap(self) -> CircularMessage {
match self {
CircularMessage::A => CircularMessage::B,
CircularMessage::B => CircularMessage::A,
}
}
}
impl Message for CircularMessage {
type Result = ();
}
impl SyncHandler<CircularMessage> for MyActor {
fn handle(&mut self, m: CircularMessage, ctx: &mut Context<Self>) {
println!("Got {:?}", m);
ctx.notify_later(m.swap())
}
}
#[tokio::main]
async fn main() {
let addr = MyActor.spawn();
addr.do_send(CircularMessage::A).unwrap();
loop {} // prevent exit
} For interest's sake, it does not seem to leak memory, either. The program's memory stays pinned at around ~160kb. |
Sure, that's what I was trying to do: use async_trait::async_trait;
use xtra::prelude::*;
struct Initialized(Address<ActorA>);
impl Message for Initialized {
type Result = ();
}
struct Hello;
impl Message for Hello {
type Result = ();
}
struct ActorA {
actor_b: Address<ActorB>,
}
impl Actor for ActorA {}
#[async_trait]
impl Handler<Hello> for ActorA {
async fn handle(&mut self, _: Hello, _: &mut Context<Self>) {
println!("ActorA: Hello");
self.actor_b.send(Hello).await.unwrap();
}
}
struct ActorB;
impl Actor for ActorB {}
#[async_trait]
impl Handler<Initialized> for ActorB {
async fn handle(&mut self, m: Initialized, _: &mut Context<Self>) {
println!("ActorB: Initialized");
let actor_a = m.0;
actor_a.send(Hello).await.unwrap();
}
}
#[async_trait]
impl Handler<Hello> for ActorB {
async fn handle(&mut self, _: Hello, _: &mut Context<Self>) {
println!("ActorB: Hello");
}
}
#[tokio::main]
async fn main() {
let actor_b = ActorB {}.spawn();
let actor_a = ActorA {
actor_b: actor_b.clone(),
}
.spawn();
actor_b.send(Initialized(actor_a.clone())).await.unwrap();
}
|
I think the issue here might be that Once this change is applied, I get the following output: ActorB: Initialized
ActorA: Hello
ActorB: Hello and then the program exits successfully. The recommendation for Does this solve your actual usecase too? |
Thanks for the explanation, that makes sense.
Unfortunately it does not - I do need to use |
Off the top of my head, might it be possible to have an |
The |
Could you do this: ActorA sends information needed for the request to ActorB. It doesn't wait for a response. Or similar? |
In my usecase |
Would this work: ActorB sends "Hello" to ActorA. ActorB waits for response. If not, could we discuss on the rust discord to reduce back-and-forthing, perhaps? |
There could certainly be a different modelling that works, but what leaves me worried is that it might lock at runtime without compile-time warning, which could get problematic especially when the code base grows, since it doesn't get easier to have the full picture of which ways messages could flow. And sure, feel free to DM me on discord. |
I've just opened #15, which allows interleaving messages using the strategy I explained to you in Discord DMs. Applying this to your example prints the following:
Could you test it out with your real use case? |
Looks like an elegant solution, and works. Thanks! |
Released in 0.4.0 |
When sending async messages like
A -[send]-> B -[send]-> A
, xtra will be stuck in a deadlock. After researching a bit it seems that this is expected behavior. There wasnotify
mentioned to mitigate that, but that doesn't seem to work in my case. If it's possible a pointer/example would be helpful.It'd also nice if it'd be somehow possible to print a warning or something when this happens, because keeping track of who sends messages to whom in an actor system can get quite hard. Going by that reddit thread I can see that it's not an easy problem to solve.
Kudos again for xtra as it is!
The text was updated successfully, but these errors were encountered: