@@ -38,6 +38,7 @@ pub fn futex<'tcx>(
38
38
let futex_wait = this. eval_libc_i32 ( "FUTEX_WAIT" ) ?;
39
39
let futex_wait_bitset = this. eval_libc_i32 ( "FUTEX_WAIT_BITSET" ) ?;
40
40
let futex_wake = this. eval_libc_i32 ( "FUTEX_WAKE" ) ?;
41
+ let futex_wake_bitset = this. eval_libc_i32 ( "FUTEX_WAKE_BITSET" ) ?;
41
42
let futex_realtime = this. eval_libc_i32 ( "FUTEX_CLOCK_REALTIME" ) ?;
42
43
43
44
// FUTEX_PRIVATE enables an optimization that stops it from working across processes.
@@ -48,32 +49,37 @@ pub fn futex<'tcx>(
48
49
// or *timeout expires. `timeout == null` for an infinite timeout.
49
50
//
50
51
// FUTEX_WAIT_BITSET: (int *addr, int op = FUTEX_WAIT_BITSET, int val, const timespec *timeout, int *_ignored, unsigned int bitset)
51
- // When bitset is u32::MAX, this is identical to FUTEX_WAIT, except the timeout is absolute rather than relative.
52
+ // This is identical to FUTEX_WAIT, except:
53
+ // - The timeout is absolute rather than relative.
54
+ // - You can specify the bitset to selecting what WAKE operations to respond to.
52
55
op if op & !futex_realtime == futex_wait || op & !futex_realtime == futex_wait_bitset => {
53
56
let wait_bitset = op & !futex_realtime == futex_wait_bitset;
54
57
58
+ let bitset;
59
+
55
60
if wait_bitset {
56
61
if args. len ( ) != 7 {
57
62
throw_ub_format ! (
58
63
"incorrect number of arguments for `futex` syscall with `op=FUTEX_WAIT_BITSET`: got {}, expected 7" ,
59
64
args. len( )
60
65
) ;
61
66
}
62
-
63
- let bitset = this. read_scalar ( & args[ 6 ] ) ?. to_u32 ( ) ?;
64
-
65
- if bitset != u32:: MAX {
66
- throw_unsup_format ! (
67
- "Miri does not support `futex` syscall with `op=FUTEX_WAIT_BITSET` with a bitset other than UINT_MAX"
68
- ) ;
69
- }
67
+ bitset = this. read_scalar ( & args[ 6 ] ) ?. to_u32 ( ) ?;
70
68
} else {
71
69
if args. len ( ) < 5 {
72
70
throw_ub_format ! (
73
71
"incorrect number of arguments for `futex` syscall with `op=FUTEX_WAIT`: got {}, expected at least 5" ,
74
72
args. len( )
75
73
) ;
76
74
}
75
+ bitset = u32:: MAX ;
76
+ }
77
+
78
+ if bitset == 0 {
79
+ let einval = this. eval_libc ( "EINVAL" ) ?;
80
+ this. set_last_error ( einval) ?;
81
+ this. write_scalar ( Scalar :: from_machine_isize ( -1 , this) , dest) ?;
82
+ return Ok ( ( ) ) ;
77
83
}
78
84
79
85
// `deref_operand` but not actually dereferencing the ptr yet (it might be NULL!).
@@ -141,7 +147,7 @@ pub fn futex<'tcx>(
141
147
if val == futex_val {
142
148
// The value still matches, so we block the trait make it wait for FUTEX_WAKE.
143
149
this. block_thread ( thread) ;
144
- this. futex_wait ( addr_scalar. to_machine_usize ( this) ?, thread) ;
150
+ this. futex_wait ( addr_scalar. to_machine_usize ( this) ?, thread, bitset ) ;
145
151
// Succesfully waking up from FUTEX_WAIT always returns zero.
146
152
this. write_scalar ( Scalar :: from_machine_isize ( 0 , this) , dest) ?;
147
153
// Register a timeout callback if a timeout was specified.
@@ -173,10 +179,30 @@ pub fn futex<'tcx>(
173
179
// Wakes at most `val` threads waiting on the futex at `addr`.
174
180
// Returns the amount of threads woken up.
175
181
// Does not access the futex value at *addr.
176
- op if op == futex_wake => {
182
+ // FUTEX_WAKE_BITSET: (int *addr, int op = FUTEX_WAKE, int val, const timespect *_unused, int *_unused, unsigned int bitset)
183
+ // Same as FUTEX_WAKE, but allows you to specify a bitset to select which threads to wake up.
184
+ op if op == futex_wake || op == futex_wake_bitset => {
185
+ let bitset;
186
+ if op == futex_wake_bitset {
187
+ if args. len ( ) != 7 {
188
+ throw_ub_format ! (
189
+ "incorrect number of arguments for `futex` syscall with `op=FUTEX_WAKE_BITSET`: got {}, expected 7" ,
190
+ args. len( )
191
+ ) ;
192
+ }
193
+ bitset = this. read_scalar ( & args[ 6 ] ) ?. to_u32 ( ) ?;
194
+ } else {
195
+ bitset = u32:: MAX ;
196
+ }
197
+ if bitset == 0 {
198
+ let einval = this. eval_libc ( "EINVAL" ) ?;
199
+ this. set_last_error ( einval) ?;
200
+ this. write_scalar ( Scalar :: from_machine_isize ( -1 , this) , dest) ?;
201
+ return Ok ( ( ) ) ;
202
+ }
177
203
let mut n = 0 ;
178
204
for _ in 0 ..val {
179
- if let Some ( thread) = this. futex_wake ( addr_scalar. to_machine_usize ( this) ?) {
205
+ if let Some ( thread) = this. futex_wake ( addr_scalar. to_machine_usize ( this) ?, bitset ) {
180
206
this. unblock_thread ( thread) ;
181
207
this. unregister_timeout_callback_if_exists ( thread) ;
182
208
n += 1 ;
0 commit comments