use std::sync::{
OnceLock,
atomic::{AtomicUsize, Ordering},
mpsc::{self, Receiver},
};
use log::error;
use crate::circuit_processor::RuntimeError;
pub struct CompletionHandler {
pub(crate) ops_remaining: AtomicUsize,
pub(crate) callback: Box<dyn Fn(Option<RuntimeError>) + 'static + Sync + Send>,
pub(crate) error: OnceLock<RuntimeError>,
}
impl CompletionHandler {
pub fn new<F>(callback: F) -> Self
where
F: Fn(Option<RuntimeError>) + Sync + Send + 'static,
{
Self {
ops_remaining: AtomicUsize::new(1),
callback: Box::new(callback),
error: OnceLock::new(),
}
}
pub(crate) fn dispatch(&self) {
self.ops_remaining.fetch_add(1, Ordering::Acquire);
}
pub(crate) fn retire(&self) {
if self.ops_remaining.fetch_sub(1, Ordering::Release) == 1 {
(self.callback)(self.error.get().map(|x| x.to_owned()));
}
}
pub fn new_notify() -> (Self, Receiver<Option<RuntimeError>>) {
let (send, recv) = mpsc::channel();
(
Self::new(move |x| {
if let Err(e) = send.send(x.map(|x| x.to_owned())) {
error!("Completion handler notification failed: {e}");
}
}),
recv,
)
}
}