Skip to main content

gasm/
sync.rs

1//! Locks for [`crate::thread`]'s cooperative threads, shaped like `std::sync`:
2//! [`Mutex`], [`Condvar`], [`Semaphore`]. A wait lets the other threads run; time
3//! is the frame's. Without the threaded loop there is only the calling thread: locks
4//! always succeed, waits with a timeout time out at once, waits without one trap
5//! (nothing else could ever wake them).
6
7use std::cell::{Cell, UnsafeCell};
8use std::ops::{Deref, DerefMut};
9use std::time::Duration;
10
11use crate::thread::{self, FOREVER};
12
13fn addr<T: ?Sized>(x: &T) -> usize {
14    x as *const T as *const u8 as usize
15}
16
17/// A mutual exclusion lock (not recursive: locking it again from the same thread
18/// is a deadlock).
19pub struct Mutex<T: ?Sized> {
20    owner: Cell<u64>,
21    data: UnsafeCell<T>,
22}
23
24// One wasm thread, and only one cooperative thread inside at a time.
25unsafe impl<T: ?Sized> Sync for Mutex<T> {}
26unsafe impl<T: ?Sized> Send for Mutex<T> {}
27
28impl<T> Mutex<T> {
29    pub const fn new(value: T) -> Mutex<T> {
30        Mutex { owner: Cell::new(0), data: UnsafeCell::new(value) }
31    }
32    pub fn into_inner(self) -> T {
33        self.data.into_inner()
34    }
35}
36
37impl<T: ?Sized> Mutex<T> {
38    /// Wait until the lock is free, then hold it until the guard drops.
39    pub fn lock(&self) -> MutexGuard<'_, T> {
40        while self.owner.get() != 0 {
41            thread::block(addr(self), FOREVER);
42        }
43        self.owner.set(thread::current().0);
44        MutexGuard { lock: self }
45    }
46    /// The lock if it is free now.
47    pub fn try_lock(&self) -> Option<MutexGuard<'_, T>> {
48        if self.owner.get() != 0 {
49            return None;
50        }
51        self.owner.set(thread::current().0);
52        Some(MutexGuard { lock: self })
53    }
54    pub fn get_mut(&mut self) -> &mut T {
55        self.data.get_mut()
56    }
57    fn unlock(&self) {
58        self.owner.set(0);
59        thread::wake(addr(self), true);
60    }
61}
62
63impl<T: Default> Default for Mutex<T> {
64    fn default() -> Self {
65        Mutex::new(T::default())
66    }
67}
68
69/// Holds a [`Mutex`]; unlocks it when dropped.
70pub struct MutexGuard<'a, T: ?Sized> {
71    lock: &'a Mutex<T>,
72}
73
74impl<T: ?Sized> Deref for MutexGuard<'_, T> {
75    type Target = T;
76    fn deref(&self) -> &T {
77        unsafe { &*self.lock.data.get() }
78    }
79}
80impl<T: ?Sized> DerefMut for MutexGuard<'_, T> {
81    fn deref_mut(&mut self) -> &mut T {
82        unsafe { &mut *self.lock.data.get() }
83    }
84}
85impl<T: ?Sized> Drop for MutexGuard<'_, T> {
86    fn drop(&mut self) {
87        self.lock.unlock();
88    }
89}
90
91/// Whether a timed wait returned because the time ran out.
92#[derive(Clone, Copy, Debug, PartialEq, Eq)]
93pub struct WaitTimeoutResult(bool);
94impl WaitTimeoutResult {
95    pub fn timed_out(&self) -> bool {
96        self.0
97    }
98}
99
100/// A condition variable: wait for a change another thread makes under a [`Mutex`].
101#[derive(Default)]
102pub struct Condvar {
103    // a byte, so every Condvar has its own address (what waiters block on)
104    _id: u8,
105}
106
107impl Condvar {
108    pub const fn new() -> Condvar {
109        Condvar { _id: 0 }
110    }
111    /// Unlock, wait for a notify, lock again.
112    pub fn wait<'a, T: ?Sized>(&self, guard: MutexGuard<'a, T>) -> MutexGuard<'a, T> {
113        self.wait_deadline(guard, FOREVER).0
114    }
115    /// [`wait`](Condvar::wait) with a timeout in frame time.
116    pub fn wait_timeout<'a, T: ?Sized>(&self, guard: MutexGuard<'a, T>, d: Duration) -> (MutexGuard<'a, T>, WaitTimeoutResult) {
117        self.wait_deadline(guard, crate::time_ms() + d.as_secs_f64() * 1000.0)
118    }
119    /// Wait while `condition` holds.
120    pub fn wait_while<'a, T: ?Sized>(&self, mut guard: MutexGuard<'a, T>, mut condition: impl FnMut(&mut T) -> bool) -> MutexGuard<'a, T> {
121        while condition(&mut *guard) {
122            guard = self.wait(guard);
123        }
124        guard
125    }
126    fn wait_deadline<'a, T: ?Sized>(&self, guard: MutexGuard<'a, T>, deadline: f64) -> (MutexGuard<'a, T>, WaitTimeoutResult) {
127        let lock = guard.lock;
128        drop(guard);
129        let timed_out = thread::block(addr(self), deadline);
130        (lock.lock(), WaitTimeoutResult(timed_out))
131    }
132    pub fn notify_one(&self) {
133        thread::wake(addr(self), true);
134    }
135    pub fn notify_all(&self) {
136        thread::wake(addr(self), false);
137    }
138}
139
140/// A counting semaphore.
141pub struct Semaphore {
142    count: Cell<u32>,
143}
144
145unsafe impl Sync for Semaphore {}
146
147impl Semaphore {
148    pub const fn new(count: u32) -> Semaphore {
149        Semaphore { count: Cell::new(count) }
150    }
151    /// Wait until the count is above 0, then take one.
152    pub fn acquire(&self) {
153        self.acquire_deadline(FOREVER);
154    }
155    /// [`acquire`](Semaphore::acquire) with a timeout in frame time; false if it timed out.
156    pub fn acquire_timeout(&self, d: Duration) -> bool {
157        self.acquire_deadline(crate::time_ms() + d.as_secs_f64() * 1000.0)
158    }
159    pub fn try_acquire(&self) -> bool {
160        let n = self.count.get();
161        if n == 0 {
162            return false;
163        }
164        self.count.set(n - 1);
165        true
166    }
167    pub fn release(&self) {
168        self.count.set(self.count.get() + 1);
169        thread::wake(addr(self), true);
170    }
171    pub fn count(&self) -> u32 {
172        self.count.get()
173    }
174    fn acquire_deadline(&self, deadline: f64) -> bool {
175        while self.count.get() == 0 {
176            if thread::block(addr(self), deadline) {
177                return self.try_acquire();
178            }
179        }
180        self.count.set(self.count.get() - 1);
181        true
182    }
183}