For context, in our program we have a long running task that acts as a state machine, and every so often an event occurs that we need to handle. The event is produced and consumed by one task at most, the event is recurring, and multiple events shouldn't queue (i.e. if the event happens twice without being consumed, we need to handle it only once).
A coworker of mine recently implemented this using a loop and a shared atomic boolean flag (the idea being when the flag
is set, do a thing. The event producer sets the flag when the event occurs, and the consumer resets the flag after handling the event):
async fn toggle_on_reactor(flag: Arc<AtomicBool>) {
while !flag.load(Ordering::Acquire) {
tokio::time::sleep(Duration::from_millis(10)).await
}
}
used by the consumer like
let event = toggle_on_reactor(flag);
loop {
select! {
// other futures here
// the event
_ = event => {
// do things, then set `flag` back to false
}
}
}
In code review, I suggested they use something like tokio::sync::Notify
instead, but got some pushback (because Notify
internally uses a loop and does some atomic operations to check for a notification, which my coworker believed was just as wasteful as their version) and now I'm doubting myself.
What would be the best way to do this? For such a simple case Notify
may be overkill, but using a loop with a timeout as in the original just doesn't feel right to me.