#will my receiver.recv().await ever return None?
50 messages · Page 1 of 1 (latest)
What I typically do to not risk losing messages is to make ctrl_c drop all senders. When all senders are dropped, and the channel is empty, recv() will return none
Additionally, I would make sure to await the join handle for that message processor task
Are they long lived or short lived tasks?
nope, just a re-export
you can await task completion in that case
Yeah, basically, you get a None if all senders have been destroyed. So make sure that your cleanup logic results in all senders being destroyed.
so on ctrl_c() senders will be dropped, and it will await completion of receiver part
I mean, you can also rely on the runtime just killing all tasks on exit.
But most of the time, it's cleaner to tell such tasks to exit, causing them to drop senders so that everything else can exit.
you may have deatached tasks, but if you need to be sure - then await of completion
I wouldn't use ctrl_c in more than one place.
Instead, have that one place signal everything else via cancellation tokens.
or broadcast to all tasks
which one?
it's result of await of task
The syntax is:
var_name = my_future => { /* logic to run when my_future finishes */ }
Here, var_name is the return value of my_future.
It's only going to run one of the blocks. Specifically, the block whose future exits first.
if it's not biased;
Sure, that's a reasonable replacement.
That way, you're making it clear that you're not ignoring a real value.
The left-hand-side can be any pattern.
You can also write this:
Some(output) = chan.recv() => {
// This code runs when `chan.recv()` returns something that matches the pattern Some(output).
// That is, this does not run if `chan.recv()` returns `None`. In that case, the select! just continues waiting for the other futures.
}
You don't have to use it.
I mean, for one, the futures crate has a function called select. It's less powerful than the macro, but it isn't a macro.
tokio::spawn(async move {
token.run_until_cancelled(my_real_code()).await;
});
or even
while let Some(Some(msg)) = token.run_until_cancelled(chan.recv()).await {
// handle msg
}
correct
Yeah, that's the same as select
There's also this crate: https://docs.rs/futures-concurrency
I believe it's trying to provide superior alternatives to select!
I think it lets you do (future1, future2).race(). This requires both futures to have the same return type.
I mean, dealing nicely with different types is why the select! macro is nice.
Each future gets its own variable name.
The others basically require you to use an Either enum or something like that.
If all you need it for is waiting for a token or timer, then run_until_cancelled can also work, though.
E.g. with tokio::time::Interval, you can do this:
while token.run_until_cancelled(interval.tick()).await.is_some() {
// handle timer tick
}
// token got cancelled
versus with select:
loop {
tokio::select! {
() = token.cancelled() => break,
() = interval.tick() => { /* handle tick */ },
}
}
If you don't care about the task's partial progress at all ... sure. But it's not too common for that to be the case.
E.g., if there are connections, you might want them to close the connection nicely instead of just stopping abrubtly.
What about the things talking with the shop actor? They might send a message and never get a reply.
I guess they get killed too, but perhaps you want the other actor to shut down nicely and send a goodbye message on a websocket.
oh, so they just update the shop on a timer?
I guess you're probably fine. Though another thing is that if you clean everything up, then you can find bugs by shutting stuff down and looking in tokio-console to see if there are remaining tasks. You can't do that if some tasks legitimately don't exit.
yeah, if you make all tasks exit in the shutdown routine, then you can run the shutdown routine and then don't kill the runtime to see if the number of tasks is now zero
It's up to you, but there are some advantages to cleaning up everything.