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}