@@ -38,6 +38,7 @@ pub fn futex<'tcx>(
3838 let futex_wait = this. eval_libc_i32 ( "FUTEX_WAIT" ) ?;
3939 let futex_wait_bitset = this. eval_libc_i32 ( "FUTEX_WAIT_BITSET" ) ?;
4040 let futex_wake = this. eval_libc_i32 ( "FUTEX_WAKE" ) ?;
41+ let futex_wake_bitset = this. eval_libc_i32 ( "FUTEX_WAKE_BITSET" ) ?;
4142 let futex_realtime = this. eval_libc_i32 ( "FUTEX_CLOCK_REALTIME" ) ?;
4243
4344 // FUTEX_PRIVATE enables an optimization that stops it from working across processes.
@@ -48,32 +49,37 @@ pub fn futex<'tcx>(
4849 // or *timeout expires. `timeout == null` for an infinite timeout.
4950 //
5051 // 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.
5255 op if op & !futex_realtime == futex_wait || op & !futex_realtime == futex_wait_bitset => {
5356 let wait_bitset = op & !futex_realtime == futex_wait_bitset;
5457
58+ let bitset;
59+
5560 if wait_bitset {
5661 if args. len ( ) != 7 {
5762 throw_ub_format ! (
5863 "incorrect number of arguments for `futex` syscall with `op=FUTEX_WAIT_BITSET`: got {}, expected 7" ,
5964 args. len( )
6065 ) ;
6166 }
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 ( ) ?;
7068 } else {
7169 if args. len ( ) < 5 {
7270 throw_ub_format ! (
7371 "incorrect number of arguments for `futex` syscall with `op=FUTEX_WAIT`: got {}, expected at least 5" ,
7472 args. len( )
7573 ) ;
7674 }
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 ( ( ) ) ;
7783 }
7884
7985 // `deref_operand` but not actually dereferencing the ptr yet (it might be NULL!).
@@ -141,7 +147,7 @@ pub fn futex<'tcx>(
141147 if val == futex_val {
142148 // The value still matches, so we block the trait make it wait for FUTEX_WAKE.
143149 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 ) ;
145151 // Succesfully waking up from FUTEX_WAIT always returns zero.
146152 this. write_scalar ( Scalar :: from_machine_isize ( 0 , this) , dest) ?;
147153 // Register a timeout callback if a timeout was specified.
@@ -173,10 +179,30 @@ pub fn futex<'tcx>(
173179 // Wakes at most `val` threads waiting on the futex at `addr`.
174180 // Returns the amount of threads woken up.
175181 // 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+ }
177203 let mut n = 0 ;
178204 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 ) {
180206 this. unblock_thread ( thread) ;
181207 this. unregister_timeout_callback_if_exists ( thread) ;
182208 n += 1 ;
0 commit comments