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