posix_sync/mutex/mod.rs
1//! Rust wrappers around POSIX mutexes.
2//!
3//! This module exposes two kinds of mutexes: [`OwnedMutex`] and [`BorrowedMutex`].
4//! Both wrap an underlying `libc::pthread_mutex_t`, the mutex object POSIX defines, but differ
5//! in the following ways:
6//!
7//! | | **`OwnedMutex`** | **`BorrowedMutex`** |
8//! |------------------------------------|------------------|---------------------|
9//! | Implements `Copy`? | No | Yes |
10//! | Destroys underlying mutex on drop? | When unlocked | Never |
11//! | Can be used for shared memory IPC? | No | Yes |
12//! | Can be used without `unsafe`? | Yes | No |
13//!
14//! Neither poisons, which makes them `!UnwindSafe` and `!RefUnwindSafe`. Shared memory IPC
15//! additionally needs the mutex to be built with `with_sharing(MutexSharing::Shared)`, an
16//! option not every platform implements: see [Platform support](#platform-support).
17//!
18//! Both kinds are generic over a [robustness marker](robustness_markers), which decides what
19//! happens when the owner of a lock dies without unlocking it. The robustness marker `Standard`
20//! leaves everyone else blocked forever. The marker `Robust` hands the next owner an
21//! indeterminate [guard](guards) instead, giving the new owner the opportunity to repair whatever
22//! the dead one left behind.
23//!
24//! In POSIX terms, the markers are the two values the robustness attribute can take, both set
25//! through `pthread_mutexattr_setrobust`: `Standard` corresponds to `PTHREAD_MUTEX_STALLED`, the
26//! default, and `Robust` to `PTHREAD_MUTEX_ROBUST`.
27//!
28//! Mutexes are constructed with [`MutexBuilder`]:
29//!
30//! ```
31//! use posix_sync::mutex::{MutexBuilder, MutexType, robustness_markers::Standard};
32//!
33//! let mtx = MutexBuilder::<Standard>::new()
34//! .with_type(MutexType::ErrorCheck)
35//! .build_owned();
36//!
37//! let guard = mtx.lock()?;
38//! // ... critical section ...
39//! drop(guard);
40//! # Ok::<(), posix_sync::mutex::MutexLockError>(())
41//! ```
42//!
43//! The next example puts a robust, process-shared mutex at some memory mapped region with
44//! [`build_borrowed`](MutexBuilder::build_borrowed). We use the
45//! [`memmap2`](https://crates.io/crates/memmap2) crate to create the mapping, though any
46//! RAII wrapper would work.
47//!
48//! ```
49//! # #[cfg(any(target_os = "linux", target_os = "freebsd"))]
50//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
51//! use posix_sync::mutex::{
52//! MutexBuilder, MutexSharing, RawMutexAlloc,
53//! guards::RobustGuardContainer,
54//! robustness_markers::Robust,
55//! };
56//!
57//! // A shared, page-aligned mapping that another process could map as well.
58//! let file = tempfile::tempfile()?;
59//! file.set_len(RawMutexAlloc::SIZE as u64)?;
60//! let map = memmap2::MmapRaw::map_raw(&file)?;
61//!
62//! let mtx = unsafe {
63//! MutexBuilder::<Robust>::new()
64//! .with_sharing(MutexSharing::Shared)
65//! .build_borrowed(map.as_mut_ptr() as *mut RawMutexAlloc, &map)
66//! };
67//!
68//! match unsafe { mtx.lock()? } {
69//! RobustGuardContainer::Standard(_guard) => {
70//! // ... critical section ...
71//! }
72//! RobustGuardContainer::Indeterminate(guard) => {
73//! // The last owner died holding the lock. Repair whatever it was protecting, and only
74//! // then say so: until somebody does, every later lock fails with NotRecoverable.
75//! let _guard = guard.make_consistent()?;
76//! }
77//! }
78//!
79//! unsafe { mtx.destroy() };
80//! # Ok(())
81//! # }
82//! # #[cfg(not(any(target_os = "linux", target_os = "freebsd")))]
83//! # fn main() {}
84//! ```
85//!
86//! If the mutex was initialised by another process, use [`BorrowedMutex::from_raw`] instead of
87//! the builder:
88//!
89//! ```no_run
90//! use posix_sync::mutex::{BorrowedMutex, RawMutexAlloc, robustness_markers::Standard};
91//!
92//! // A mapping of the file in which another process already initialised a mutex.
93//! let file = std::fs::OpenOptions::new()
94//! .read(true)
95//! .write(true)
96//! .open("mutex.shared")?;
97//! let map = memmap2::MmapRaw::map_raw(&file)?;
98//!
99//! // The marker has to match the robustness the initialising process picked; assume it picked
100//! // Standard.
101//! let mtx: BorrowedMutex<Standard> = unsafe {
102//! BorrowedMutex::from_raw(map.as_mut_ptr() as *mut RawMutexAlloc, &map)
103//! };
104//!
105//! drop(unsafe { mtx.lock()? });
106//! # Ok::<(), Box<dyn std::error::Error>>(())
107//! ```
108//!
109//! When you are setting up a shared mapping that carries some state, the mutex guarding that
110//! state, and perhaps a condvar to wait on it with, [`RawMutexAlloc::SIZE`] bytes need to be
111//! reserved for the mutex, at an offset aligned to [`RawMutexAlloc::ALIGN`].
112//!
113//! # Platform support
114//!
115//! Process sharing, robust mutexes, timed locking, the `Inherit` protocol and priority ceilings
116//! are not available everywhere. See the table at the [crate root](crate#platform-support).
117
118use std::marker::PhantomPinned;
119use std::mem::{align_of, size_of};
120
121use libc::pthread_mutex_t;
122
123use crate::utils::AsRawUnderlying;
124
125mod errors;
126pub use errors::*;
127
128mod owned;
129pub use owned::*;
130
131mod borrowed;
132pub use borrowed::*;
133
134pub mod guards;
135
136pub mod robustness_markers;
137
138pub mod builders;
139pub use builders::{MutexBuilder, MutexProtocol, MutexType};
140
141#[cfg(not(any(target_os = "dragonfly", target_os = "netbsd", target_os = "openbsd")))]
142pub use builders::MutexSharing;
143
144/// Cast to a pointer of this type when constructing a mutex from a raw pointer.
145///
146/// This is a `pthread_mutex_t` that has been made `!Unpin`, because a mutex must not be relocated
147/// once it has been initialised: waiters and the kernel both refer to it by address. The two
148/// associated constants describe how much room to leave for one when carving up a shared mapping
149/// by hand.
150#[repr(transparent)]
151pub struct RawMutexAlloc {
152 // the storage itself. it is only ever touched through a `*mut pthread_mutex_t`.
153 #[allow(dead_code)]
154 raw: pthread_mutex_t,
155
156 /// This field is here because [`pthread_mutex_t`] is `Unpin`.
157 _phantom: PhantomPinned,
158}
159
160impl RawMutexAlloc {
161 /// The size, in bytes, of the underlying `pthread_mutex_t`.
162 pub const SIZE: usize = size_of::<pthread_mutex_t>();
163
164 /// The alignment a `*mut RawMutexAlloc` must satisfy.
165 pub const ALIGN: usize = align_of::<pthread_mutex_t>();
166}