Skip to main content

std/sys/helpers/
c_opaque.rs

1use crate::mem::MaybeUninit;
2use crate::pin::{Pin, UnsafePinned};
3
4/// A wrapper for an opaque C object.
5///
6/// Some libraries like UNIX's pthread have data types that must be treated
7/// as entirely opaque. Soundly wrapping these types is very hard since
8/// Rust's operational semantics are much stricter when it comes to e.g. the
9/// initialization state of data types and pointer aliasing. For instance, a
10/// function like `pthread_mutexattr_init` might not fully initialize the
11/// `libc::pthread_mutexattr_t` passed to it, so doing e.g.
12/// ```ignore (for-illustration-purposes-only)
13/// let mut attr = MaybeUninit::uninit();
14/// pthread_mutexattr_init(attr.as_mut_ptr());
15/// let attr = attr.assume_init();
16/// ```
17/// is unsound. Another example: on platforms like macOS a `pthread_mutex_t`
18/// cannot be moved because the implementation will dynamically align some inner
19/// fields to a higher alignment than required by the definition. And furthermore,
20/// some implementations (e.g. AIX) of `pthread_cond_t` use intrinsically linked
21/// lists, and hence doing
22/// ```ignore (for-illustration-purposes-only)
23/// pub struct Condvar(UnsafeCell<libc::pthread_cont_t>);
24///
25/// /* initialization and usage omitted for brevity */
26///
27/// impl Drop for Condvar {
28///     fn drop(&mut self) {
29///         unsafe { libc::pthread_cond_destroy(self.0.get()) };
30///     }
31/// }
32/// ```
33/// results in undefined behaviour (even when utilizing `Pin` to ensure
34/// immovability) because the creation of the `&mut Condvar` passed to `drop`
35/// invalidates other pointers in the linked list.
36///
37/// `COpaque` helps with avoiding all these caveats:
38/// * it wraps the inner value in `MaybeUninit` and thus is entirely oblivious
39///   of its initialization state.
40/// * [`COpaque::get`] takes a `Pin` and thus prevents accidental moves.
41/// * it utilizes `UnsafePinned` to relax the aliasing guarantees of mutable
42///   references to the `COpaque`.
43///
44/// The only way to access the inner value is via [`COpaque::get`]. It returns
45/// a pointer which should be directly passed to the platform functions.
46///
47/// In effect, a pinned instance of this wrapper acts very much like a C variable.
48pub struct COpaque<T> {
49    inner: UnsafePinned<MaybeUninit<T>>,
50}
51
52impl<T> COpaque<T> {
53    /// Creates an uninitialized C-like storage for `T`.
54    ///
55    /// If you'd write
56    /// ```c
57    /// T var;
58    /// ```
59    /// in C, the equivalent Rust code is
60    /// ```ignore (for-illustration-purposes-only)
61    /// let var = pin!(COpaque::uninit());
62    /// ```
63    pub fn uninit() -> COpaque<T> {
64        COpaque { inner: UnsafePinned::new(MaybeUninit::uninit()) }
65    }
66
67    /// Creates a zero-initialized C-like storage for `T`.
68    ///
69    /// If you'd write
70    /// ```c
71    /// T var = {};
72    /// ```
73    /// in C, the equivalent Rust code is
74    /// ```ignore (for-illustration-purposes-only)
75    /// let var = pin!(COpaque::zeroed());
76    /// ```
77    pub fn zeroed() -> COpaque<T> {
78        COpaque { inner: UnsafePinned::new(MaybeUninit::zeroed()) }
79    }
80
81    /// Creates a pre-initialized C-like storage for `T`.
82    ///
83    /// If you'd write
84    /// ```c
85    /// T var = T_INITIALIZER;
86    /// ```
87    /// in C, the equivalent Rust code is
88    /// ```ignore (for-illustration-purposes-only)
89    /// let var = pin!(COpaque::new(T_INITIALIZER));
90    /// ```
91    pub fn new(initializer: T) -> COpaque<T> {
92        COpaque { inner: UnsafePinned::new(MaybeUninit::new(initializer)) }
93    }
94
95    /// Gets a pointer to the value.
96    ///
97    /// Use this as a replacement for C's ampersand operator.
98    pub fn get(self: Pin<&Self>) -> *mut T {
99        self.inner.get().cast_init()
100    }
101}