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}