use std::sync::{Once, Weak};
use super::{FLASH, FlashInner};
use crate::{
common::time::Instant as RealInstant,
flash::Duration,
native::sync::{Condvar, Mutex, MutexGuard},
};
pub(super) struct Pacer {
cv: Condvar,
armed: Mutex<bool>,
spawn: Once,
owner: Weak<FlashInner>,
}
impl Pacer {
const TICK: Duration = Duration::from_millis(1);
pub(super) fn new(owner: Weak<FlashInner>) -> Self {
Self {
owner,
armed: Mutex::default(),
cv: Condvar::default(),
spawn: Once::new(),
}
}
}
impl FlashInner {
fn pace_run(&self) {
loop {
wait_armed(&self.pacer.cv, self.pacer.armed.lock());
crate::native::thread::sleep(Pacer::TICK);
let mut s = self.core.lock();
let adv = s.try_advance(&self.clock);
drop(s);
adv.fire();
}
}
pub(in crate::flash) fn real_io_enter(&self) {
self.pacer.spawn.call_once(|| {
let owner = self
.pacer
.owner
.upgrade()
.expect("BUG: real_io_enter is reachable only through a live Arc<FlashInner>");
std::thread::Builder::new()
.name("kithara-flash-io-pacer".into())
.spawn(move || owner.pace_run())
.expect("BUG: spawning the flash io-pacer thread cannot fail");
});
let mut armed = self.pacer.armed.lock();
let mut s = self.core.lock();
s.sched.real_io += 1;
if s.sched.real_io == 1 {
s.sched.pace_anchor = Some((RealInstant::now(), self.clock.now_nanos()));
}
drop(s);
if !*armed {
*armed = true;
drop(armed);
self.pacer.cv.notify_one();
}
}
pub(in crate::flash) fn real_io_exit(&self) {
let mut armed = self.pacer.armed.lock();
let mut s = self.core.lock();
debug_assert!(s.sched.real_io > 0, "real_io exit without a matching enter");
s.sched.real_io = s.sched.real_io.saturating_sub(1);
if s.sched.real_io != 0 {
return;
}
s.sched.pace_anchor = None;
*armed = false;
let adv = s.try_advance(&self.clock);
drop(s);
drop(armed);
adv.fire();
}
}
fn wait_armed(cv: &Condvar, mut armed: MutexGuard<'_, bool>) {
while !*armed {
armed = cv.wait(armed);
}
}
pub(in crate::flash) fn real_io_enter() {
FLASH.real_io_enter();
}
pub(in crate::flash) fn real_io_exit() {
FLASH.real_io_exit();
}