#![cfg_attr(
target_family = "wasm",
allow(
clippy::cast_possible_truncation,
clippy::cast_possible_wrap,
clippy::cast_sign_loss,
)
)]
#[cfg(not(target_family = "wasm"))]
mod native_signal {
use crate::io;
#[cfg(unix)]
use core::sync::atomic::AtomicI32;
use core::sync::atomic::{AtomicBool, Ordering};
#[cfg(unix)]
static SIGNAL_PIPE_READ: AtomicI32 = AtomicI32::new(-1);
#[cfg(unix)]
static SIGNAL_PIPE_WRITE: AtomicI32 = AtomicI32::new(-1);
#[cfg(target_os = "linux")]
const O_CLOEXEC: i32 = 0o2_000_000;
#[cfg(any(
target_os = "macos",
target_os = "freebsd",
target_os = "openbsd",
target_os = "netbsd",
))]
const O_CLOEXEC: i32 = 0x0100_0000;
#[cfg(target_os = "linux")]
const O_NONBLOCK: i32 = 0o0_004_000;
#[cfg(any(
target_os = "macos",
target_os = "freebsd",
target_os = "openbsd",
target_os = "netbsd",
))]
const O_NONBLOCK: i32 = 0x0000_0004;
#[cfg(unix)]
const SIGINT: i32 = 2;
#[cfg(unix)]
type SigHandlerFn = extern "C" fn(i32);
#[cfg(unix)]
unsafe extern "C" {
fn pipe2(pipefd: *mut i32, flags: i32) -> i32;
fn write(fd: i32, buf: *const u8, count: usize) -> isize;
fn read(fd: i32, buf: *mut u8, count: usize) -> isize;
fn signal(signum: i32, handler: SigHandlerFn) -> usize;
}
#[cfg(unix)]
fn get_signal_pipe() -> io::Result<[i32; 2]> {
let r = SIGNAL_PIPE_READ.load(Ordering::Acquire);
if r != -1 {
let w = SIGNAL_PIPE_WRITE.load(Ordering::Acquire);
return Ok([r, w]);
}
let mut fds = [0i32; 2];
if unsafe { pipe2(fds.as_mut_ptr(), O_CLOEXEC | O_NONBLOCK) } < 0 {
return Err(io::Error::last_os_error());
}
SIGNAL_PIPE_WRITE.store(fds[1], Ordering::Release);
SIGNAL_PIPE_READ.store(fds[0], Ordering::Release);
Ok(fds)
}
#[cfg(unix)]
extern "C" fn sigint_handler(_sig: i32) {
let tx = SIGNAL_PIPE_WRITE.load(Ordering::Acquire);
if tx != -1 {
unsafe { write(tx, b"\x00".as_ptr(), 1) };
}
}
static HANDLER_INSTALLED: AtomicBool = AtomicBool::new(false);
fn setup_handler() -> io::Result<()> {
if HANDLER_INSTALLED
.compare_exchange(false, true, Ordering::AcqRel, Ordering::Acquire)
.is_err()
{
return Ok(());
}
install_handler()
}
#[cfg(unix)]
fn install_handler() -> io::Result<()> {
get_signal_pipe()?;
unsafe {
signal(SIGINT, sigint_handler);
}
Ok(())
}
#[cfg(windows)]
static WINDOWS_WAKER: crate::sync::SpinMutex<Option<core::task::Waker>> =
crate::sync::SpinMutex::new(None);
#[cfg(windows)]
static CTRL_C_TRIGGERED: core::sync::atomic::AtomicBool =
core::sync::atomic::AtomicBool::new(false);
#[cfg(windows)]
extern "system" fn windows_ctrl_handler(ctrl_type: u32) -> i32 {
if ctrl_type == 0 {
CTRL_C_TRIGGERED.store(true, core::sync::atomic::Ordering::Release);
{
let mut lock = WINDOWS_WAKER.lock();
if let Some(waker) = lock.take() {
waker.wake();
}
}
1 } else {
0 }
}
#[cfg(windows)]
#[allow(clippy::unnecessary_wraps, clippy::missing_const_for_fn)]
fn install_handler() -> io::Result<()> {
unsafe extern "system" {
fn SetConsoleCtrlHandler(
handler: Option<extern "system" fn(u32) -> i32>,
add: i32,
) -> i32;
}
let res = unsafe { SetConsoleCtrlHandler(Some(windows_ctrl_handler), 1) };
if res == 0 {
Err(io::Error::last_os_error())
} else {
Ok(())
}
}
#[cfg(not(any(unix, windows)))]
#[allow(clippy::unnecessary_wraps)]
fn install_handler() -> io::Result<()> {
Ok(())
}
#[cfg(unix)]
async fn ctrl_c_impl() -> io::Result<()> {
let [rx, _] = get_signal_pipe()?;
crate::rt::wait_readable(rx).await?;
let mut b = 0u8;
loop {
let n = unsafe { read(rx, &mut b, 1) };
if n <= 0 {
break;
}
}
Ok(())
}
#[cfg(windows)]
#[allow(clippy::unused_async, clippy::unnecessary_wraps)]
async fn ctrl_c_impl() -> io::Result<()> {
struct CtrlC;
impl core::future::Future for CtrlC {
type Output = io::Result<()>;
fn poll(
self: core::pin::Pin<&mut Self>,
cx: &mut core::task::Context<'_>,
) -> core::task::Poll<Self::Output> {
*WINDOWS_WAKER.lock() = Some(cx.waker().clone());
if CTRL_C_TRIGGERED.swap(false, core::sync::atomic::Ordering::AcqRel) {
core::task::Poll::Ready(Ok(()))
} else {
core::task::Poll::Pending
}
}
}
CtrlC.await
}
#[cfg(not(any(unix, windows)))]
#[allow(clippy::unused_async)]
async fn ctrl_c_impl() -> io::Result<()> {
Err(io::Error::other("ctrl_c not supported on this platform"))
}
pub async fn ctrl_c() -> io::Result<()> {
setup_handler()?;
ctrl_c_impl().await
}
}
#[cfg(not(target_family = "wasm"))]
pub use native_signal::ctrl_c;
#[cfg(target_family = "wasm")]
use crate::abi::imports;
#[cfg(target_family = "wasm")]
use crate::rt::wasm::OverlappedFuture;
#[cfg(target_family = "wasm")]
pub async fn signal_wait(signum: u32) -> crate::io::Result<()> {
let (err, _, _) = OverlappedFuture::new(move |ov| {
unsafe { imports::signal_wait(ov, signum) };
})
.await;
if err != 0 {
return Err(crate::io::Error::from_raw_os_error(err as i32));
}
Ok(())
}
#[cfg(target_family = "wasm")]
pub async fn ctrl_c() -> crate::io::Result<()> {
signal_wait(2).await
}