11#![ cfg( any(
22 target_os = "linux" ,
33 target_os = "android" ,
4- all( target_os = "emscripten" , target_feature = "atomics" )
4+ all( target_os = "emscripten" , target_feature = "atomics" ) ,
5+ target_os = "freebsd" ,
6+ target_os = "openbsd" ,
7+ target_os = "dragonfly" ,
58) ) ]
69
710use crate :: sync:: atomic:: AtomicU32 ;
@@ -12,7 +15,7 @@ use crate::time::Duration;
1215/// Returns directly if the futex doesn't hold the expected value.
1316///
1417/// Returns false on timeout, and true in all other cases.
15- #[ cfg( any( target_os = "linux" , target_os = "android" ) ) ]
18+ #[ cfg( any( target_os = "linux" , target_os = "android" , target_os = "freebsd" ) ) ]
1619pub fn futex_wait ( futex : & AtomicU32 , expected : u32 , timeout : Option < Duration > ) -> bool {
1720 use super :: time:: Timespec ;
1821 use crate :: ptr:: null;
@@ -30,18 +33,43 @@ pub fn futex_wait(futex: &AtomicU32, expected: u32, timeout: Option<Duration>) -
3033 return true ;
3134 }
3235
33- // Use FUTEX_WAIT_BITSET rather than FUTEX_WAIT to be able to give an
34- // absolute time rather than a relative time.
3536 let r = unsafe {
36- libc:: syscall (
37- libc:: SYS_futex ,
38- futex as * const AtomicU32 ,
39- libc:: FUTEX_WAIT_BITSET | libc:: FUTEX_PRIVATE_FLAG ,
40- expected,
41- timespec. as_ref ( ) . map_or ( null ( ) , |t| & t. t as * const libc:: timespec ) ,
42- null :: < u32 > ( ) , // This argument is unused for FUTEX_WAIT_BITSET.
43- !0u32 , // A full bitmask, to make it behave like a regular FUTEX_WAIT.
44- )
37+ cfg_if:: cfg_if! {
38+ if #[ cfg( target_os = "freebsd" ) ] {
39+ // FreeBSD doesn't have futex(), but it has
40+ // _umtx_op(UMTX_OP_WAIT_UINT_PRIVATE), which is nearly
41+ // identical. It supports absolute timeouts through a flag
42+ // in the _umtx_time struct.
43+ let umtx_timeout = timespec. map( |t| libc:: _umtx_time {
44+ _timeout: t. t,
45+ _flags: libc:: UMTX_ABSTIME ,
46+ _clockid: libc:: CLOCK_MONOTONIC as u32 ,
47+ } ) ;
48+ let umtx_timeout_ptr = umtx_timeout. as_ref( ) . map_or( null( ) , |t| t as * const _) ;
49+ let umtx_timeout_size = umtx_timeout. as_ref( ) . map_or( 0 , |t| crate :: mem:: size_of_val( t) ) ;
50+ libc:: _umtx_op(
51+ futex as * const AtomicU32 as * mut _,
52+ libc:: UMTX_OP_WAIT_UINT_PRIVATE ,
53+ expected as libc:: c_ulong,
54+ crate :: ptr:: invalid_mut( umtx_timeout_size) ,
55+ umtx_timeout_ptr as * mut _,
56+ )
57+ } else if #[ cfg( any( target_os = "linux" , target_os = "android" ) ) ] {
58+ // Use FUTEX_WAIT_BITSET rather than FUTEX_WAIT to be able to give an
59+ // absolute time rather than a relative time.
60+ libc:: syscall(
61+ libc:: SYS_futex ,
62+ futex as * const AtomicU32 ,
63+ libc:: FUTEX_WAIT_BITSET | libc:: FUTEX_PRIVATE_FLAG ,
64+ expected,
65+ timespec. as_ref( ) . map_or( null( ) , |t| & t. t as * const libc:: timespec) ,
66+ null:: <u32 >( ) , // This argument is unused for FUTEX_WAIT_BITSET.
67+ !0u32 , // A full bitmask, to make it behave like a regular FUTEX_WAIT.
68+ )
69+ } else {
70+ compile_error!( "unknown target_os" ) ;
71+ }
72+ }
4573 } ;
4674
4775 match ( r < 0 ) . then ( super :: os:: errno) {
@@ -56,31 +84,133 @@ pub fn futex_wait(futex: &AtomicU32, expected: u32, timeout: Option<Duration>) -
5684///
5785/// Returns true if this actually woke up such a thread,
5886/// or false if no thread was waiting on this futex.
87+ ///
88+ /// On some platforms, this always returns false.
89+ #[ cfg( any( target_os = "linux" , target_os = "android" ) ) ]
90+ pub fn futex_wake ( futex : & AtomicU32 ) -> bool {
91+ let ptr = futex as * const AtomicU32 ;
92+ let op = libc:: FUTEX_WAKE | libc:: FUTEX_PRIVATE_FLAG ;
93+ unsafe { libc:: syscall ( libc:: SYS_futex , ptr, op, 1 ) > 0 }
94+ }
95+
96+ /// Wake up all threads that are waiting on futex_wait on this futex.
5997#[ cfg( any( target_os = "linux" , target_os = "android" ) ) ]
98+ pub fn futex_wake_all ( futex : & AtomicU32 ) {
99+ let ptr = futex as * const AtomicU32 ;
100+ let op = libc:: FUTEX_WAKE | libc:: FUTEX_PRIVATE_FLAG ;
101+ unsafe {
102+ libc:: syscall ( libc:: SYS_futex , ptr, op, i32:: MAX ) ;
103+ }
104+ }
105+
106+ // FreeBSD doesn't tell us how many threads are woken up, so this always returns false.
107+ #[ cfg( target_os = "freebsd" ) ]
60108pub fn futex_wake ( futex : & AtomicU32 ) -> bool {
109+ use crate :: ptr:: null_mut;
61110 unsafe {
62- libc:: syscall (
63- libc:: SYS_futex ,
64- futex as * const AtomicU32 ,
65- libc:: FUTEX_WAKE | libc:: FUTEX_PRIVATE_FLAG ,
111+ libc:: _umtx_op (
112+ futex as * const AtomicU32 as * mut _ ,
113+ libc:: UMTX_OP_WAKE_PRIVATE ,
66114 1 ,
67- ) > 0
115+ null_mut ( ) ,
116+ null_mut ( ) ,
117+ )
118+ } ;
119+ false
120+ }
121+
122+ #[ cfg( target_os = "freebsd" ) ]
123+ pub fn futex_wake_all ( futex : & AtomicU32 ) {
124+ use crate :: ptr:: null_mut;
125+ unsafe {
126+ libc:: _umtx_op (
127+ futex as * const AtomicU32 as * mut _ ,
128+ libc:: UMTX_OP_WAKE_PRIVATE ,
129+ i32:: MAX as libc:: c_ulong ,
130+ null_mut ( ) ,
131+ null_mut ( ) ,
132+ )
133+ } ;
134+ }
135+
136+ #[ cfg( target_os = "openbsd" ) ]
137+ pub fn futex_wait ( futex : & AtomicU32 , expected : u32 , timeout : Option < Duration > ) -> bool {
138+ use crate :: convert:: TryInto ;
139+ use crate :: ptr:: { null, null_mut} ;
140+ let timespec = timeout. and_then ( |d| {
141+ Some ( libc:: timespec {
142+ // Sleep forever if the timeout is longer than fits in a timespec.
143+ tv_sec : d. as_secs ( ) . try_into ( ) . ok ( ) ?,
144+ // This conversion never truncates, as subsec_nanos is always <1e9.
145+ tv_nsec : d. subsec_nanos ( ) as _ ,
146+ } )
147+ } ) ;
148+
149+ let r = unsafe {
150+ libc:: futex (
151+ futex as * const AtomicU32 as * mut u32 ,
152+ libc:: FUTEX_WAIT ,
153+ expected as i32 ,
154+ timespec. as_ref ( ) . map_or ( null ( ) , |t| t as * const libc:: timespec ) ,
155+ null_mut ( ) ,
156+ )
157+ } ;
158+
159+ r == 0 || super :: os:: errno ( ) != libc:: ETIMEDOUT
160+ }
161+
162+ #[ cfg( target_os = "openbsd" ) ]
163+ pub fn futex_wake ( futex : & AtomicU32 ) -> bool {
164+ use crate :: ptr:: { null, null_mut} ;
165+ unsafe {
166+ libc:: futex ( futex as * const AtomicU32 as * mut u32 , libc:: FUTEX_WAKE , 1 , null ( ) , null_mut ( ) )
167+ > 0
68168 }
69169}
70170
71- /// Wake up all threads that are waiting on futex_wait on this futex.
72- #[ cfg( any( target_os = "linux" , target_os = "android" ) ) ]
171+ #[ cfg( target_os = "openbsd" ) ]
73172pub fn futex_wake_all ( futex : & AtomicU32 ) {
173+ use crate :: ptr:: { null, null_mut} ;
74174 unsafe {
75- libc:: syscall (
76- libc:: SYS_futex ,
77- futex as * const AtomicU32 ,
78- libc:: FUTEX_WAKE | libc:: FUTEX_PRIVATE_FLAG ,
175+ libc:: futex (
176+ futex as * const AtomicU32 as * mut u32 ,
177+ libc:: FUTEX_WAKE ,
79178 i32:: MAX ,
179+ null ( ) ,
180+ null_mut ( ) ,
80181 ) ;
81182 }
82183}
83184
185+ #[ cfg( target_os = "dragonfly" ) ]
186+ pub fn futex_wait ( futex : & AtomicU32 , expected : u32 , timeout : Option < Duration > ) -> bool {
187+ use crate :: convert:: TryFrom ;
188+
189+ // A timeout of 0 means infinite.
190+ // We round smaller timeouts up to 1 millisecond.
191+ // Overflows are rounded up to an infinite timeout.
192+ let timeout_ms =
193+ timeout. and_then ( |d| Some ( i32:: try_from ( d. as_millis ( ) ) . ok ( ) ?. max ( 1 ) ) ) . unwrap_or ( 0 ) ;
194+
195+ let r = unsafe {
196+ libc:: umtx_sleep ( futex as * const AtomicU32 as * const i32 , expected as i32 , timeout_ms)
197+ } ;
198+
199+ r == 0 || super :: os:: errno ( ) != libc:: ETIMEDOUT
200+ }
201+
202+ // DragonflyBSD doesn't tell us how many threads are woken up, so this always returns false.
203+ #[ cfg( target_os = "dragonfly" ) ]
204+ pub fn futex_wake ( futex : & AtomicU32 ) -> bool {
205+ unsafe { libc:: umtx_wakeup ( futex as * const AtomicU32 as * const i32 , 1 ) } ;
206+ false
207+ }
208+
209+ #[ cfg( target_os = "dragonfly" ) ]
210+ pub fn futex_wake_all ( futex : & AtomicU32 ) {
211+ unsafe { libc:: umtx_wakeup ( futex as * const AtomicU32 as * const i32 , i32:: MAX ) } ;
212+ }
213+
84214#[ cfg( target_os = "emscripten" ) ]
85215extern "C" {
86216 fn emscripten_futex_wake ( addr : * const AtomicU32 , count : libc:: c_int ) -> libc:: c_int ;
0 commit comments