spice/core/lock.rs
1use std::cell::Cell;
2use std::marker::PhantomData;
3use std::sync::atomic::{AtomicBool, Ordering};
4
5// Atomic bool to keep track of whether an instance exists
6static mut IS_LOCKED: AtomicBool = AtomicBool::new(false);
7
8/// A wrapper singleton struct around the API to prevent concurrent calls to SPICE functions from multiple threads.
9/// Exposes all functions as methods with identical signatures besides the added `&self` argument.
10/// Only available with the `lock` feature enabled.
11pub struct SpiceLock {
12 // Private dummy field. Prevents direct instantiation and makes type `!Sync` (because `Cell` is `!Sync`)
13 _x: PhantomData<Cell<()>>,
14}
15
16impl SpiceLock {
17 /// Attempt to create a `SpiceLock` instance.
18 /// Will be `Err` if an instance already exists.
19 pub fn try_acquire() -> Result<Self, &'static str> {
20 // Sets value equal to `true` if it was `false` and
21 // returns a result with the previous value (`Ok` if swapped, `Err` if not)
22 let was_unlocked = unsafe {
23 IS_LOCKED
24 .compare_exchange(false, true, Ordering::Acquire, Ordering::Relaxed)
25 .is_ok()
26 };
27 // If the value was changed, it was atomically set to true and no instance exists
28 if was_unlocked {
29 // Safely return the only instance
30 Ok(Self { _x: PhantomData })
31 } else {
32 // A lock already exists somewhere
33 Err("Cannot acquire SPICE lock: Already locked.")
34 }
35 }
36}
37
38impl Drop for SpiceLock {
39 fn drop(&mut self) {
40 unsafe {
41 IS_LOCKED.store(false, Ordering::Release);
42 }
43 }
44}