use std::{
ffi::c_void,
future::Future,
pin::Pin,
sync::{
atomic::{AtomicBool, AtomicI32},
Arc, Mutex,
},
task::Poll,
};
use atomic_waker::AtomicWaker;
use log::trace;
use crate::{HidError, HidResult};
use objc2_io_kit::kIOReturnSuccess;
pub struct CallbackInner<Result> {
pub result: Mutex<Option<Result>>,
pub ret: AtomicI32,
pub waker: AtomicWaker,
pub done: AtomicBool,
pub cancelled: AtomicBool,
}
impl<Result> Default for CallbackInner<Result> {
fn default() -> Self {
Self {
result: Default::default(),
waker: Default::default(),
done: Default::default(),
cancelled: Default::default(),
ret: Default::default(),
}
}
}
pub struct CallbackContext<Result> {
inner: Arc<CallbackInner<Result>>,
}
impl<Result> CallbackContext<Result> {
pub fn new() -> Self {
Self {
inner: Arc::new(CallbackInner::default()),
}
}
#[inline(always)]
pub fn as_raw(&self) -> *const CallbackInner<Result> {
let callback_arc = self.inner.clone();
Arc::into_raw(callback_arc)
}
pub fn inner_from_raw(raw: *const c_void) -> Arc<CallbackInner<Result>> {
unsafe { Arc::from_raw(raw as *const CallbackInner<Result>) }
}
}
impl<Result> Drop for CallbackContext<Result> {
fn drop(&mut self) {
self.inner
.cancelled
.store(true, std::sync::atomic::Ordering::Relaxed);
trace!("CallbackContext dropped");
}
}
impl<R: Copy> Future for CallbackContext<R> {
type Output = HidResult<Option<R>>;
fn poll(self: Pin<&mut Self>, cx: &mut std::task::Context<'_>) -> Poll<Self::Output> {
self.inner.waker.register(cx.waker());
if !self.inner.done.load(std::sync::atomic::Ordering::Relaxed) {
return Poll::Pending;
}
#[allow(non_upper_case_globals, non_snake_case)]
Poll::Ready(match self.inner.ret.load(std::sync::atomic::Ordering::Relaxed) {
kIOReturnSuccess => match self.inner.result.lock() {
Ok(result) => Ok(*result),
Err(e) => Err(HidError::message(format!("Mutex error: {:?}", e))),
},
other => Err(HidError::message(format!("report writer callback error: {:#X}", other))),
})
}
}