Assume that I have a task that does some work in an infinite loop (listening to connections or taking data from receiver). Now I want to be able to stop it gracefully by some cancellation signal. It might be done in at least 3 ways:
- Use token inside the loop
- Use token outside the loop
- Use token.run_until_cancelled (I think it is equivalent to 2)
To me they look more or less equivalent, but I feel that there are some pitfalls I could have missed. The question is when to use on of these ways and what are the implications of each one on behavior, safety, and performance.
async fn func_select_inside(mut receiver: mpsc::Receiver<String>, cancel: CancellationToken) {
println!("select! inside the loop:");
let mut num_handled = 0;
loop {
select! {
msg = receiver.recv() => {
match msg {
Some(_msg) => {num_handled += 1;}
None => break,
}
}
_ = cancel.cancelled() => {
println!("[inside] cancelled");
break;
}
}
}
println!("Num handled {num_handled}");
}
async fn func_select_outside(mut receiver: mpsc::Receiver<String>, cancel: CancellationToken) {
println!("select! outside the loop:");
let num_handled = Arc::new(AtomicU64::new(0));
let main_loop = {
let num_handled = num_handled.clone();
async move {
loop {
match receiver.recv().await {
Some(_msg) => {
num_handled.fetch_add(1, std::sync::atomic::Ordering::SeqCst);
}
None => break,
}
}
}
};
tokio::select! {
_ = main_loop => {},
_ = cancel.cancelled() => {
println!("[outside] cancelled");
},
}
let num_handled = num_handled.load(std::sync::atomic::Ordering::SeqCst);
println!("Num handled {num_handled}");
}
async fn func_run_until(mut receiver: mpsc::Receiver<String>, cancel: CancellationToken) {
println!("run_until:");
let num_handled = Arc::new(AtomicU64::new(0));
let main_loop = {
let num_handled = num_handled.clone();
async move {
loop {
match receiver.recv().await {
Some(_msg) => {
num_handled.fetch_add(1, std::sync::atomic::Ordering::SeqCst);
}
None => break,
}
}
}
};
cancel.run_until_cancelled(main_loop).await;
let num_handled = num_handled.load(std::sync::atomic::Ordering::SeqCst);
println!("Num handled {num_handled}");
}
Full code can be found here Rust Playground