11//@ignore-target-windows: No libc on Windows
2+ // We use `yield` to test specific interleavings, so disable automatic preemption.
3+ //@compile-flags: -Zmiri-preemption-rate=0
4+ #![feature(sync_unsafe_cell)]
5+
6+ use std::cell::SyncUnsafeCell;
7+ use std::thread;
8+ use std::{mem, ptr};
29
310fn main() {
411 test_mutex_libc_init_recursive();
512 test_mutex_libc_init_normal();
613 test_mutex_libc_init_errorcheck();
714 test_rwlock_libc_static_initializer();
8-
915 #[cfg(target_os = "linux")]
1016 test_mutex_libc_static_initializer_recursive();
17+
18+ test_mutex();
19+ check_rwlock_write();
20+ check_rwlock_read_no_deadlock();
1121}
1222
1323fn test_mutex_libc_init_recursive() {
1424 unsafe {
15- let mut attr: libc::pthread_mutexattr_t = std:: mem::zeroed();
25+ let mut attr: libc::pthread_mutexattr_t = mem::zeroed();
1626 assert_eq!(libc::pthread_mutexattr_init(&mut attr as *mut _), 0);
1727 assert_eq!(
1828 libc::pthread_mutexattr_settype(&mut attr as *mut _, libc::PTHREAD_MUTEX_RECURSIVE),
1929 0,
2030 );
21- let mut mutex: libc::pthread_mutex_t = std:: mem::zeroed();
31+ let mut mutex: libc::pthread_mutex_t = mem::zeroed();
2232 assert_eq!(libc::pthread_mutex_init(&mut mutex as *mut _, &mut attr as *mut _), 0);
2333 assert_eq!(libc::pthread_mutex_lock(&mut mutex as *mut _), 0);
2434 assert_eq!(libc::pthread_mutex_trylock(&mut mutex as *mut _), 0);
@@ -36,7 +46,7 @@ fn test_mutex_libc_init_recursive() {
3646
3747fn test_mutex_libc_init_normal() {
3848 unsafe {
39- let mut mutexattr: libc::pthread_mutexattr_t = std:: mem::zeroed();
49+ let mut mutexattr: libc::pthread_mutexattr_t = mem::zeroed();
4050 assert_eq!(
4151 libc::pthread_mutexattr_settype(&mut mutexattr as *mut _, 0x12345678),
4252 libc::EINVAL,
@@ -45,7 +55,7 @@ fn test_mutex_libc_init_normal() {
4555 libc::pthread_mutexattr_settype(&mut mutexattr as *mut _, libc::PTHREAD_MUTEX_NORMAL),
4656 0,
4757 );
48- let mut mutex: libc::pthread_mutex_t = std:: mem::zeroed();
58+ let mut mutex: libc::pthread_mutex_t = mem::zeroed();
4959 assert_eq!(libc::pthread_mutex_init(&mut mutex as *mut _, &mutexattr as *const _), 0);
5060 assert_eq!(libc::pthread_mutex_lock(&mut mutex as *mut _), 0);
5161 assert_eq!(libc::pthread_mutex_trylock(&mut mutex as *mut _), libc::EBUSY);
@@ -58,15 +68,15 @@ fn test_mutex_libc_init_normal() {
5868
5969fn test_mutex_libc_init_errorcheck() {
6070 unsafe {
61- let mut mutexattr: libc::pthread_mutexattr_t = std:: mem::zeroed();
71+ let mut mutexattr: libc::pthread_mutexattr_t = mem::zeroed();
6272 assert_eq!(
6373 libc::pthread_mutexattr_settype(
6474 &mut mutexattr as *mut _,
6575 libc::PTHREAD_MUTEX_ERRORCHECK,
6676 ),
6777 0,
6878 );
69- let mut mutex: libc::pthread_mutex_t = std:: mem::zeroed();
79+ let mut mutex: libc::pthread_mutex_t = mem::zeroed();
7080 assert_eq!(libc::pthread_mutex_init(&mut mutex as *mut _, &mutexattr as *const _), 0);
7181 assert_eq!(libc::pthread_mutex_lock(&mut mutex as *mut _), 0);
7282 assert_eq!(libc::pthread_mutex_trylock(&mut mutex as *mut _), libc::EBUSY);
@@ -98,6 +108,111 @@ fn test_mutex_libc_static_initializer_recursive() {
98108 }
99109}
100110
111+ struct SendPtr<T> {
112+ ptr: *mut T,
113+ }
114+ unsafe impl<T> Send for SendPtr<T> {}
115+ impl<T> Copy for SendPtr<T> {}
116+ impl<T> Clone for SendPtr<T> {
117+ fn clone(&self) -> Self {
118+ *self
119+ }
120+ }
121+
122+ fn test_mutex() {
123+ // Specifically *not* using `Arc` to make sure there is no synchronization apart from the mutex.
124+ unsafe {
125+ let data = SyncUnsafeCell::new((libc::PTHREAD_MUTEX_INITIALIZER, 0));
126+ let ptr = SendPtr { ptr: data.get() };
127+ let mut threads = Vec::new();
128+
129+ for _ in 0..3 {
130+ let thread = thread::spawn(move || {
131+ let ptr = ptr; // circumvent per-field closure capture
132+ let mutexptr = ptr::addr_of_mut!((*ptr.ptr).0);
133+ assert_eq!(libc::pthread_mutex_lock(mutexptr), 0);
134+ thread::yield_now();
135+ (*ptr.ptr).1 += 1;
136+ assert_eq!(libc::pthread_mutex_unlock(mutexptr), 0);
137+ });
138+ threads.push(thread);
139+ }
140+
141+ for thread in threads {
142+ thread.join().unwrap();
143+ }
144+
145+ let mutexptr = ptr::addr_of_mut!((*ptr.ptr).0);
146+ assert_eq!(libc::pthread_mutex_trylock(mutexptr), 0);
147+ assert_eq!((*ptr.ptr).1, 3);
148+ }
149+ }
150+
151+ fn check_rwlock_write() {
152+ unsafe {
153+ let data = SyncUnsafeCell::new((libc::PTHREAD_RWLOCK_INITIALIZER, 0));
154+ let ptr = SendPtr { ptr: data.get() };
155+ let mut threads = Vec::new();
156+
157+ for _ in 0..3 {
158+ let thread = thread::spawn(move || {
159+ let ptr = ptr; // circumvent per-field closure capture
160+ let rwlockptr = ptr::addr_of_mut!((*ptr.ptr).0);
161+ assert_eq!(libc::pthread_rwlock_wrlock(rwlockptr), 0);
162+ thread::yield_now();
163+ (*ptr.ptr).1 += 1;
164+ assert_eq!(libc::pthread_rwlock_unlock(rwlockptr), 0);
165+ });
166+ threads.push(thread);
167+
168+ let readthread = thread::spawn(move || {
169+ let ptr = ptr; // circumvent per-field closure capture
170+ let rwlockptr = ptr::addr_of_mut!((*ptr.ptr).0);
171+ assert_eq!(libc::pthread_rwlock_rdlock(rwlockptr), 0);
172+ thread::yield_now();
173+ let val = (*ptr.ptr).1;
174+ assert!(val >= 0 && val <= 3);
175+ assert_eq!(libc::pthread_rwlock_unlock(rwlockptr), 0);
176+ });
177+ threads.push(readthread);
178+ }
179+
180+ for thread in threads {
181+ thread.join().unwrap();
182+ }
183+
184+ let rwlockptr = ptr::addr_of_mut!((*ptr.ptr).0);
185+ assert_eq!(libc::pthread_rwlock_tryrdlock(rwlockptr), 0);
186+ assert_eq!((*ptr.ptr).1, 3);
187+ }
188+ }
189+
190+ fn check_rwlock_read_no_deadlock() {
191+ unsafe {
192+ let l1 = SyncUnsafeCell::new(libc::PTHREAD_RWLOCK_INITIALIZER);
193+ let l1 = SendPtr { ptr: l1.get() };
194+ let l2 = SyncUnsafeCell::new(libc::PTHREAD_RWLOCK_INITIALIZER);
195+ let l2 = SendPtr { ptr: l2.get() };
196+
197+ // acquire l1 and hold it until after the other thread is done
198+ assert_eq!(libc::pthread_rwlock_rdlock(l1.ptr), 0);
199+ let handle = thread::spawn(move || {
200+ let l1 = l1; // circumvent per-field closure capture
201+ let l2 = l2; // circumvent per-field closure capture
202+ // acquire l2 before the other thread
203+ assert_eq!(libc::pthread_rwlock_rdlock(l2.ptr), 0);
204+ thread::yield_now();
205+ assert_eq!(libc::pthread_rwlock_rdlock(l1.ptr), 0);
206+ thread::yield_now();
207+ assert_eq!(libc::pthread_rwlock_unlock(l1.ptr), 0);
208+ assert_eq!(libc::pthread_rwlock_unlock(l2.ptr), 0);
209+ });
210+ thread::yield_now();
211+ assert_eq!(libc::pthread_rwlock_rdlock(l2.ptr), 0);
212+ handle.join().unwrap();
213+ }
214+ }
215+
101216// std::sync::RwLock does not even used pthread_rwlock any more.
102217// Do some smoke testing of the API surface.
103218fn test_rwlock_libc_static_initializer() {
0 commit comments