use std::cell::Cell;
use std::fs::File;
use std::sync::{Mutex, MutexGuard, OnceLock};
static MLX_RUNTIME_LOCK: OnceLock<Mutex<()>> = OnceLock::new();
thread_local! {
static DEPTH: Cell<usize> = const { Cell::new(0) };
}
pub(crate) struct RuntimeGuard {
_process: Option<ProcessLock>,
_outer: Option<MutexGuard<'static, ()>>,
}
impl Drop for RuntimeGuard {
fn drop(&mut self) {
DEPTH.with(|d| d.set(d.get() - 1));
}
}
pub(crate) fn runtime_guard() -> RuntimeGuard {
let depth = DEPTH.with(|d| {
let v = d.get();
d.set(v + 1);
v
});
if depth == 0 {
let process = ProcessLock::acquire();
let outer = MLX_RUNTIME_LOCK
.get_or_init(|| Mutex::new(()))
.lock()
.expect("mlx runtime lock poisoned");
RuntimeGuard {
_process: process,
_outer: Some(outer),
}
} else {
RuntimeGuard {
_process: None,
_outer: None,
}
}
}
struct ProcessLock(File);
impl ProcessLock {
fn acquire() -> Option<Self> {
#[cfg(unix)]
{
use std::os::unix::io::AsRawFd;
let path = std::env::temp_dir().join("rlx-mlx-runtime.lock");
let file = match File::create(&path) {
Ok(f) => f,
Err(e) => {
eprintln!("rlx-mlx: warning: could not create process lock {path:?}: {e}");
return None;
}
};
let fd = file.as_raw_fd();
loop {
let rc = unsafe { libc::flock(fd, libc::LOCK_EX) };
if rc == 0 {
break;
}
let err = std::io::Error::last_os_error();
if err.kind() == std::io::ErrorKind::Interrupted {
continue;
}
eprintln!("rlx-mlx: warning: flock failed on {path:?}: {err}");
return None;
}
Some(ProcessLock(file))
}
#[cfg(not(unix))]
{
None
}
}
}
impl Drop for ProcessLock {
fn drop(&mut self) {
#[cfg(unix)]
{
use std::os::unix::io::AsRawFd;
let fd = self.0.as_raw_fd();
unsafe {
libc::flock(fd, libc::LOCK_UN);
}
}
}
}