emf_core_base_rs/
global.rs

1//! Global api that can be used instead of the local api.
2//!
3//! The global api is the preferred way of interfacing with the interface.
4use crate::ffi::{
5    collections::NonNullConst,
6    sys::api::{GetFunctionFn as GetFunctionFnFFI, SysBinding},
7    Bool, CBase as CBaseFFI,
8};
9use crate::init::CBaseAPILoader;
10use crate::{CBase, CBaseInterfaceInfo, CBaseRef};
11use std::mem::MaybeUninit;
12use std::ptr::NonNull;
13
14pub mod library;
15pub mod module;
16pub mod sys;
17pub mod version;
18
19#[cfg(feature = "extensions")]
20pub mod extensions;
21
22static mut INTERFACE: MaybeUninit<CBaseRef<'static>> = MaybeUninit::uninit();
23
24/// Type indicating that dropping unlocks the interface.
25#[derive(Debug)]
26pub struct Unlock {}
27
28/// Type indicating that dropping does not unlock the interface.
29#[derive(Debug)]
30pub struct ForgetUnlock {}
31
32impl Drop for Unlock {
33    fn drop(&mut self) {
34        unsafe { SysBinding::unlock(get_interface()) }
35    }
36}
37
38/// A token indicating a locked interface.
39#[derive(Debug)]
40pub struct LockToken<T> {
41    _phantom: T,
42}
43
44impl LockToken<Unlock> {
45    /// Takes ownership of the token and exchanges it with
46    /// a token which does not unlock the interface.
47    ///
48    /// # Safety
49    ///
50    /// Improper usage can leave the interface in a locked state.
51    pub unsafe fn relinquish_locking(self) -> LockToken<ForgetUnlock> {
52        std::mem::forget(self);
53        LockToken {
54            _phantom: ForgetUnlock {},
55        }
56    }
57}
58
59impl LockToken<ForgetUnlock> {
60    /// Takes ownership of the token and exchanges it with
61    /// a token which unlocks the interface.
62    ///
63    /// # Safety
64    ///
65    /// Improper usage can unlock the interface multiple times.
66    pub unsafe fn take_ownership(self) -> LockToken<Unlock> {
67        std::mem::forget(self);
68        LockToken {
69            _phantom: Unlock {},
70        }
71    }
72}
73
74impl<T> LockToken<T> {
75    /// Constructs a new token by locking the interface.
76    ///
77    /// The calling thread is stalled until the lock can be acquired.
78    /// Only one thread can hold the lock at a time.
79    ///
80    /// # Return
81    ///
82    /// A token.
83    #[inline]
84    #[must_use]
85    pub fn lock() -> LockToken<Unlock> {
86        unsafe {
87            SysBinding::lock(get_interface());
88            LockToken {
89                _phantom: Unlock {},
90            }
91        }
92    }
93
94    /// Tries to lock the interface.
95    ///
96    /// The function fails if another thread already holds the lock.
97    ///
98    /// # Return
99    ///
100    /// [LockToken] on success and [None] otherwise.
101    #[inline]
102    #[must_use]
103    pub fn try_lock() -> Option<LockToken<Unlock>> {
104        unsafe {
105            match SysBinding::try_lock(get_interface()) {
106                Bool::False => None,
107                Bool::True => Some(LockToken {
108                    _phantom: Unlock {},
109                }),
110            }
111        }
112    }
113
114    /// Constructs a new token without locking.
115    ///
116    /// # Return
117    ///
118    /// A token.
119    ///
120    /// # Safety
121    ///
122    /// Most of the interface assumes that the caller has unique access to the interface.
123    /// This function can be used to bypass this restriction, if the user can guarantee
124    /// that no data-races will occur.
125    #[inline]
126    pub unsafe fn assume_locked() -> LockToken<ForgetUnlock> {
127        LockToken {
128            _phantom: ForgetUnlock {},
129        }
130    }
131}
132
133/// Initializes the interface.
134#[inline]
135pub fn initialize(base_module: Option<NonNull<CBaseFFI>>, get_function_fn: GetFunctionFnFFI) {
136    unsafe {
137        INTERFACE = MaybeUninit::new(CBaseRef::new(NonNullConst::from(
138            CBase::fetch_interface(base_module, get_function_fn).internal_interface(),
139        )));
140    }
141
142    #[cfg(feature = "unwind_internal")]
143    extensions::unwind_internal::initialize();
144}
145
146/// Fetches a reference to the interface.
147///
148/// Using the interface is safe, as long as a [LockToken] is constructed.
149#[inline]
150pub fn get_interface<'a>() -> &'a CBaseRef<'static> {
151    unsafe { &*INTERFACE.as_ptr() }
152}
153
154/// Fetches a mutable reference to the interface.
155///
156/// Using the interface is safe, as long as a [LockToken] is constructed.
157#[inline]
158pub fn get_mut_interface<'a>() -> &'a mut CBaseRef<'static> {
159    unsafe { &mut *INTERFACE.as_mut_ptr() }
160}