@@ -36,7 +36,9 @@ pub fn futex<'tcx>(
3636
3737 let futex_private = this. eval_libc_i32 ( "FUTEX_PRIVATE_FLAG" ) ?;
3838 let futex_wait = this. eval_libc_i32 ( "FUTEX_WAIT" ) ?;
39+ let futex_wait_bitset = this. eval_libc_i32 ( "FUTEX_WAIT_BITSET" ) ?;
3940 let futex_wake = this. eval_libc_i32 ( "FUTEX_WAKE" ) ?;
41+ let futex_wake_bitset = this. eval_libc_i32 ( "FUTEX_WAKE_BITSET" ) ?;
4042 let futex_realtime = this. eval_libc_i32 ( "FUTEX_CLOCK_REALTIME" ) ?;
4143
4244 // FUTEX_PRIVATE enables an optimization that stops it from working across processes.
@@ -45,12 +47,37 @@ pub fn futex<'tcx>(
4547 // FUTEX_WAIT: (int *addr, int op = FUTEX_WAIT, int val, const timespec *timeout)
4648 // Blocks the thread if *addr still equals val. Wakes up when FUTEX_WAKE is called on the same address,
4749 // or *timeout expires. `timeout == null` for an infinite timeout.
48- op if op & !futex_realtime == futex_wait => {
49- if args. len ( ) < 5 {
50- throw_ub_format ! (
51- "incorrect number of arguments for `futex` syscall with `op=FUTEX_WAIT`: got {}, expected at least 5" ,
52- args. len( )
53- ) ;
50+ //
51+ // FUTEX_WAIT_BITSET: (int *addr, int op = FUTEX_WAIT_BITSET, int val, const timespec *timeout, int *_ignored, unsigned int bitset)
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.
55+ op if op & !futex_realtime == futex_wait || op & !futex_realtime == futex_wait_bitset => {
56+ let wait_bitset = op & !futex_realtime == futex_wait_bitset;
57+
58+ let bitset = if wait_bitset {
59+ if args. len ( ) != 7 {
60+ throw_ub_format ! (
61+ "incorrect number of arguments for `futex` syscall with `op=FUTEX_WAIT_BITSET`: got {}, expected 7" ,
62+ args. len( )
63+ ) ;
64+ }
65+ this. read_scalar ( & args[ 6 ] ) ?. to_u32 ( ) ?
66+ } else {
67+ if args. len ( ) < 5 {
68+ throw_ub_format ! (
69+ "incorrect number of arguments for `futex` syscall with `op=FUTEX_WAIT`: got {}, expected at least 5" ,
70+ args. len( )
71+ ) ;
72+ }
73+ u32:: MAX
74+ } ;
75+
76+ if bitset == 0 {
77+ let einval = this. eval_libc ( "EINVAL" ) ?;
78+ this. set_last_error ( einval) ?;
79+ this. write_scalar ( Scalar :: from_machine_isize ( -1 , this) , dest) ?;
80+ return Ok ( ( ) ) ;
5481 }
5582
5683 // `deref_operand` but not actually dereferencing the ptr yet (it might be NULL!).
@@ -70,10 +97,20 @@ pub fn futex<'tcx>(
7097 return Ok ( ( ) ) ;
7198 }
7299 } ;
73- Some ( if op & futex_realtime != 0 {
74- Time :: RealTime ( SystemTime :: now ( ) . checked_add ( duration) . unwrap ( ) )
100+ Some ( if wait_bitset {
101+ // FUTEX_WAIT_BITSET uses an absolute timestamp.
102+ if op & futex_realtime != 0 {
103+ Time :: RealTime ( SystemTime :: UNIX_EPOCH . checked_add ( duration) . unwrap ( ) )
104+ } else {
105+ Time :: Monotonic ( this. machine . time_anchor . checked_add ( duration) . unwrap ( ) )
106+ }
75107 } else {
76- Time :: Monotonic ( Instant :: now ( ) . checked_add ( duration) . unwrap ( ) )
108+ // FUTEX_WAIT uses a relative timestamp.
109+ if op & futex_realtime != 0 {
110+ Time :: RealTime ( SystemTime :: now ( ) . checked_add ( duration) . unwrap ( ) )
111+ } else {
112+ Time :: Monotonic ( Instant :: now ( ) . checked_add ( duration) . unwrap ( ) )
113+ }
77114 } )
78115 } ;
79116 // Check the pointer for alignment and validity.
@@ -108,7 +145,7 @@ pub fn futex<'tcx>(
108145 if val == futex_val {
109146 // The value still matches, so we block the trait make it wait for FUTEX_WAKE.
110147 this. block_thread ( thread) ;
111- this. futex_wait ( addr_scalar. to_machine_usize ( this) ?, thread) ;
148+ this. futex_wait ( addr_scalar. to_machine_usize ( this) ?, thread, bitset ) ;
112149 // Succesfully waking up from FUTEX_WAIT always returns zero.
113150 this. write_scalar ( Scalar :: from_machine_isize ( 0 , this) , dest) ?;
114151 // Register a timeout callback if a timeout was specified.
@@ -140,10 +177,29 @@ pub fn futex<'tcx>(
140177 // Wakes at most `val` threads waiting on the futex at `addr`.
141178 // Returns the amount of threads woken up.
142179 // Does not access the futex value at *addr.
143- op if op == futex_wake => {
180+ // FUTEX_WAKE_BITSET: (int *addr, int op = FUTEX_WAKE, int val, const timespect *_unused, int *_unused, unsigned int bitset)
181+ // Same as FUTEX_WAKE, but allows you to specify a bitset to select which threads to wake up.
182+ op if op == futex_wake || op == futex_wake_bitset => {
183+ let bitset = if op == futex_wake_bitset {
184+ if args. len ( ) != 7 {
185+ throw_ub_format ! (
186+ "incorrect number of arguments for `futex` syscall with `op=FUTEX_WAKE_BITSET`: got {}, expected 7" ,
187+ args. len( )
188+ ) ;
189+ }
190+ this. read_scalar ( & args[ 6 ] ) ?. to_u32 ( ) ?
191+ } else {
192+ u32:: MAX
193+ } ;
194+ if bitset == 0 {
195+ let einval = this. eval_libc ( "EINVAL" ) ?;
196+ this. set_last_error ( einval) ?;
197+ this. write_scalar ( Scalar :: from_machine_isize ( -1 , this) , dest) ?;
198+ return Ok ( ( ) ) ;
199+ }
144200 let mut n = 0 ;
145201 for _ in 0 ..val {
146- if let Some ( thread) = this. futex_wake ( addr_scalar. to_machine_usize ( this) ?) {
202+ if let Some ( thread) = this. futex_wake ( addr_scalar. to_machine_usize ( this) ?, bitset ) {
147203 this. unblock_thread ( thread) ;
148204 this. unregister_timeout_callback_if_exists ( thread) ;
149205 n += 1 ;
0 commit comments