1
1
//@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} ;
2
9
3
10
fn main ( ) {
4
11
test_mutex_libc_init_recursive ( ) ;
5
12
test_mutex_libc_init_normal ( ) ;
6
13
test_mutex_libc_init_errorcheck ( ) ;
7
14
test_rwlock_libc_static_initializer ( ) ;
8
-
9
15
#[ cfg( target_os = "linux" ) ]
10
16
test_mutex_libc_static_initializer_recursive ( ) ;
17
+
18
+ test_mutex ( ) ;
19
+ check_rwlock_write ( ) ;
20
+ check_rwlock_read_no_deadlock ( ) ;
11
21
}
12
22
13
23
fn test_mutex_libc_init_recursive ( ) {
14
24
unsafe {
15
- let mut attr: libc:: pthread_mutexattr_t = std :: mem:: zeroed ( ) ;
25
+ let mut attr: libc:: pthread_mutexattr_t = mem:: zeroed ( ) ;
16
26
assert_eq ! ( libc:: pthread_mutexattr_init( & mut attr as * mut _) , 0 ) ;
17
27
assert_eq ! (
18
28
libc:: pthread_mutexattr_settype( & mut attr as * mut _, libc:: PTHREAD_MUTEX_RECURSIVE ) ,
19
29
0 ,
20
30
) ;
21
- let mut mutex: libc:: pthread_mutex_t = std :: mem:: zeroed ( ) ;
31
+ let mut mutex: libc:: pthread_mutex_t = mem:: zeroed ( ) ;
22
32
assert_eq ! ( libc:: pthread_mutex_init( & mut mutex as * mut _, & mut attr as * mut _) , 0 ) ;
23
33
assert_eq ! ( libc:: pthread_mutex_lock( & mut mutex as * mut _) , 0 ) ;
24
34
assert_eq ! ( libc:: pthread_mutex_trylock( & mut mutex as * mut _) , 0 ) ;
@@ -36,7 +46,7 @@ fn test_mutex_libc_init_recursive() {
36
46
37
47
fn test_mutex_libc_init_normal ( ) {
38
48
unsafe {
39
- let mut mutexattr: libc:: pthread_mutexattr_t = std :: mem:: zeroed ( ) ;
49
+ let mut mutexattr: libc:: pthread_mutexattr_t = mem:: zeroed ( ) ;
40
50
assert_eq ! (
41
51
libc:: pthread_mutexattr_settype( & mut mutexattr as * mut _, 0x12345678 ) ,
42
52
libc:: EINVAL ,
@@ -45,7 +55,7 @@ fn test_mutex_libc_init_normal() {
45
55
libc:: pthread_mutexattr_settype( & mut mutexattr as * mut _, libc:: PTHREAD_MUTEX_NORMAL ) ,
46
56
0 ,
47
57
) ;
48
- let mut mutex: libc:: pthread_mutex_t = std :: mem:: zeroed ( ) ;
58
+ let mut mutex: libc:: pthread_mutex_t = mem:: zeroed ( ) ;
49
59
assert_eq ! ( libc:: pthread_mutex_init( & mut mutex as * mut _, & mutexattr as * const _) , 0 ) ;
50
60
assert_eq ! ( libc:: pthread_mutex_lock( & mut mutex as * mut _) , 0 ) ;
51
61
assert_eq ! ( libc:: pthread_mutex_trylock( & mut mutex as * mut _) , libc:: EBUSY ) ;
@@ -58,15 +68,15 @@ fn test_mutex_libc_init_normal() {
58
68
59
69
fn test_mutex_libc_init_errorcheck ( ) {
60
70
unsafe {
61
- let mut mutexattr: libc:: pthread_mutexattr_t = std :: mem:: zeroed ( ) ;
71
+ let mut mutexattr: libc:: pthread_mutexattr_t = mem:: zeroed ( ) ;
62
72
assert_eq ! (
63
73
libc:: pthread_mutexattr_settype(
64
74
& mut mutexattr as * mut _,
65
75
libc:: PTHREAD_MUTEX_ERRORCHECK ,
66
76
) ,
67
77
0 ,
68
78
) ;
69
- let mut mutex: libc:: pthread_mutex_t = std :: mem:: zeroed ( ) ;
79
+ let mut mutex: libc:: pthread_mutex_t = mem:: zeroed ( ) ;
70
80
assert_eq ! ( libc:: pthread_mutex_init( & mut mutex as * mut _, & mutexattr as * const _) , 0 ) ;
71
81
assert_eq ! ( libc:: pthread_mutex_lock( & mut mutex as * mut _) , 0 ) ;
72
82
assert_eq ! ( libc:: pthread_mutex_trylock( & mut mutex as * mut _) , libc:: EBUSY ) ;
@@ -98,6 +108,111 @@ fn test_mutex_libc_static_initializer_recursive() {
98
108
}
99
109
}
100
110
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
+
101
216
// std::sync::RwLock does not even used pthread_rwlock any more.
102
217
// Do some smoke testing of the API surface.
103
218
fn test_rwlock_libc_static_initializer ( ) {
0 commit comments