| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184 |
- // SPDX-License-Identifier: Apache-2.0 OR MIT
- // inspired by <https://github.com/nbdd0121/pin-init/blob/trunk/examples/pthread_mutex.rs>
- #![allow(clippy::undocumented_unsafe_blocks)]
- #![cfg_attr(feature = "alloc", feature(allocator_api))]
- #![cfg_attr(not(RUSTC_LINT_REASONS_IS_STABLE), feature(lint_reasons))]
- #[cfg(not(windows))]
- mod pthread_mtx {
- #[cfg(feature = "alloc")]
- use core::alloc::AllocError;
- use core::{
- cell::UnsafeCell,
- marker::PhantomPinned,
- mem::MaybeUninit,
- ops::{Deref, DerefMut},
- pin::Pin,
- };
- use pin_init::*;
- use std::convert::Infallible;
- #[pin_data(PinnedDrop)]
- pub struct PThreadMutex<T> {
- #[pin]
- raw: UnsafeCell<libc::pthread_mutex_t>,
- data: UnsafeCell<T>,
- #[pin]
- pin: PhantomPinned,
- }
- unsafe impl<T: Send> Send for PThreadMutex<T> {}
- unsafe impl<T: Send> Sync for PThreadMutex<T> {}
- #[pinned_drop]
- impl<T> PinnedDrop for PThreadMutex<T> {
- fn drop(self: Pin<&mut Self>) {
- unsafe {
- libc::pthread_mutex_destroy(self.raw.get());
- }
- }
- }
- #[derive(Debug)]
- pub enum Error {
- #[allow(dead_code)]
- IO(std::io::Error),
- #[allow(dead_code)]
- Alloc,
- }
- impl From<Infallible> for Error {
- fn from(e: Infallible) -> Self {
- match e {}
- }
- }
- #[cfg(feature = "alloc")]
- impl From<AllocError> for Error {
- fn from(_: AllocError) -> Self {
- Self::Alloc
- }
- }
- impl<T> PThreadMutex<T> {
- #[allow(dead_code)]
- pub fn new(data: T) -> impl PinInit<Self, Error> {
- fn init_raw() -> impl PinInit<UnsafeCell<libc::pthread_mutex_t>, Error> {
- let init = |slot: *mut UnsafeCell<libc::pthread_mutex_t>| {
- // we can cast, because `UnsafeCell` has the same layout as T.
- let slot: *mut libc::pthread_mutex_t = slot.cast();
- let mut attr = MaybeUninit::uninit();
- let attr = attr.as_mut_ptr();
- // SAFETY: ptr is valid
- let ret = unsafe { libc::pthread_mutexattr_init(attr) };
- if ret != 0 {
- return Err(Error::IO(std::io::Error::from_raw_os_error(ret)));
- }
- // SAFETY: attr is initialized
- let ret = unsafe {
- libc::pthread_mutexattr_settype(attr, libc::PTHREAD_MUTEX_NORMAL)
- };
- if ret != 0 {
- // SAFETY: attr is initialized
- unsafe { libc::pthread_mutexattr_destroy(attr) };
- return Err(Error::IO(std::io::Error::from_raw_os_error(ret)));
- }
- // SAFETY: slot is valid
- unsafe { slot.write(libc::PTHREAD_MUTEX_INITIALIZER) };
- // SAFETY: attr and slot are valid ptrs and attr is initialized
- let ret = unsafe { libc::pthread_mutex_init(slot, attr) };
- // SAFETY: attr was initialized
- unsafe { libc::pthread_mutexattr_destroy(attr) };
- if ret != 0 {
- return Err(Error::IO(std::io::Error::from_raw_os_error(ret)));
- }
- Ok(())
- };
- // SAFETY: mutex has been initialized
- unsafe { pin_init_from_closure(init) }
- }
- pin_init!(Self {
- data: UnsafeCell::new(data),
- raw <- init_raw(),
- pin: PhantomPinned,
- }? Error)
- }
- #[allow(dead_code)]
- pub fn lock(&self) -> PThreadMutexGuard<'_, T> {
- // SAFETY: raw is always initialized
- unsafe { libc::pthread_mutex_lock(self.raw.get()) };
- PThreadMutexGuard { mtx: self }
- }
- }
- pub struct PThreadMutexGuard<'a, T> {
- mtx: &'a PThreadMutex<T>,
- }
- impl<T> Drop for PThreadMutexGuard<'_, T> {
- fn drop(&mut self) {
- // SAFETY: raw is always initialized
- unsafe { libc::pthread_mutex_unlock(self.mtx.raw.get()) };
- }
- }
- impl<T> Deref for PThreadMutexGuard<'_, T> {
- type Target = T;
- fn deref(&self) -> &Self::Target {
- unsafe { &*self.mtx.data.get() }
- }
- }
- impl<T> DerefMut for PThreadMutexGuard<'_, T> {
- fn deref_mut(&mut self) -> &mut Self::Target {
- unsafe { &mut *self.mtx.data.get() }
- }
- }
- }
- #[cfg_attr(test, test)]
- #[cfg_attr(all(test, miri), ignore)]
- fn main() {
- #[cfg(all(any(feature = "std", feature = "alloc"), not(windows)))]
- {
- use core::pin::Pin;
- use pin_init::*;
- use pthread_mtx::*;
- use std::{
- sync::Arc,
- thread::{sleep, Builder},
- time::Duration,
- };
- let mtx: Pin<Arc<PThreadMutex<usize>>> = Arc::try_pin_init(PThreadMutex::new(0)).unwrap();
- let mut handles = vec![];
- let thread_count = 20;
- let workload = 1_000_000;
- for i in 0..thread_count {
- let mtx = mtx.clone();
- handles.push(
- Builder::new()
- .name(format!("worker #{i}"))
- .spawn(move || {
- for _ in 0..workload {
- *mtx.lock() += 1;
- }
- println!("{i} halfway");
- sleep(Duration::from_millis((i as u64) * 10));
- for _ in 0..workload {
- *mtx.lock() += 1;
- }
- println!("{i} finished");
- })
- .expect("should not fail"),
- );
- }
- for h in handles {
- h.join().expect("thread panicked");
- }
- println!("{:?}", &*mtx.lock());
- assert_eq!(*mtx.lock(), workload * thread_count * 2);
- }
- }
|