@@ -4,6 +4,8 @@ use std::{
44 time:: Duration ,
55} ;
66
7+ use crate :: FutexError ;
8+
79/// The number of OS synchronization primitives to use.
810const TABLE_SIZE : usize = 256 ;
911
@@ -12,9 +14,14 @@ static TABLE: [TableEntry; TABLE_SIZE] = [TableEntry::DEFAULT; TABLE_SIZE];
1214
1315/// Puts the current thread to sleep if `condition` evaluates to `true`.
1416/// The thread will be woken after `timeout` if it is provided.
15- pub fn wait ( ptr : * const ( ) , condition : impl FnOnce ( ) -> bool , timeout : Option < Duration > ) {
17+ pub fn wait (
18+ ptr : * const ( ) ,
19+ condition : impl Fn ( ) -> bool ,
20+ timeout : Option < Duration > ,
21+ ) -> Result < ( ) , FutexError > {
1622 let entry = & TABLE [ entry_for_ptr ( ptr) as usize ] ;
1723 let mut guard = spin_lock ( & entry. mutex ) ;
24+ let mut timedout = false ;
1825 if condition ( ) {
1926 if guard. waiting_count == 0 {
2027 guard. address = ptr;
@@ -25,42 +32,58 @@ pub fn wait(ptr: *const (), condition: impl FnOnce() -> bool, timeout: Option<Du
2532 guard. waiting_count += 1 ;
2633
2734 guard = if let Some ( time) = timeout {
28- entry
35+ let ( guard , result ) = entry
2936 . condvar
3037 . wait_timeout ( guard, time)
31- . expect ( "Failed to lock mutex" )
32- . 0
38+ . expect ( "Failed to lock mutex" ) ;
39+ timedout = result. timed_out ( ) ;
40+ guard
3341 } else {
3442 entry. condvar . wait ( guard) . expect ( "Failed to lock mutex" )
3543 } ;
3644
3745 guard. waiting_count -= 1 ;
46+
47+ if timedout {
48+ Err ( FutexError :: Timeout )
49+ } else {
50+ Ok ( ( ) )
51+ }
52+ } else {
53+ Err ( FutexError :: NotEqual )
3854 }
3955}
4056
4157/// Wakes all threads waiting on `ptr`.
42- pub fn notify_all ( ptr : * const ( ) ) {
43- if !ptr. is_null ( ) {
44- let entry = & TABLE [ entry_for_ptr ( ptr) as usize ] ;
45- let metadata = * spin_lock ( & entry. mutex ) ;
46- if 0 < metadata. waiting_count {
47- entry. condvar . notify_all ( ) ;
48- }
58+ pub fn notify_all ( ptr : * const ( ) ) -> usize {
59+ if ptr. is_null ( ) {
60+ return 0 ;
4961 }
62+ let entry = & TABLE [ entry_for_ptr ( ptr) as usize ] ;
63+ let metadata = * spin_lock ( & entry. mutex ) ;
64+ if 0 < metadata. waiting_count {
65+ entry. condvar . notify_all ( ) ;
66+ }
67+ metadata. waiting_count
5068}
5169
5270/// Wakes at least one thread waiting on `ptr`.
53- pub fn notify_one ( ptr : * const ( ) ) {
54- if !ptr. is_null ( ) {
55- let entry = & TABLE [ entry_for_ptr ( ptr) as usize ] ;
56- let metadata = * spin_lock ( & entry. mutex ) ;
57- if 0 < metadata. waiting_count {
58- if metadata. address . is_null ( ) {
59- entry. condvar . notify_all ( ) ;
60- } else if metadata. address == ptr {
61- entry. condvar . notify_one ( ) ;
62- }
71+ pub fn notify_many ( ptr : * const ( ) , count : usize ) -> usize {
72+ if ptr. is_null ( ) {
73+ return 0 ;
74+ }
75+ let entry = & TABLE [ entry_for_ptr ( ptr) as usize ] ;
76+ let metadata = * spin_lock ( & entry. mutex ) ;
77+ if metadata. waiting_count == 0 {
78+ 0
79+ } else if metadata. waiting_count < count || metadata. address . is_null ( ) {
80+ entry. condvar . notify_all ( ) ;
81+ metadata. waiting_count
82+ } else {
83+ for _ in 0 ..count {
84+ entry. condvar . notify_one ( ) ;
6385 }
86+ count
6487 }
6588}
6689
@@ -79,9 +102,9 @@ fn spin_lock<T>(mutex: &Mutex<T>) -> MutexGuard<'_, T> {
79102/// Gets the entry index to use for the given address.
80103fn entry_for_ptr ( ptr : * const ( ) ) -> u8 {
81104 let x_64 = ptr as u64 ;
82- let x_32 = ( x_64 >> 32 ) as u32 | x_64 as u32 ;
83- let x_16 = ( x_32 >> 16 ) as u16 | x_32 as u16 ;
84- ( x_16 >> 8 ) as u8 | x_16 as u8
105+ let x_32 = ( x_64 >> 32 ) as u32 ^ x_64 as u32 ;
106+ let x_16 = ( x_32 >> 16 ) as u16 ^ x_32 as u16 ;
107+ ( x_16 >> 8 ) as u8 ^ ( x_16 >> 2 ) as u8
85108}
86109
87110/// Holds metadata that gets written while locking.
0 commit comments