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}