1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
//! Global api that can be used instead of the local api.
//!
//! The global api is the preferred way of interfacing with the interface.
use crate::ffi::{
    collections::NonNullConst,
    sys::api::{GetFunctionFn as GetFunctionFnFFI, SysBinding},
    Bool, CBase as CBaseFFI,
};
use crate::init::CBaseAPILoader;
use crate::{CBase, CBaseInterfaceInfo, CBaseRef};
use std::mem::MaybeUninit;
use std::ptr::NonNull;

pub mod library;
pub mod module;
pub mod sys;
pub mod version;

#[cfg(feature = "extensions")]
pub mod extensions;

static mut INTERFACE: MaybeUninit<CBaseRef<'static>> = MaybeUninit::uninit();

/// Type indicating that dropping unlocks the interface.
#[derive(Debug)]
pub struct Unlock {}

/// Type indicating that dropping does not unlock the interface.
#[derive(Debug)]
pub struct ForgetUnlock {}

impl Drop for Unlock {
    fn drop(&mut self) {
        unsafe { SysBinding::unlock(get_interface()) }
    }
}

/// A token indicating a locked interface.
#[derive(Debug)]
pub struct LockToken<T> {
    _phantom: T,
}

impl LockToken<Unlock> {
    /// Takes ownership of the token and exchanges it with
    /// a token which does not unlock the interface.
    ///
    /// # Safety
    ///
    /// Improper usage can leave the interface in a locked state.
    pub unsafe fn relinquish_locking(self) -> LockToken<ForgetUnlock> {
        std::mem::forget(self);
        LockToken {
            _phantom: ForgetUnlock {},
        }
    }
}

impl LockToken<ForgetUnlock> {
    /// Takes ownership of the token and exchanges it with
    /// a token which unlocks the interface.
    ///
    /// # Safety
    ///
    /// Improper usage can unlock the interface multiple times.
    pub unsafe fn take_ownership(self) -> LockToken<Unlock> {
        std::mem::forget(self);
        LockToken {
            _phantom: Unlock {},
        }
    }
}

impl<T> LockToken<T> {
    /// Constructs a new token by locking the interface.
    ///
    /// The calling thread is stalled until the lock can be acquired.
    /// Only one thread can hold the lock at a time.
    ///
    /// # Return
    ///
    /// A token.
    #[inline]
    #[must_use]
    pub fn lock() -> LockToken<Unlock> {
        unsafe {
            SysBinding::lock(get_interface());
            LockToken {
                _phantom: Unlock {},
            }
        }
    }

    /// Tries to lock the interface.
    ///
    /// The function fails if another thread already holds the lock.
    ///
    /// # Return
    ///
    /// [LockToken] on success and [None] otherwise.
    #[inline]
    #[must_use]
    pub fn try_lock() -> Option<LockToken<Unlock>> {
        unsafe {
            match SysBinding::try_lock(get_interface()) {
                Bool::False => None,
                Bool::True => Some(LockToken {
                    _phantom: Unlock {},
                }),
            }
        }
    }

    /// Constructs a new token without locking.
    ///
    /// # Return
    ///
    /// A token.
    ///
    /// # Safety
    ///
    /// Most of the interface assumes that the caller has unique access to the interface.
    /// This function can be used to bypass this restriction, if the user can guarantee
    /// that no data-races will occur.
    #[inline]
    pub unsafe fn assume_locked() -> LockToken<ForgetUnlock> {
        LockToken {
            _phantom: ForgetUnlock {},
        }
    }
}

/// Initializes the interface.
#[inline]
pub fn initialize(base_module: Option<NonNull<CBaseFFI>>, get_function_fn: GetFunctionFnFFI) {
    unsafe {
        INTERFACE = MaybeUninit::new(CBaseRef::new(NonNullConst::from(
            CBase::fetch_interface(base_module, get_function_fn).internal_interface(),
        )));
    }

    #[cfg(feature = "unwind_internal")]
    extensions::unwind_internal::initialize();
}

/// Fetches a reference to the interface.
///
/// Using the interface is safe, as long as a [LockToken] is constructed.
#[inline]
pub fn get_interface<'a>() -> &'a CBaseRef<'static> {
    unsafe { &*INTERFACE.as_ptr() }
}

/// Fetches a mutable reference to the interface.
///
/// Using the interface is safe, as long as a [LockToken] is constructed.
#[inline]
pub fn get_mut_interface<'a>() -> &'a mut CBaseRef<'static> {
    unsafe { &mut *INTERFACE.as_mut_ptr() }
}