flexible_locks/
os.rs

1// Copyright 2018 Mike Hommey
2//
3// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
5// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
6// option. This file may not be copied, modified, or distributed
7// except according to those terms.
8
9use core::cell::UnsafeCell;
10use core::mem;
11
12#[cfg(windows)]
13use winapi::um::synchapi::{AcquireSRWLockExclusive, DeleteCriticalSection, EnterCriticalSection,
14                           InitializeCriticalSection, LeaveCriticalSection,
15                           ReleaseSRWLockExclusive};
16
17#[cfg(unix)]
18use libc;
19
20mod raw {
21    #[cfg(unix)]
22    pub use libc::pthread_mutex_t;
23    #[cfg(windows)]
24    pub use winapi::um::minwinbase::CRITICAL_SECTION;
25    #[cfg(windows)]
26    pub use winapi::um::synchapi::SRWLOCK;
27    #[cfg(any(target_os = "macos", target_os = "ios"))]
28    #[repr(C)]
29    pub struct OSSpinLock(pub i32);
30}
31
32use RawMutex;
33
34/// A trait for unsafe raw mutual exclusion primitives.
35///
36/// Types implementing this trait are meant to be wrapped with [`RawOsMutex`],
37/// bringing them an automatic [`RawMutex`] implementation.
38pub trait UnsafeRawOsMutex {
39    /// Initialize the raw mutex.
40    ///
41    /// See [`RawMutex::init`].
42    unsafe fn init(_mutex: *mut Self) {}
43
44    /// Destroy the raw mutex.
45    ///
46    /// See [`RawMutex::destroy`]
47    unsafe fn destroy(_mutex: *mut Self) {}
48
49    /// Acquire the raw mutex.
50    ///
51    /// See [`RawMutex::lock`]
52    unsafe fn lock(mutex: *mut Self);
53
54    /// Release the raw mutex.
55    ///
56    /// See [`RawMutex::unlock`]
57    unsafe fn unlock(mutex: *mut Self);
58}
59
60/// Platform mutex primitives for use with [`Mutex`] and [`MutexWrap`].
61///
62/// While the [`std::sync::Mutex`] type only uses one kind of platform mutex
63/// primitive (except on Windows, where things are a little convoluted),
64/// flexible-locks allow to use different kinds.
65///
66/// [`std::sync::Mutex`]: https://doc.rust-lang.org/std/sync/struct.Mutex.html
67///
68/// The available primitives are:
69/// - [`pthread_mutex_t`], on Unix-like systems, including macOS,
70/// - [`OSSpinLock`] on macOS,
71/// - [`SRWLock`] on Windows,
72/// - [`CRITICAL_SECTION`] on Windows.
73///
74/// Other primitives could be added in the future, such as [`os_unfair_lock_t`]
75/// on macOS.
76///
77/// [`pthread_mutex_t`]: http://pubs.opengroup.org/onlinepubs/7908799/xsh/pthread_mutex_init.html
78/// [`OSSpinLock`]: https://developer.apple.com/documentation/kernel/osspinlock
79/// [`SRWLock`]: https://msdn.microsoft.com/en-us/library/windows/desktop/aa904937(v=vs.85).aspx
80/// [`CRITICAL_SECTION`]: https://msdn.microsoft.com/en-us/library/windows/desktop/ms682530(v=vs.85).aspx
81/// [`os_unfair_lock_t`]: https://developer.apple.com/documentation/os/os_unfair_lock_t
82///
83/// For types that can be statically initialized, until `const fn` is
84/// stabilized, initializer macros are provided:
85/// - [`pthread_mutex_new`]
86/// - [`osspinlock_new`]
87/// - [`srwlock_new`]
88///
89/// For non-static initialization, `Default::default()` can be used for these.
90///
91/// [`pthread_mutex_new`]: ../x86_64-unknown-linux-gnu/flexible_locks/macro.pthread_mutex_new.html
92/// [`osspinlock_new`]: ../x86_64-apple-darwin/flexible_locks/macro.osspinlock_new.html
93/// [`srwlock_new`]: ../x86_64-pc-windows-msvc/flexible_locks/macro.srwlock_new.html
94///
95/// For more specific non default use cases, you may want to implement your own
96/// type and implement the [`RawMutex`] or [`UnsafeRawOsMutex`] trait for it.
97///
98/// ## Safety
99///
100/// Generally speaking, platform mutex primitives cannot be moved in memory.
101/// That is, they must stay at the same address. Please ensure that is the
102/// case when you use them.
103pub struct RawOsMutex<T: UnsafeRawOsMutex> {
104    #[doc(hidden)]
105    pub __inner: UnsafeCell<T>,
106}
107
108unsafe impl<T: UnsafeRawOsMutex> Send for RawOsMutex<T> {}
109unsafe impl<T: UnsafeRawOsMutex> Sync for RawOsMutex<T> {}
110
111impl<T: UnsafeRawOsMutex> RawMutex for RawOsMutex<T> {
112    unsafe fn init(&mut self) {
113        T::init(self.__inner.get());
114    }
115
116    unsafe fn lock(&self) {
117        T::lock(self.__inner.get());
118    }
119
120    unsafe fn unlock(&self) {
121        T::unlock(self.__inner.get());
122    }
123
124    unsafe fn destroy(&self) {
125        T::destroy(self.__inner.get());
126    }
127}
128
129/// Statically initializes a [`RawOsMutex`]
130///
131/// # Examples
132///
133/// ```
134/// #[macro_use]
135/// extern crate flexible_locks;
136/// use flexible_locks::{RawOsMutex, UnsafeRawOsMutex};
137///
138/// struct FakeRawMutex;
139///
140/// impl UnsafeRawOsMutex for FakeRawMutex {
141///     unsafe fn lock(mutex: *mut Self) {
142///         // Real implementation goes here.
143///     }
144///     unsafe fn unlock(mutex: *mut Self) {
145///         // Real implementation goes here.
146///     }
147/// }
148///
149/// static MUTEX: RawOsMutex<FakeRawMutex> = raw_os_mutex_new!(FakeRawMutex);
150/// # fn main() {}
151/// ```
152#[macro_export]
153macro_rules! raw_os_mutex_new {
154    ($e:expr) => {
155        $crate::RawOsMutex {
156            __inner: $crate::UnsafeCell::new($e),
157        }
158    };
159}
160
161/// [`RawOsMutex`] wrapper for `winapi::um::synchapi::SRWLOCK`.
162#[cfg(windows)]
163pub type SRWLOCK = RawOsMutex<raw::SRWLOCK>;
164
165#[cfg(windows)]
166impl UnsafeRawOsMutex for raw::SRWLOCK {
167    #[inline]
168    unsafe fn lock(mutex: *mut Self) {
169        AcquireSRWLockExclusive(mutex);
170    }
171
172    #[inline]
173    unsafe fn unlock(mutex: *mut Self) {
174        ReleaseSRWLockExclusive(mutex);
175    }
176}
177
178#[cfg(windows)]
179#[doc(hidden)]
180pub use winapi::um::synchapi::SRWLOCK_INIT;
181
182/// Statically initializes a [`SRWLOCK`]
183///
184/// # Examples
185///
186/// ```
187/// #[macro_use]
188/// extern crate flexible_locks;
189/// use flexible_locks::SRWLOCK;
190/// static MUTEX: SRWLOCK = srwlock_new!();
191/// # fn main() {}
192/// ```
193#[cfg(windows)]
194#[macro_export]
195macro_rules! srwlock_new {
196    () => {
197        raw_os_mutex_new!($crate::SRWLOCK_INIT)
198    };
199}
200
201#[cfg(windows)]
202impl Default for SRWLOCK {
203    #[inline]
204    fn default() -> Self {
205        srwlock_new!()
206    }
207}
208
209/// [`RawOsMutex`] wrapper for `winapi::um::minwinbase::CRITICAL_SECTION`.
210#[cfg(windows)]
211#[allow(non_camel_case_types)]
212pub type CRITICAL_SECTION = RawOsMutex<raw::CRITICAL_SECTION>;
213
214#[cfg(windows)]
215impl UnsafeRawOsMutex for raw::CRITICAL_SECTION {
216    #[inline]
217    unsafe fn init(mutex: *mut Self) {
218        InitializeCriticalSection(mutex);
219    }
220
221    #[inline]
222    unsafe fn lock(mutex: *mut Self) {
223        EnterCriticalSection(mutex);
224    }
225
226    #[inline]
227    unsafe fn unlock(mutex: *mut Self) {
228        LeaveCriticalSection(mutex);
229    }
230
231    #[inline]
232    unsafe fn destroy(mutex: *mut Self) {
233        DeleteCriticalSection(mutex);
234    }
235}
236
237#[cfg(windows)]
238impl Default for CRITICAL_SECTION {
239    #[inline]
240    fn default() -> Self {
241        unsafe { mem::uninitialized() }
242    }
243}
244
245#[cfg(unix)]
246#[doc(hidden)]
247pub use libc::PTHREAD_MUTEX_INITIALIZER;
248
249/// [`RawOsMutex`] wrapper for `libc::pthread_mutex_t`.
250#[cfg(unix)]
251#[allow(non_camel_case_types)]
252pub type pthread_mutex_t = RawOsMutex<raw::pthread_mutex_t>;
253
254#[cfg(unix)]
255impl UnsafeRawOsMutex for raw::pthread_mutex_t {
256    unsafe fn init(mutex: *mut Self) {
257        let mut attr: libc::pthread_mutexattr_t = mem::uninitialized();
258        let r = libc::pthread_mutexattr_init(&mut attr);
259        debug_assert_eq!(r, 0);
260        let r = libc::pthread_mutexattr_settype(&mut attr, libc::PTHREAD_MUTEX_NORMAL);
261        debug_assert_eq!(r, 0);
262        let r = libc::pthread_mutex_init(mutex, &attr);
263        debug_assert_eq!(r, 0);
264        let r = libc::pthread_mutexattr_destroy(&mut attr);
265        debug_assert_eq!(r, 0);
266    }
267
268    #[inline]
269    unsafe fn lock(mutex: *mut Self) {
270        libc::pthread_mutex_lock(mutex);
271    }
272
273    #[inline]
274    unsafe fn unlock(mutex: *mut Self) {
275        libc::pthread_mutex_unlock(mutex);
276    }
277
278    #[inline]
279    unsafe fn destroy(mutex: *mut Self) {
280        libc::pthread_mutex_destroy(mutex);
281    }
282}
283
284/// Statically initializes a [`pthread_mutex_t`]
285///
286/// # Examples
287///
288/// ```
289/// #[macro_use]
290/// extern crate flexible_locks;
291/// use flexible_locks::pthread_mutex_t;
292/// static MUTEX: pthread_mutex_t = pthread_mutex_new!();
293/// # fn main() {}
294/// ```
295#[cfg(unix)]
296#[macro_export]
297macro_rules! pthread_mutex_new {
298    () => {
299        raw_os_mutex_new!($crate::PTHREAD_MUTEX_INITIALIZER)
300    };
301    ($e:expr) => {
302        raw_os_mutex_new!($e)
303    };
304}
305
306#[cfg(unix)]
307impl Default for pthread_mutex_t {
308    #[inline]
309    fn default() -> Self {
310        pthread_mutex_new!()
311    }
312}
313
314#[cfg(any(target_os = "macos", target_os = "ios"))]
315#[doc(hidden)]
316pub const OS_SPINLOCK_INIT: raw::OSSpinLock = raw::OSSpinLock(0);
317
318#[cfg(any(target_os = "macos", target_os = "ios"))]
319#[link(name = "System")]
320extern "C" {
321    fn OSSpinLockLock(lock: *mut raw::OSSpinLock);
322    fn OSSpinLockUnlock(lock: *mut raw::OSSpinLock);
323}
324
325/// [`RawOsMutex`] wrapper for `OSSpinLock`.
326#[cfg(any(target_os = "macos", target_os = "ios"))]
327pub type OSSpinLock = RawOsMutex<raw::OSSpinLock>;
328
329#[cfg(any(target_os = "macos", target_os = "ios"))]
330impl UnsafeRawOsMutex for raw::OSSpinLock {
331    #[inline]
332    unsafe fn lock(mutex: *mut Self) {
333        OSSpinLockLock(mutex);
334    }
335
336    #[inline]
337    unsafe fn unlock(mutex: *mut Self) {
338        OSSpinLockUnlock(mutex);
339    }
340}
341
342/// Statically initializes a [`OSSpinLock`]
343///
344/// # Examples
345///
346/// ```
347/// #[macro_use]
348/// extern crate flexible_locks;
349/// use flexible_locks::OSSpinLock;
350/// static MUTEX: OSSpinLock = osspinlock_new!();
351/// # fn main() {}
352/// ```
353#[cfg(any(target_os = "macos", target_os = "ios"))]
354#[macro_export]
355macro_rules! osspinlock_new {
356    () => {
357        raw_os_mutex_new!($crate::OS_SPINLOCK_INIT)
358    };
359}
360
361#[cfg(any(target_os = "macos", target_os = "ios"))]
362impl Default for OSSpinLock {
363    #[inline]
364    fn default() -> Self {
365        osspinlock_new!()
366    }
367}