1use std::sync::{Condvar, Mutex, MutexGuard};
14use std::time::{Duration, Instant};
15
16struct State {
17 closed: bool,
18 busy: usize,
20}
21
22pub struct GpuGate {
23 state: Mutex<State>,
24 idle: Condvar,
25}
26
27pub struct Work<'a>(&'a GpuGate);
29
30static GATE: GpuGate = GpuGate::new();
31
32pub fn enter() -> Option<Work<'static>> {
35 GATE.enter()
36}
37
38pub fn begin_shutdown() {
39 GATE.close();
40}
41
42pub fn shutting_down() -> bool {
43 GATE.lock().closed
44}
45
46pub fn wait_idle(timeout: Duration) -> bool {
47 GATE.wait_idle(timeout)
48}
49
50impl Default for GpuGate {
51 fn default() -> Self {
52 Self::new()
53 }
54}
55
56impl GpuGate {
57 pub const fn new() -> GpuGate {
58 GpuGate {
59 state: Mutex::new(State {
60 closed: false,
61 busy: 0,
62 }),
63 idle: Condvar::new(),
64 }
65 }
66
67 fn lock(&self) -> MutexGuard<'_, State> {
68 self.state.lock().unwrap_or_else(|e| e.into_inner())
69 }
70
71 pub fn enter(&self) -> Option<Work<'_>> {
72 let mut s = self.lock();
73 if s.closed {
74 return None;
75 }
76 s.busy = s.busy.saturating_add(1);
77 Some(Work(self))
78 }
79
80 pub fn close(&self) {
82 self.lock().closed = true;
83 }
84
85 pub fn wait_idle(&self, timeout: Duration) -> bool {
87 let t0 = Instant::now();
88 let mut s = self.lock();
89 while s.busy > 0 {
90 let Some(left) = timeout.checked_sub(t0.elapsed()).filter(|d| !d.is_zero()) else {
91 return false;
92 };
93 s = self
94 .idle
95 .wait_timeout(s, left)
96 .unwrap_or_else(|e| e.into_inner())
97 .0;
98 }
99 true
100 }
101}
102
103impl Drop for Work<'_> {
104 fn drop(&mut self) {
105 let mut s = self.0.lock();
106 s.busy = s.busy.saturating_sub(1);
107 if s.busy == 0 {
108 drop(s);
109 self.0.idle.notify_all();
110 }
111 }
112}