use std::{
cell::Cell,
io,
marker::PhantomData,
os::windows::io::{AsRawHandle, RawHandle},
process::{Child, ChildStderr},
thread::sleep,
time::Duration,
};
use ::find_msvc_tools::windows_sys::{
WaitForMultipleObjects, INFINITE, MAXIMUM_WAIT_OBJECTS, WAIT_FAILED, WAIT_OBJECT_0,
};
use crate::{cell_modify, cell_push};
#[derive(Default)]
pub(crate) struct Reactor {
handles: Cell<Vec<RawHandle>>,
on_semaphore: Cell<Option<Box<dyn FnOnce()>>>,
}
pub(crate) struct Registration<'a>(PhantomData<&'a Reactor>);
pub(crate) struct ChildRegistration<'a> {
reactor: &'a Reactor,
handle: RawHandle,
}
pub(crate) struct SemaphoreRegistration<'a> {
reactor: &'a Reactor,
}
impl Reactor {
pub(crate) fn register_stderr(&self, _stderr: &ChildStderr) -> io::Result<Registration<'_>> {
Ok(Registration(PhantomData))
}
pub(crate) fn register_child(&self, child: &Child) -> io::Result<ChildRegistration<'_>> {
let handle = child.as_raw_handle();
cell_push(&self.handles, handle);
Ok(ChildRegistration {
reactor: self,
handle,
})
}
pub(crate) fn register_semaphore(
&self,
semaphore: RawHandle,
on_acquired: Box<dyn FnOnce()>,
) -> SemaphoreRegistration<'_> {
let previous = self.on_semaphore.replace(Some(on_acquired));
assert!(previous.is_none(), "a semaphore is already registered");
cell_modify(&self.handles, |handles| {
handles.push(semaphore);
let last = handles.len() - 1;
handles.swap(0, last);
});
SemaphoreRegistration { reactor: self }
}
pub(crate) fn wait_with_timeout(&self, timeout: Duration) -> io::Result<()> {
let timeout = u32::try_from(timeout.as_millis())
.unwrap_or(u32::MAX)
.min(INFINITE - 1);
cell_modify(&self.handles, |handles| {
if handles.is_empty() {
sleep(Duration::from_millis(timeout.into()));
return Ok(());
}
let count = handles.len().min(MAXIMUM_WAIT_OBJECTS);
let ret = unsafe { WaitForMultipleObjects(count as u32, handles.as_ptr(), 0, timeout) };
if ret == WAIT_FAILED {
return Err(io::Error::last_os_error());
}
if ret == WAIT_OBJECT_0 {
if let Some(on_acquired) = self.on_semaphore.take() {
handles.swap_remove(0);
on_acquired();
}
}
Ok(())
})
}
}
impl Drop for ChildRegistration<'_> {
fn drop(&mut self) {
let handle = self.handle;
cell_modify(&self.reactor.handles, |handles| {
if let Some(i) = handles.iter().position(|h| *h == handle) {
handles.swap_remove(i);
}
});
}
}
impl Drop for SemaphoreRegistration<'_> {
fn drop(&mut self) {
if self.reactor.on_semaphore.take().is_some() {
cell_modify(&self.reactor.handles, |handles| handles.swap_remove(0));
}
}
}