use std::{
future::Future,
marker::PhantomData,
pin::Pin,
ptr::null_mut,
sync::atomic::{AtomicBool, AtomicPtr, Ordering},
task::{Context, Poll, Waker},
};
use ctrlc::set_handler;
pub use ctrlc::Error;
use crate::driver::{unpark::Unpark, UnparkHandle};
static WAKER: AtomicPtr<Waker> = AtomicPtr::new(null_mut());
static ACTIVE: AtomicBool = AtomicBool::new(false);
#[derive(Debug)]
pub struct CtrlC {
_private: PhantomData<*const ()>,
}
impl Future for CtrlC {
type Output = ();
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
if ACTIVE.swap(false, Ordering::SeqCst) {
Poll::Ready(())
} else {
let new_waker = Box::new(cx.waker().clone());
let old_waker_ptr = WAKER.swap(Box::into_raw(new_waker), Ordering::SeqCst);
if !old_waker_ptr.is_null() {
let _ = unsafe { Box::from_raw(old_waker_ptr) };
}
Poll::Pending
}
}
}
impl CtrlC {
pub fn new() -> Result<Self, Error> {
let unpark_handler = UnparkHandle::current();
set_handler(move || {
ACTIVE.store(true, Ordering::SeqCst);
let waker_ptr = WAKER.swap(null_mut(), Ordering::SeqCst);
if !waker_ptr.is_null() {
unsafe { Box::from_raw(waker_ptr) }.wake();
}
let _ = unpark_handler.unpark();
})?;
Ok(CtrlC {
_private: PhantomData,
})
}
pub fn ctrlc() {
let pid = std::process::id() as _;
unsafe {
#[cfg(unix)]
libc::kill(pid, libc::SIGINT);
#[cfg(windows)]
windows_sys::Win32::System::Console::GenerateConsoleCtrlEvent(
windows_sys::Win32::System::Console::CTRL_C_EVENT,
pid,
);
};
}
}