blob: 10d4d20e8518d2fe2ea71ccb92d8b68c98d1fd55 [file] [edit]
use crate::mem::MaybeUninit;
use crate::pin::{Pin, UnsafePinned};
/// A wrapper for an opaque C object.
///
/// Some libraries like UNIX's pthread have data types that must be treated
/// as entirely opaque. Soundly wrapping these types is very hard since
/// Rust's operational semantics are much stricter when it comes to e.g. the
/// initialization state of data types and pointer aliasing. For instance, a
/// function like `pthread_mutexattr_init` might not fully initialize the
/// `libc::pthread_mutexattr_t` passed to it, so doing e.g.
/// ```ignore (for-illustration-purposes-only)
/// let mut attr = MaybeUninit::uninit();
/// pthread_mutexattr_init(attr.as_mut_ptr());
/// let attr = attr.assume_init();
/// ```
/// is unsound. Another example: on platforms like macOS a `pthread_mutex_t`
/// cannot be moved because the implementation will dynamically align some inner
/// fields to a higher alignment than required by the definition. And furthermore,
/// some implementations (e.g. AIX) of `pthread_cond_t` use intrinsically linked
/// lists, and hence doing
/// ```ignore (for-illustration-purposes-only)
/// pub struct Condvar(UnsafeCell<libc::pthread_cont_t>);
///
/// /* initialization and usage omitted for brevity */
///
/// impl Drop for Condvar {
/// fn drop(&mut self) {
/// unsafe { libc::pthread_cond_destroy(self.0.get()) };
/// }
/// }
/// ```
/// results in undefined behaviour (even when utilizing `Pin` to ensure
/// immovability) because the creation of the `&mut Condvar` passed to `drop`
/// invalidates other pointers in the linked list.
///
/// `COpaque` helps with avoiding all these caveats:
/// * it wraps the inner value in `MaybeUninit` and thus is entirely oblivious
/// of its initialization state.
/// * [`COpaque::get`] takes a `Pin` and thus prevents accidental moves.
/// * it utilizes `UnsafePinned` to relax the aliasing guarantees of mutable
/// references to the `COpaque`.
///
/// The only way to access the inner value is via [`COpaque::get`]. It returns
/// a pointer which should be directly passed to the platform functions.
///
/// In effect, a pinned instance of this wrapper acts very much like a C variable.
pub struct COpaque<T> {
inner: UnsafePinned<MaybeUninit<T>>,
}
impl<T> COpaque<T> {
/// Creates an uninitialized C-like storage for `T`.
///
/// If you'd write
/// ```c
/// T var;
/// ```
/// in C, the equivalent Rust code is
/// ```ignore (for-illustration-purposes-only)
/// let var = pin!(COpaque::uninit());
/// ```
pub fn uninit() -> COpaque<T> {
COpaque { inner: UnsafePinned::new(MaybeUninit::uninit()) }
}
/// Creates a zero-initialized C-like storage for `T`.
///
/// If you'd write
/// ```c
/// T var = {};
/// ```
/// in C, the equivalent Rust code is
/// ```ignore (for-illustration-purposes-only)
/// let var = pin!(COpaque::zeroed());
/// ```
pub fn zeroed() -> COpaque<T> {
COpaque { inner: UnsafePinned::new(MaybeUninit::zeroed()) }
}
/// Creates a pre-initialized C-like storage for `T`.
///
/// If you'd write
/// ```c
/// T var = T_INITIALIZER;
/// ```
/// in C, the equivalent Rust code is
/// ```ignore (for-illustration-purposes-only)
/// let var = pin!(COpaque::new(T_INITIALIZER));
/// ```
pub fn new(initializer: T) -> COpaque<T> {
COpaque { inner: UnsafePinned::new(MaybeUninit::new(initializer)) }
}
/// Gets a pointer to the value.
///
/// Use this as a replacement for C's ampersand operator.
pub fn get(self: Pin<&Self>) -> *mut T {
self.inner.get().cast_init()
}
}