use core::pin::pin;
use std::sync::{Arc, OnceLock, Weak};
use std::thread::Thread;
use std::time::Duration;
use kovan_queue::seg_queue::SegQueue;
use super::bell::Bell;
use super::life::Life;
use super::slot::{self, Slot};
use crate::rt::runtime::Inner;
use crate::rt::shard::{Shard, Shared};
use crate::sync::internal::{AtomicBool, AtomicPtr, Ordering, fence};
const STREAK: u32 = 4;
pub(crate) struct Owned(pub(crate) Arc<Shard>);
unsafe impl Send for Owned {}
struct Held(Weak<Shard>);
unsafe impl Send for Held {}
unsafe impl Sync for Held {}
#[derive(Clone)]
pub(crate) struct Runner {
pub(crate) slot: Arc<Slot>,
pub(crate) life: Arc<Life>,
}
struct Place {
shared: Arc<Shared>,
shard: Held,
first: Runner,
}
pub(crate) struct Gift {
pub(crate) place: usize,
pub(crate) shard: Owned,
pub(crate) slot: Arc<Slot>,
}
pub(crate) struct Spare {
thread: Thread,
life: Arc<Life>,
gift: AtomicPtr<Gift>,
}
impl Spare {
pub(crate) fn here(life: Arc<Life>) -> Arc<Self> {
Arc::new(Self {
thread: std::thread::current(),
life,
gift: AtomicPtr::new(core::ptr::null_mut()),
})
}
fn take_gift(&self) -> Option<Gift> {
let gift = self.gift.swap(core::ptr::null_mut(), Ordering::Acquire);
(!gift.is_null()).then(|| *unsafe { Box::from_raw(gift) })
}
}
impl Drop for Spare {
fn drop(&mut self) {
drop(self.take_gift());
}
}
pub(crate) struct Monitor {
after: Duration,
full_fence: bool,
places: Box<[OnceLock<Place>]>,
spares: SegQueue<Arc<Spare>>,
asleep: AtomicBool,
bell: Bell,
closed: AtomicBool,
closing: crate::sync::Notify,
cores: Vec<usize>,
long_poll: Option<crate::rt::tuning::TaskHook>,
}
impl Monitor {
pub(crate) fn new(
after: Duration,
places: usize,
cores: Vec<usize>,
long_poll: Option<crate::rt::tuning::TaskHook>,
) -> Self {
Self {
after,
full_fence: !super::fence::available(),
places: (0..places).map(|_| OnceLock::new()).collect(),
spares: SegQueue::new(),
asleep: AtomicBool::new(false),
bell: Bell::new(),
closed: AtomicBool::new(false),
closing: crate::sync::Notify::new(),
cores,
long_poll,
}
}
fn report_long(&self, task: u64) {
let (Some(hook), Some(id)) = (
&self.long_poll,
core::num::NonZeroU64::new(task).map(crate::rt::task::Id),
) else {
return;
};
let meta = crate::rt::tuning::TaskMeta::new(id);
let _ = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| hook(&meta)));
}
pub(crate) fn slot(&self) -> Arc<Slot> {
Arc::new(Slot::new(self.full_fence))
}
pub(crate) fn started(&self, index: usize, shard: &Arc<Shard>, first: Runner) {
if let Some(place) = self.places.get(index) {
let _ = place.set(Place {
shared: Arc::clone(&shard.shared),
shard: Held(Arc::downgrade(shard)),
first,
});
}
self.worker_woke();
}
pub(crate) fn worker_woke(&self) {
fence(Ordering::SeqCst);
if self.asleep.load(Ordering::SeqCst) {
self.bell.ring();
}
}
pub(crate) fn close(&self) {
self.closed.store(true, Ordering::SeqCst);
self.closing.notify_waiters();
self.bell.ring();
while let Ok(spare) = self.spares.try_pop() {
spare.thread.unpark();
}
}
pub(crate) fn is_closed(&self) -> bool {
self.closed.load(Ordering::SeqCst)
}
pub(crate) fn wait_for_shard(&self, me: &Arc<Spare>) -> Option<Gift> {
self.spares.push(Arc::clone(me));
loop {
if let Some(gift) = me.take_gift() {
return Some(gift);
}
if self.is_closed() {
return me.take_gift();
}
std::thread::park();
}
}
pub(crate) async fn home_done(&self, home: &super::home::Home) {
let mut closing = pin!(self.closing.notified());
closing.as_mut().enable();
if self.is_closed() {
return;
}
core::future::poll_fn(|cx| {
if home.poll_drained(cx).is_ready() {
return core::task::Poll::Ready(());
}
closing.as_mut().poll(cx)
})
.await;
}
fn all_parked(&self) -> bool {
self.places
.iter()
.filter_map(OnceLock::get)
.all(|place| place.shared.is_parked() || place.shared.is_stopped())
}
}
struct Watch {
runner: Runner,
last: u64,
streak: u32,
longest: u32,
reported: u64,
}
pub(crate) fn run(monitor: &Arc<Monitor>, runtime: &Weak<Inner>) {
monitor.bell.bind();
let tick = (monitor.after / STREAK).max(Duration::from_micros(100));
let mut watches: Vec<Option<Watch>> = (0..monitor.places.len()).map(|_| None).collect();
let mut waiting: Option<Gift> = None;
let deliver = |watches: &mut Vec<Option<Watch>>, gift: Gift| match give(monitor, runtime, gift)
{
Ok((place, life)) => {
if let Some(watch) = watches[place].as_mut() {
watch.runner.life = life;
}
None
}
Err(gift) => Some(gift),
};
while !monitor.is_closed() {
if let Some(gift) = waiting.take() {
waiting = deliver(&mut watches, gift);
}
if waiting.is_none() && monitor.all_parked() {
monitor.asleep.store(true, Ordering::SeqCst);
fence(Ordering::SeqCst);
if monitor.all_parked() && !monitor.is_closed() {
monitor.bell.wait();
}
monitor.asleep.store(false, Ordering::SeqCst);
watches
.iter_mut()
.flatten()
.for_each(|watch| watch.streak = 0);
continue;
}
let next = std::time::Instant::now() + tick;
while let Some(left) = next.checked_duration_since(std::time::Instant::now())
&& !left.is_zero()
&& !monitor.is_closed()
{
monitor.bell.wait_for(left);
}
for (index, place) in monitor.places.iter().enumerate() {
let Some(place) = place.get() else {
continue;
};
let watch = watches[index].get_or_insert_with(|| Watch {
runner: place.first.clone(),
last: slot::inside(0),
streak: 0,
longest: 0,
reported: slot::inside(0),
});
let word = watch.runner.slot.word();
if slot::is_outside(word) && word == watch.last {
watch.streak += 1;
} else {
if watch.streak > watch.longest {
watch.longest = watch.streak;
place.shared.counters.longest_poll(tick * watch.streak);
}
watch.streak = 0;
}
watch.last = word;
if watch.streak < STREAK {
continue;
}
let long = (watch.reported != word).then(|| {
watch.reported = word;
let task = watch.runner.slot.task();
if watch.runner.slot.word() == word {
task
} else {
0
}
});
if waiting.is_none() {
watch.streak = 0;
if let Some(gift) = take(monitor, place, index, watch, word) {
place.shared.counters.longest_poll(tick * STREAK);
waiting = deliver(&mut watches, gift);
}
}
if let Some(task) = long {
monitor.report_long(task);
}
}
}
if let Some(gift) = waiting.take() {
let inner = runtime.upgrade();
let _runtime = inner.as_ref().map(crate::rt::context::set_runtime);
drop(gift);
}
}
fn take(
monitor: &Monitor,
place: &Place,
index: usize,
watch: &mut Watch,
word: u64,
) -> Option<Gift> {
let old = &watch.runner;
old.slot.claim();
if monitor.full_fence {
fence(Ordering::SeqCst);
} else if !super::fence::heavy() {
old.slot.cancel();
return None;
}
if old.slot.word() != word || old.slot.is_pinned() {
old.slot.cancel();
return None;
}
if !old.slot.take() {
return None;
}
let shard = place.shard.0.upgrade()?;
old.life.detach();
old.life.unpin(&monitor.cores);
shard.hand_over_queues();
shard.shared.counters.handed_off();
let slot = monitor.slot();
watch.runner = Runner {
slot: Arc::clone(&slot),
life: Arc::clone(&old.life),
};
watch.last = slot::inside(0);
watch.reported = slot::inside(0);
Some(Gift {
place: index,
shard: Owned(shard),
slot,
})
}
fn give(monitor: &Monitor, runtime: &Weak<Inner>, gift: Gift) -> Result<(usize, Arc<Life>), Gift> {
let place = gift.place;
if let Ok(spare) = monitor.spares.try_pop() {
let life = Arc::clone(&spare.life);
spare
.gift
.store(Box::into_raw(Box::new(gift)), Ordering::Release);
spare.thread.unpark();
return Ok((place, life));
}
let Some(inner) = runtime.upgrade() else {
return Err(gift);
};
let life = Arc::new(Life::new());
crate::rt::workers::Workers::spawn_runner(&inner, gift, Arc::clone(&life))?;
Ok((place, life))
}