use std::cmp::Reverse;
use std::collections::BinaryHeap;
use std::sync::{Arc, Condvar, Mutex};
use std::time::{Duration, Instant};
use crossbeam_deque::Injector;
use super::error::report_fault;
use super::mailbox::Mailbox;
use super::process::{Flow, FlowId};
use super::sync_lock;
enum TimerPayload {
WakeSleeper(Box<Flow>),
WakeReceiver {
pid: FlowId,
mailbox: Arc<Mailbox>,
dest_reg: u8,
},
}
struct TimerEntry {
deadline: Instant,
payload: TimerPayload,
}
impl PartialEq for TimerEntry {
fn eq(&self, other: &Self) -> bool {
self.deadline == other.deadline
}
}
impl Eq for TimerEntry {}
impl PartialOrd for TimerEntry {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl Ord for TimerEntry {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.deadline.cmp(&other.deadline)
}
}
pub struct TimerWheel {
heap: Mutex<BinaryHeap<Reverse<TimerEntry>>>,
cvar: Condvar,
shutdown: Mutex<bool>,
}
impl TimerWheel {
pub fn new() -> Arc<Self> {
Arc::new(TimerWheel {
heap: Mutex::new(BinaryHeap::new()),
cvar: Condvar::new(),
shutdown: Mutex::new(false),
})
}
pub fn schedule_sleep(&self, delay: Duration, flow: Box<Flow>) {
let entry = TimerEntry {
deadline: Instant::now() + delay,
payload: TimerPayload::WakeSleeper(flow),
};
self.push(entry);
}
pub fn schedule_receive_timeout(
&self,
delay: Duration,
pid: FlowId,
mailbox: Arc<Mailbox>,
dest_reg: u8,
) {
let entry = TimerEntry {
deadline: Instant::now() + delay,
payload: TimerPayload::WakeReceiver {
pid,
mailbox,
dest_reg,
},
};
self.push(entry);
}
fn push(&self, entry: TimerEntry) {
match sync_lock::lock(&self.heap, "TimerWheel::push") {
Ok(mut heap) => {
heap.push(Reverse(entry));
self.cvar.notify_one();
}
Err(e) => report_fault(e),
}
}
pub fn shutdown(&self) {
match sync_lock::lock(&self.shutdown, "TimerWheel::shutdown") {
Ok(mut flag) => *flag = true,
Err(e) => report_fault(e),
}
self.cvar.notify_all();
}
pub fn drive(self: &Arc<Self>, injector: &Injector<Box<Flow>>, notify: &(Mutex<()>, Condvar)) {
loop {
let mut heap = match sync_lock::lock(&self.heap, "TimerWheel::drive") {
Ok(h) => h,
Err(e) => {
report_fault(e);
return;
}
};
let shutting_down = match sync_lock::lock(&self.shutdown, "TimerWheel::drive/shutdown") {
Ok(g) => *g,
Err(e) => {
report_fault(e);
return;
}
};
if shutting_down {
return;
}
match heap.peek() {
None => {
match sync_lock::wait_timeout(
&self.cvar,
heap,
Duration::from_millis(250),
"TimerWheel::idle",
) {
Ok((guard, _)) => {
drop(guard);
}
Err(e) => {
report_fault(e);
return;
}
}
}
Some(Reverse(top)) => {
let now = Instant::now();
if top.deadline <= now {
let Reverse(entry) = match heap.pop() {
Some(e) => e,
None => continue,
};
drop(heap);
self.fire(entry, injector, notify);
} else {
let wait_for = top.deadline - now;
match sync_lock::wait_timeout(
&self.cvar,
heap,
wait_for,
"TimerWheel::wait",
) {
Ok((guard, _)) => drop(guard),
Err(e) => {
report_fault(e);
return;
}
}
}
}
}
}
}
fn fire(
&self,
entry: TimerEntry,
injector: &Injector<Box<Flow>>,
notify: &(Mutex<()>, Condvar),
) {
match entry.payload {
TimerPayload::WakeSleeper(flow) => {
injector.push(flow);
}
TimerPayload::WakeReceiver {
pid,
mailbox,
dest_reg,
} => {
match mailbox.take_parked() {
Ok(Some(mut flow)) => {
debug_assert_eq!(
flow.id, pid,
"timer fired for a mailbox owned by a different flow"
);
let _ = flow
.vm
.resume_with(dest_reg, crate::bytecode::Value::Unit);
injector.push(flow);
}
Ok(None) => {
}
Err(e) => report_fault(e),
}
}
}
match sync_lock::lock(¬ify.0, "TimerWheel::fire/notify") {
Ok(_guard) => notify.1.notify_all(),
Err(e) => report_fault(e),
}
}
}