1
1
//@only-target: freebsd
2
- //@compile-flags: -Zmiri-preemption-rate=0
2
+ //@compile-flags: -Zmiri-preemption-rate=0 -Zmiri-disable-isolation
3
3
4
- use std:: mem:: MaybeUninit ;
4
+ use std:: mem:: { self , MaybeUninit } ;
5
5
use std:: ptr:: { self , addr_of} ;
6
6
use std:: sync:: atomic:: AtomicU32 ;
7
7
use std:: time:: { Duration , Instant } ;
8
8
use std:: { io, thread} ;
9
9
10
10
fn wake_nobody ( ) {
11
- // TODO: _umtx_op does not return how many threads were woken up
12
- // How do i test this?
11
+ // Current thread waits on futex
12
+ // New thread wakes up 0 threads waiting on that futex
13
+ // Current thread should time out
14
+ static mut FUTEX : u32 = 0 ;
15
+
16
+ let waker = thread:: spawn ( || {
17
+ thread:: sleep ( Duration :: from_millis ( 200 ) ) ;
18
+
19
+ unsafe {
20
+ assert_eq ! (
21
+ libc:: _umtx_op(
22
+ addr_of!( FUTEX ) as * mut _,
23
+ libc:: UMTX_OP_WAKE_PRIVATE ,
24
+ 0 , // wake up 0 waiters
25
+ ptr:: null_mut:: <libc:: c_void>( ) ,
26
+ ptr:: null_mut:: <libc:: c_void>( ) ,
27
+ ) ,
28
+ 0
29
+ ) ;
30
+ }
31
+ } ) ;
32
+ let mut timeout = libc:: timespec { tv_sec : 0 , tv_nsec : 400_000_000 } ;
33
+ let timeout_size_arg =
34
+ ptr:: without_provenance_mut :: < libc:: c_void > ( mem:: size_of :: < libc:: timespec > ( ) ) ;
35
+ unsafe {
36
+ assert_eq ! (
37
+ libc:: _umtx_op(
38
+ addr_of!( FUTEX ) as * mut _,
39
+ libc:: UMTX_OP_WAIT_UINT_PRIVATE ,
40
+ 0 ,
41
+ timeout_size_arg,
42
+ & mut timeout as * mut _ as _,
43
+ ) ,
44
+ -1
45
+ ) ;
46
+ // main thread did not get woken up, so it timed out
47
+ assert_eq ! ( io:: Error :: last_os_error( ) . raw_os_error( ) . unwrap( ) , libc:: ETIMEDOUT ) ;
48
+ }
49
+
50
+ waker. join ( ) . unwrap ( ) ;
13
51
}
14
52
15
53
fn wake_dangling ( ) {
@@ -36,6 +74,7 @@ fn wake_dangling() {
36
74
fn wait_wrong_val ( ) {
37
75
let futex: u32 = 123 ;
38
76
77
+ // Wait with a wrong value just returns 0
39
78
unsafe {
40
79
assert_eq ! (
41
80
libc:: _umtx_op(
@@ -45,35 +84,69 @@ fn wait_wrong_val() {
45
84
ptr:: null_mut:: <libc:: c_void>( ) ,
46
85
ptr:: null_mut:: <libc:: c_void>( ) ,
47
86
) ,
48
- - 1
87
+ 0
49
88
) ;
50
- // man page doesn't document but we set EINVAL for consistency?
51
- assert_eq ! ( io:: Error :: last_os_error( ) . raw_os_error( ) . unwrap( ) , libc:: EINVAL ) ;
52
89
}
53
90
}
54
91
55
92
fn wait_relative_timeout ( ) {
56
- let start = Instant :: now ( ) ;
93
+ fn without_timespec ( ) {
94
+ let start = Instant :: now ( ) ;
57
95
58
- let futex: u32 = 123 ;
96
+ let futex: u32 = 123 ;
59
97
60
- // Wait for 200ms, with nobody waking us up early
61
- unsafe {
62
- assert_eq ! (
63
- libc:: _umtx_op(
64
- ptr:: from_ref( & futex) . cast_mut( ) . cast( ) ,
65
- libc:: UMTX_OP_WAIT_UINT_PRIVATE ,
66
- 123 ,
67
- & mut libc:: timespec { tv_sec: 0 , tv_nsec: 200_000_000 } as * mut _ as * mut _,
68
- ptr:: null_mut:: <libc:: c_void>( ) ,
69
- ) ,
70
- -1
71
- ) ;
72
- // man page doesn't document but we set EINVAL for consistency
73
- assert_eq ! ( io:: Error :: last_os_error( ) . raw_os_error( ) . unwrap( ) , libc:: ETIMEDOUT ) ;
98
+ let mut timeout = libc:: timespec { tv_sec : 0 , tv_nsec : 200_000_000 } ;
99
+ let timeout_size_arg =
100
+ ptr:: without_provenance_mut :: < libc:: c_void > ( mem:: size_of :: < libc:: timespec > ( ) ) ;
101
+ // Wait for 200ms, with nobody waking us up early
102
+ unsafe {
103
+ assert_eq ! (
104
+ libc:: _umtx_op(
105
+ ptr:: from_ref( & futex) . cast_mut( ) . cast( ) ,
106
+ libc:: UMTX_OP_WAIT_UINT_PRIVATE ,
107
+ 123 ,
108
+ timeout_size_arg,
109
+ & mut timeout as * mut _ as _,
110
+ ) ,
111
+ -1
112
+ ) ;
113
+ assert_eq ! ( io:: Error :: last_os_error( ) . raw_os_error( ) . unwrap( ) , libc:: ETIMEDOUT ) ;
114
+ }
115
+
116
+ assert ! ( ( 200 ..1000 ) . contains( & start. elapsed( ) . as_millis( ) ) ) ;
74
117
}
75
118
76
- assert ! ( ( 200 ..1000 ) . contains( & start. elapsed( ) . as_millis( ) ) ) ;
119
+ fn with_timespec ( ) {
120
+ let futex: u32 = 123 ;
121
+ let mut timeout = libc:: _umtx_time {
122
+ _timeout : libc:: timespec { tv_sec : 0 , tv_nsec : 200_000_000 } ,
123
+ _flags : 0 ,
124
+ _clockid : libc:: CLOCK_MONOTONIC as u32 ,
125
+ } ;
126
+ let timeout_size_arg =
127
+ ptr:: without_provenance_mut :: < libc:: c_void > ( mem:: size_of :: < libc:: _umtx_time > ( ) ) ;
128
+
129
+ let start = Instant :: now ( ) ;
130
+
131
+ // Wait for 200ms, with nobody waking us up early
132
+ unsafe {
133
+ assert_eq ! (
134
+ libc:: _umtx_op(
135
+ ptr:: from_ref( & futex) . cast_mut( ) . cast( ) ,
136
+ libc:: UMTX_OP_WAIT_UINT_PRIVATE ,
137
+ 123 ,
138
+ timeout_size_arg,
139
+ & mut timeout as * mut _ as _,
140
+ ) ,
141
+ -1
142
+ ) ;
143
+ assert_eq ! ( io:: Error :: last_os_error( ) . raw_os_error( ) . unwrap( ) , libc:: ETIMEDOUT ) ;
144
+ }
145
+ assert ! ( ( 200 ..1000 ) . contains( & start. elapsed( ) . as_millis( ) ) ) ;
146
+ }
147
+
148
+ without_timespec ( ) ;
149
+ with_timespec ( ) ;
77
150
}
78
151
79
152
fn wait_absolute_timeout ( ) {
@@ -93,14 +166,14 @@ fn wait_absolute_timeout() {
93
166
timeout. tv_sec += 1 ;
94
167
}
95
168
96
- // Create umtx_timeout struct
169
+ // Create umtx_timeout struct with that absolute timeout.
97
170
let umtx_timeout = libc:: _umtx_time {
98
171
_timeout : timeout,
99
172
_flags : libc:: UMTX_ABSTIME ,
100
173
_clockid : libc:: CLOCK_MONOTONIC as u32 ,
101
174
} ;
102
175
let umtx_timeout_ptr = & umtx_timeout as * const _ ;
103
- let umtx_timeout_size = std :: mem:: size_of_val ( & umtx_timeout) ;
176
+ let umtx_timeout_size = ptr :: without_provenance_mut ( mem:: size_of_val ( & umtx_timeout) ) ;
104
177
105
178
let futex: u32 = 123 ;
106
179
@@ -111,55 +184,70 @@ fn wait_absolute_timeout() {
111
184
ptr:: from_ref( & futex) . cast_mut( ) . cast( ) ,
112
185
libc:: UMTX_OP_WAIT_UINT_PRIVATE ,
113
186
123 ,
114
- ptr :: without_provenance_mut ( umtx_timeout_size) ,
187
+ umtx_timeout_size,
115
188
umtx_timeout_ptr as * mut _,
116
189
) ,
117
190
-1
118
191
) ;
119
- // man page doesn't document but we set EINVAL for consistency
120
192
assert_eq ! ( io:: Error :: last_os_error( ) . raw_os_error( ) . unwrap( ) , libc:: ETIMEDOUT ) ;
121
193
}
122
194
assert ! ( ( 200 ..1000 ) . contains( & start. elapsed( ) . as_millis( ) ) ) ;
123
195
}
124
196
125
197
fn wait_wake ( ) {
126
- let start = Instant :: now ( ) ;
127
-
128
198
static mut FUTEX : u32 = 0 ;
129
199
130
- let t = thread:: spawn ( move || {
131
- thread:: sleep ( Duration :: from_millis ( 200 ) ) ;
200
+ let t1 = thread:: spawn ( move || {
201
+ let mut timeout = libc:: timespec { tv_sec : 0 , tv_nsec : 500_000_000 } ;
202
+ let timeout_size_arg =
203
+ ptr:: without_provenance_mut :: < libc:: c_void > ( mem:: size_of :: < libc:: timespec > ( ) ) ;
132
204
unsafe {
133
- assert_eq ! (
134
- libc:: _umtx_op(
135
- addr_of!( FUTEX ) as * mut _,
136
- libc:: UMTX_OP_WAKE_PRIVATE ,
137
- 10 , // Wake up 10 threads, but we can't check that we woken up 1.
138
- ptr:: null_mut:: <libc:: c_void>( ) ,
139
- ptr:: null_mut:: <libc:: c_void>( ) ,
140
- ) ,
141
- 0
205
+ libc:: _umtx_op (
206
+ addr_of ! ( FUTEX ) as * mut _ ,
207
+ libc:: UMTX_OP_WAIT_UINT_PRIVATE ,
208
+ 0 , // FUTEX is 0
209
+ timeout_size_arg,
210
+ & mut timeout as * mut _ as _ ,
211
+ ) ;
212
+ io:: Error :: last_os_error ( ) . raw_os_error ( ) . unwrap ( ) == libc:: ETIMEDOUT
213
+ }
214
+ } ) ;
215
+ let t2 = thread:: spawn ( move || {
216
+ let mut timeout = libc:: timespec { tv_sec : 0 , tv_nsec : 500_000_000 } ;
217
+ let timeout_size_arg =
218
+ ptr:: without_provenance_mut :: < libc:: c_void > ( mem:: size_of :: < libc:: timespec > ( ) ) ;
219
+ unsafe {
220
+ libc:: _umtx_op (
221
+ addr_of ! ( FUTEX ) as * mut _ ,
222
+ libc:: UMTX_OP_WAIT_UINT_PRIVATE ,
223
+ 0 , // FUTEX is 0
224
+ // make sure the threads still exit
225
+ timeout_size_arg,
226
+ & mut timeout as * mut _ as _ ,
142
227
) ;
228
+ io:: Error :: last_os_error ( ) . raw_os_error ( ) . unwrap ( ) == libc:: ETIMEDOUT
143
229
}
144
230
} ) ;
145
231
232
+ // Wake up 1 thread and make sure the other is still waiting
233
+ thread:: sleep ( Duration :: from_millis ( 200 ) ) ;
146
234
unsafe {
147
235
assert_eq ! (
148
236
libc:: _umtx_op(
149
237
addr_of!( FUTEX ) as * mut _,
150
- libc:: UMTX_OP_WAIT_UINT_PRIVATE ,
151
- 0 , // FUTEX is 0
238
+ libc:: UMTX_OP_WAKE_PRIVATE ,
239
+ 1 ,
152
240
ptr:: null_mut:: <libc:: c_void>( ) ,
153
241
ptr:: null_mut:: <libc:: c_void>( ) ,
154
242
) ,
155
243
0
156
244
) ;
157
245
}
158
-
159
- // When running this in stress-gc mode, things can take quite long.
160
- // So the timeout is 3000 ms.
161
- assert ! ( ( 200 .. 3000 ) . contains ( & start . elapsed ( ) . as_millis ( ) ) ) ;
162
- t . join ( ) . unwrap ( ) ;
246
+ // Wait a bit more for good measure.
247
+ thread :: sleep ( Duration :: from_millis ( 100 ) ) ;
248
+ let t1_woke_up = t1 . join ( ) . unwrap ( ) ;
249
+ let t2_woke_up = t2 . join ( ) . unwrap ( ) ;
250
+ assert ! ( ! ( t1_woke_up && t2_woke_up ) , "Expected only 1 thread to wake up" ) ;
163
251
}
164
252
165
253
fn main ( ) {
0 commit comments