use anyhow::Result;
use std::sync::{Arc, Condvar, Mutex, OnceLock};
use std::thread::ThreadId;
use std::time::Duration;
pub struct CardLock<'a> {
release: Option<Box<dyn FnOnce() + 'a>>,
}
impl<'a> CardLock<'a> {
pub fn new(release: impl FnOnce() + 'a) -> Self {
Self {
release: Some(Box::new(release)),
}
}
pub fn none() -> Self {
Self { release: None }
}
}
impl Drop for CardLock<'_> {
fn drop(&mut self) {
if let Some(release) = self.release.take() {
release();
}
}
}
pub trait CardScheduler: Send + Sync {
fn lock(&self);
fn unlock(&self);
fn sequence(&self) {}
fn command(&self) -> Result<()>;
}
#[derive(Default)]
pub struct ThreadLock {
owner: Mutex<(Option<ThreadId>, usize)>,
free: Condvar,
}
impl CardScheduler for ThreadLock {
fn lock(&self) {
let me = std::thread::current().id();
let mut owner = self.owner.lock().unwrap();
while owner.0.is_some_and(|t| t != me) {
owner = self.free.wait(owner).unwrap();
}
owner.0 = Some(me);
owner.1 += 1;
}
fn unlock(&self) {
let mut owner = self.owner.lock().unwrap();
owner.1 -= 1;
if owner.1 == 0 {
owner.0 = None;
self.free.notify_all();
}
}
fn command(&self) -> Result<()> {
Ok(())
}
}
pub(crate) struct SimulatedLock {
scheduler: Mutex<Arc<dyn CardScheduler>>,
}
impl Default for SimulatedLock {
fn default() -> Self {
Self {
scheduler: Mutex::new(Arc::new(ThreadLock::default())),
}
}
}
impl SimulatedLock {
pub(crate) fn set_scheduler(&self, scheduler: Arc<dyn CardScheduler>) {
*self.scheduler.lock().unwrap() = scheduler;
}
fn scheduler(&self) -> Arc<dyn CardScheduler> {
self.scheduler.lock().unwrap().clone()
}
pub(crate) fn exclusive(&self) -> CardLock<'static> {
let scheduler = self.scheduler();
scheduler.lock();
scheduler.sequence();
CardLock::new(move || scheduler.unlock())
}
pub(crate) fn command(&self) -> Result<CardLock<'static>> {
let scheduler = self.scheduler();
scheduler.lock();
let lock = CardLock::new({
let scheduler = scheduler.clone();
move || scheduler.unlock()
});
scheduler.command()?;
Ok(lock)
}
}
static WAIT_NOTICE: OnceLock<Box<dyn Fn() + Send + Sync>> = OnceLock::new();
pub fn set_wait_notice(notice: impl Fn() + Send + Sync + 'static) {
let _ = WAIT_NOTICE.set(Box::new(notice));
}
pub(crate) fn waiting<T>(wait: impl FnOnce() -> T) -> T {
let Some(notice) = WAIT_NOTICE.get() else {
return wait();
};
let (done, finished) = std::sync::mpsc::channel::<()>();
let watcher = std::thread::spawn(move || {
if finished.recv_timeout(Duration::from_secs(1))
== Err(std::sync::mpsc::RecvTimeoutError::Timeout)
{
notice();
}
});
let result = wait();
drop(done);
let _ = watcher.join();
result
}