Skip to main content

spice/core/
lock.rs

1/*!
2A guard making the CSPICE API usable from several threads.
3
4See the [multi-threaded usage][crate#multi-threaded-usage] section of the crate documentation.
5*/
6
7use std::cell::Cell;
8use std::marker::PhantomData;
9use std::sync::atomic::{AtomicBool, Ordering};
10
11/// Whether an instance currently exists.
12static IS_LOCKED: AtomicBool = AtomicBool::new(false);
13
14/**
15A wrapper singleton struct around the API to prevent concurrent calls to SPICE functions from
16multiple threads.
17
18Exposes all functions as methods with identical signatures besides the added `&self` argument.
19Only available with the `lock` feature enabled.
20*/
21pub struct SpiceLock {
22    // Private dummy field. Prevents direct instantiation and makes the type `!Sync`, because
23    // `Cell` is `!Sync`.
24    _x: PhantomData<Cell<()>>,
25}
26
27impl SpiceLock {
28    /**
29    Attempt to create a `SpiceLock` instance.
30
31    Will be `Err` if an instance already exists.
32    */
33    pub fn try_acquire() -> Result<Self, &'static str> {
34        // Sets the value to `true` if it was `false`, and reports whether the swap happened. If it
35        // did, this is the only instance in the process.
36        match IS_LOCKED.compare_exchange(false, true, Ordering::Acquire, Ordering::Relaxed) {
37            Ok(_) => Ok(Self { _x: PhantomData }),
38            Err(_) => Err("Cannot acquire SPICE lock: Already locked."),
39        }
40    }
41}
42
43impl Drop for SpiceLock {
44    fn drop(&mut self) {
45        IS_LOCKED.store(false, Ordering::Release);
46    }
47}