use std::{
cell::{Cell, RefCell},
sync::{
atomic::{AtomicU32, Ordering},
Arc, Mutex, RwLock, Weak,
},
};
use crate::blocking::Mailbox;
pub(crate) const EXEC_BITS: u32 = 10;
pub(crate) const INDEX_BITS: u32 = 22;
const MAX_EXEC: u32 = (1 << EXEC_BITS) - 1;
pub(crate) const ROOT_WAKE: u64 = u64::MAX;
static REGISTRY: RwLock<Vec<Option<Weak<Mailbox>>>> = RwLock::new(Vec::new());
static FREE_IDS: Mutex<Vec<u32>> = Mutex::new(Vec::new());
static NEXT_ID: AtomicU32 = AtomicU32::new(1);
struct IdGuard(u32);
impl Drop for IdGuard {
fn drop(&mut self) {
if let Ok(mut reg) = REGISTRY.write() {
if let Some(slot) = reg.get_mut(self.0 as usize) {
*slot = None;
}
}
FREE_IDS
.lock()
.unwrap_or_else(|e| e.into_inner())
.push(self.0);
}
}
thread_local! {
static EXEC_ID: Cell<u32> = const { Cell::new(0) };
static GUARD: RefCell<Option<IdGuard>> = const { RefCell::new(None) };
}
#[inline]
pub(crate) fn current() -> u32 {
EXEC_ID.with(Cell::get)
}
pub(crate) fn ensure_id() -> u32 {
let existing = current();
if existing != 0 {
return existing;
}
let id = FREE_IDS
.lock()
.unwrap_or_else(|e| e.into_inner())
.pop()
.unwrap_or_else(|| NEXT_ID.fetch_add(1, Ordering::Relaxed));
assert!(
id <= MAX_EXEC,
"more than {MAX_EXEC} threads are running nexio executors at once"
);
EXEC_ID.with(|c| c.set(id));
GUARD.with(|g| *g.borrow_mut() = Some(IdGuard(id)));
id
}
pub(crate) fn register(mailbox: &Arc<Mailbox>) {
let id = current() as usize;
debug_assert!(id != 0, "register before ensure_id");
let mut reg = REGISTRY.write().unwrap_or_else(|e| e.into_inner());
if reg.len() <= id {
reg.resize(id + 1, None);
}
reg[id] = Some(Arc::downgrade(mailbox));
}
pub(crate) fn wake_remote(exec: u32, raw: u64) {
let mailbox = {
let reg = REGISTRY.read().unwrap_or_else(|e| e.into_inner());
reg.get(exec as usize)
.and_then(|s| s.as_ref())
.and_then(Weak::upgrade)
};
if let Some(mailbox) = mailbox {
mailbox.post_wake(raw);
}
}