use std::sync::Arc;
use std::time::{Duration, Instant};
use super::Shard;
use crate::sync::internal::{AtomicU8, Ordering};
const ASKED: u8 = 0;
const KEEPING: u8 = 1;
const STOPPING: u8 = 2;
const DONE: u8 = 3;
const CALLED_OFF: u8 = 4;
pub(crate) struct Keeper {
state: AtomicU8,
thread: std::thread::Thread,
}
impl Shard {
pub(super) fn hand_off_port(&self) -> bool {
let port = &self.shared.port;
while port.out() > 0
&& self
.poll_io(usize::MAX, Some(Duration::ZERO))
.is_ok_and(|events| events > 0)
{}
if port.out() == 0 {
return true;
}
let keeper = Arc::new(Keeper {
state: AtomicU8::new(ASKED),
thread: std::thread::current(),
});
let job = {
let (keeper, port) = (Arc::clone(&keeper), Arc::clone(port));
move || {
if keeper
.state
.compare_exchange(ASKED, KEEPING, Ordering::AcqRel, Ordering::Acquire)
.is_err()
{
return;
}
port.keep(
|| keeper.state.load(Ordering::Acquire) == STOPPING,
|entry| unsafe { crate::io::source::port::complete(entry) },
);
if keeper.state.swap(DONE, Ordering::AcqRel) == STOPPING {
keeper.thread.unpark();
}
}
};
if !crate::rt::runtime::spawn_blocking_inside(job) {
return false;
}
*self.keeper.borrow_mut() = Some(keeper);
true
}
pub(crate) fn take_port_back(&self) {
let Some(keeper) = self.keeper.borrow_mut().take() else {
return;
};
let stopping = keeper
.state
.compare_exchange(ASKED, CALLED_OFF, Ordering::AcqRel, Ordering::Acquire)
.or_else(|_| {
keeper.state.compare_exchange(
KEEPING,
STOPPING,
Ordering::AcqRel,
Ordering::Acquire,
)
});
if stopping != Ok(KEEPING) {
return;
}
self.shared.notifier.notify();
while keeper.state.load(Ordering::Acquire) != DONE {
std::thread::park();
}
}
pub(super) fn port_kept(&self) -> bool {
self.keeper
.borrow()
.as_ref()
.is_some_and(|k| matches!(k.state.load(Ordering::Acquire), ASKED | KEEPING))
}
pub(super) fn settle_port(&self) {
let deadline = Instant::now() + Duration::from_secs(1);
while self.shared.port.out() > 0 {
let left = deadline.saturating_duration_since(Instant::now());
if left.is_zero()
|| self
.poll_io(usize::MAX, Some(left.min(Duration::from_millis(10))))
.is_err()
{
return;
}
}
}
}