use std::sync::{Condvar, Mutex, MutexGuard};
use std::time::{Duration, Instant};
struct State {
closed: bool,
busy: usize,
}
pub struct GpuGate {
state: Mutex<State>,
idle: Condvar,
}
pub struct Work<'a>(&'a GpuGate);
static GATE: GpuGate = GpuGate::new();
pub fn enter() -> Option<Work<'static>> {
GATE.enter()
}
pub fn begin_shutdown() {
GATE.close();
}
pub fn shutting_down() -> bool {
GATE.lock().closed
}
pub fn wait_idle(timeout: Duration) -> bool {
GATE.wait_idle(timeout)
}
impl Default for GpuGate {
fn default() -> Self {
Self::new()
}
}
impl GpuGate {
pub const fn new() -> GpuGate {
GpuGate {
state: Mutex::new(State {
closed: false,
busy: 0,
}),
idle: Condvar::new(),
}
}
fn lock(&self) -> MutexGuard<'_, State> {
self.state.lock().unwrap_or_else(|e| e.into_inner())
}
pub fn enter(&self) -> Option<Work<'_>> {
let mut s = self.lock();
if s.closed {
return None;
}
s.busy = s.busy.saturating_add(1);
Some(Work(self))
}
pub fn close(&self) {
self.lock().closed = true;
}
pub fn wait_idle(&self, timeout: Duration) -> bool {
let t0 = Instant::now();
let mut s = self.lock();
while s.busy > 0 {
let Some(left) = timeout.checked_sub(t0.elapsed()).filter(|d| !d.is_zero()) else {
return false;
};
s = self
.idle
.wait_timeout(s, left)
.unwrap_or_else(|e| e.into_inner())
.0;
}
true
}
}
impl Drop for Work<'_> {
fn drop(&mut self) {
let mut s = self.0.lock();
s.busy = s.busy.saturating_sub(1);
if s.busy == 0 {
drop(s);
self.0.idle.notify_all();
}
}
}