cspice/
lib.rs

1pub mod cell;
2pub mod common;
3pub mod coordinates;
4pub mod data;
5pub mod error;
6pub mod gf;
7pub mod spk;
8pub mod string;
9pub mod time;
10pub mod vector;
11
12use crate::error::set_error_defaults;
13pub use crate::error::Error;
14use crate::string::SpiceString;
15use parking_lot::{ReentrantMutex, ReentrantMutexGuard};
16use std::cell::RefCell;
17use std::fmt::Debug;
18use std::ops::Deref;
19use thiserror::Error;
20
21// Boolean indicates if library has been initialised
22static SPICE_LOCK: ReentrantMutex<RefCell<bool>> = ReentrantMutex::new(RefCell::new(false));
23
24pub(crate) fn with_spice_lock_or_panic<R, F>(f: F) -> R
25where
26    F: FnOnce() -> R,
27{
28    match try_with_spice_lock(f) {
29        Ok(k) => k,
30        Err(e) => {
31            panic!("{e}")
32        }
33    }
34}
35
36/// The SPICE library [is not thread safe](https://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/req/problems.html#Problem:%20SPICE%20code%20is%20not%20thread%20safe.).
37/// This function can be used to synchronise calls to SPICE functions.
38/// All safe functions in this library use this lock internally.
39pub fn try_with_spice_lock<R, F>(f: F) -> Result<R, SpiceLockError>
40where
41    F: FnOnce() -> R,
42{
43    let guard = SPICE_LOCK.try_lock().ok_or(SpiceLockError)?;
44    initialise_library(&guard);
45    Ok(f())
46}
47
48/// The SPICE library [is not thread safe](https://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/req/problems.html#Problem:%20SPICE%20code%20is%20not%20thread%20safe.).
49/// This function can be used to synchronise calls to SPICE functions.
50/// All safe functions in this library use this lock internally.
51/// The lock is reentrant.
52pub fn with_spice_lock<R, F>(f: F) -> R
53where
54    F: FnOnce() -> R,
55{
56    let guard = SPICE_LOCK.lock();
57    initialise_library(&guard);
58    f()
59}
60
61fn initialise_library(guard: &ReentrantMutexGuard<'static, RefCell<bool>>) {
62    if !guard.borrow().deref() {
63        *guard.borrow_mut() = true;
64        set_error_defaults();
65    }
66}
67
68#[derive(Debug)]
69pub struct SpiceLock(ReentrantMutexGuard<'static, RefCell<bool>>);
70
71/// Error returned from [try_with_spice_lock()].
72#[derive(Debug, Clone, Error)]
73#[cfg_attr(not(test), error("SPICE is already in use by another thread. If multi-threaded use is intentional wrap the call using `with_spice_lock()`."))]
74#[cfg_attr(test, error("SPICE is already in use by another thread. When running unit tests you will likely need to use the `--test-threads=1` argument."))]
75pub struct SpiceLockError;
76
77#[cfg(test)]
78mod tests {
79    use crate::data::furnish;
80    use std::path::PathBuf;
81    use std::sync::Once;
82
83    /// Load test data (once)
84    pub fn load_test_data() {
85        static SPICE_INIT: Once = Once::new();
86        SPICE_INIT.call_once(|| {
87            let data_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("test_data");
88            furnish(data_dir.join("testkernel.txt").to_string_lossy()).unwrap();
89        });
90    }
91}