pthread_mutex.rs 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184
  1. // SPDX-License-Identifier: Apache-2.0 OR MIT
  2. // inspired by <https://github.com/nbdd0121/pin-init/blob/trunk/examples/pthread_mutex.rs>
  3. #![allow(clippy::undocumented_unsafe_blocks)]
  4. #![cfg_attr(feature = "alloc", feature(allocator_api))]
  5. #![cfg_attr(not(RUSTC_LINT_REASONS_IS_STABLE), feature(lint_reasons))]
  6. #[cfg(not(windows))]
  7. mod pthread_mtx {
  8. #[cfg(feature = "alloc")]
  9. use core::alloc::AllocError;
  10. use core::{
  11. cell::UnsafeCell,
  12. marker::PhantomPinned,
  13. mem::MaybeUninit,
  14. ops::{Deref, DerefMut},
  15. pin::Pin,
  16. };
  17. use pin_init::*;
  18. use std::convert::Infallible;
  19. #[pin_data(PinnedDrop)]
  20. pub struct PThreadMutex<T> {
  21. #[pin]
  22. raw: UnsafeCell<libc::pthread_mutex_t>,
  23. data: UnsafeCell<T>,
  24. #[pin]
  25. pin: PhantomPinned,
  26. }
  27. unsafe impl<T: Send> Send for PThreadMutex<T> {}
  28. unsafe impl<T: Send> Sync for PThreadMutex<T> {}
  29. #[pinned_drop]
  30. impl<T> PinnedDrop for PThreadMutex<T> {
  31. fn drop(self: Pin<&mut Self>) {
  32. unsafe {
  33. libc::pthread_mutex_destroy(self.raw.get());
  34. }
  35. }
  36. }
  37. #[derive(Debug)]
  38. pub enum Error {
  39. #[allow(dead_code)]
  40. IO(std::io::Error),
  41. #[allow(dead_code)]
  42. Alloc,
  43. }
  44. impl From<Infallible> for Error {
  45. fn from(e: Infallible) -> Self {
  46. match e {}
  47. }
  48. }
  49. #[cfg(feature = "alloc")]
  50. impl From<AllocError> for Error {
  51. fn from(_: AllocError) -> Self {
  52. Self::Alloc
  53. }
  54. }
  55. impl<T> PThreadMutex<T> {
  56. #[allow(dead_code)]
  57. pub fn new(data: T) -> impl PinInit<Self, Error> {
  58. fn init_raw() -> impl PinInit<UnsafeCell<libc::pthread_mutex_t>, Error> {
  59. let init = |slot: *mut UnsafeCell<libc::pthread_mutex_t>| {
  60. // we can cast, because `UnsafeCell` has the same layout as T.
  61. let slot: *mut libc::pthread_mutex_t = slot.cast();
  62. let mut attr = MaybeUninit::uninit();
  63. let attr = attr.as_mut_ptr();
  64. // SAFETY: ptr is valid
  65. let ret = unsafe { libc::pthread_mutexattr_init(attr) };
  66. if ret != 0 {
  67. return Err(Error::IO(std::io::Error::from_raw_os_error(ret)));
  68. }
  69. // SAFETY: attr is initialized
  70. let ret = unsafe {
  71. libc::pthread_mutexattr_settype(attr, libc::PTHREAD_MUTEX_NORMAL)
  72. };
  73. if ret != 0 {
  74. // SAFETY: attr is initialized
  75. unsafe { libc::pthread_mutexattr_destroy(attr) };
  76. return Err(Error::IO(std::io::Error::from_raw_os_error(ret)));
  77. }
  78. // SAFETY: slot is valid
  79. unsafe { slot.write(libc::PTHREAD_MUTEX_INITIALIZER) };
  80. // SAFETY: attr and slot are valid ptrs and attr is initialized
  81. let ret = unsafe { libc::pthread_mutex_init(slot, attr) };
  82. // SAFETY: attr was initialized
  83. unsafe { libc::pthread_mutexattr_destroy(attr) };
  84. if ret != 0 {
  85. return Err(Error::IO(std::io::Error::from_raw_os_error(ret)));
  86. }
  87. Ok(())
  88. };
  89. // SAFETY: mutex has been initialized
  90. unsafe { pin_init_from_closure(init) }
  91. }
  92. pin_init!(Self {
  93. data: UnsafeCell::new(data),
  94. raw <- init_raw(),
  95. pin: PhantomPinned,
  96. }? Error)
  97. }
  98. #[allow(dead_code)]
  99. pub fn lock(&self) -> PThreadMutexGuard<'_, T> {
  100. // SAFETY: raw is always initialized
  101. unsafe { libc::pthread_mutex_lock(self.raw.get()) };
  102. PThreadMutexGuard { mtx: self }
  103. }
  104. }
  105. pub struct PThreadMutexGuard<'a, T> {
  106. mtx: &'a PThreadMutex<T>,
  107. }
  108. impl<T> Drop for PThreadMutexGuard<'_, T> {
  109. fn drop(&mut self) {
  110. // SAFETY: raw is always initialized
  111. unsafe { libc::pthread_mutex_unlock(self.mtx.raw.get()) };
  112. }
  113. }
  114. impl<T> Deref for PThreadMutexGuard<'_, T> {
  115. type Target = T;
  116. fn deref(&self) -> &Self::Target {
  117. unsafe { &*self.mtx.data.get() }
  118. }
  119. }
  120. impl<T> DerefMut for PThreadMutexGuard<'_, T> {
  121. fn deref_mut(&mut self) -> &mut Self::Target {
  122. unsafe { &mut *self.mtx.data.get() }
  123. }
  124. }
  125. }
  126. #[cfg_attr(test, test)]
  127. #[cfg_attr(all(test, miri), ignore)]
  128. fn main() {
  129. #[cfg(all(any(feature = "std", feature = "alloc"), not(windows)))]
  130. {
  131. use core::pin::Pin;
  132. use pin_init::*;
  133. use pthread_mtx::*;
  134. use std::{
  135. sync::Arc,
  136. thread::{sleep, Builder},
  137. time::Duration,
  138. };
  139. let mtx: Pin<Arc<PThreadMutex<usize>>> = Arc::try_pin_init(PThreadMutex::new(0)).unwrap();
  140. let mut handles = vec![];
  141. let thread_count = 20;
  142. let workload = 1_000_000;
  143. for i in 0..thread_count {
  144. let mtx = mtx.clone();
  145. handles.push(
  146. Builder::new()
  147. .name(format!("worker #{i}"))
  148. .spawn(move || {
  149. for _ in 0..workload {
  150. *mtx.lock() += 1;
  151. }
  152. println!("{i} halfway");
  153. sleep(Duration::from_millis((i as u64) * 10));
  154. for _ in 0..workload {
  155. *mtx.lock() += 1;
  156. }
  157. println!("{i} finished");
  158. })
  159. .expect("should not fail"),
  160. );
  161. }
  162. for h in handles {
  163. h.join().expect("thread panicked");
  164. }
  165. println!("{:?}", &*mtx.lock());
  166. assert_eq!(*mtx.lock(), workload * thread_count * 2);
  167. }
  168. }