1
1
#![ cfg( any(
2
2
target_os = "linux" ,
3
3
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" ,
5
8
) ) ]
6
9
7
10
use crate :: sync:: atomic:: AtomicU32 ;
@@ -12,7 +15,7 @@ use crate::time::Duration;
12
15
/// Returns directly if the futex doesn't hold the expected value.
13
16
///
14
17
/// 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" ) ) ]
16
19
pub fn futex_wait ( futex : & AtomicU32 , expected : u32 , timeout : Option < Duration > ) -> bool {
17
20
use super :: time:: Timespec ;
18
21
use crate :: ptr:: null;
@@ -30,18 +33,43 @@ pub fn futex_wait(futex: &AtomicU32, expected: u32, timeout: Option<Duration>) -
30
33
return true ;
31
34
}
32
35
33
- // Use FUTEX_WAIT_BITSET rather than FUTEX_WAIT to be able to give an
34
- // absolute time rather than a relative time.
35
36
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
+ }
45
73
} ;
46
74
47
75
match ( r < 0 ) . then ( super :: os:: errno) {
@@ -56,31 +84,133 @@ pub fn futex_wait(futex: &AtomicU32, expected: u32, timeout: Option<Duration>) -
56
84
///
57
85
/// Returns true if this actually woke up such a thread,
58
86
/// 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.
59
97
#[ 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" ) ]
60
108
pub fn futex_wake ( futex : & AtomicU32 ) -> bool {
109
+ use crate :: ptr:: null_mut;
61
110
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 ,
66
114
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
68
168
}
69
169
}
70
170
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" ) ]
73
172
pub fn futex_wake_all ( futex : & AtomicU32 ) {
173
+ use crate :: ptr:: { null, null_mut} ;
74
174
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 ,
79
178
i32:: MAX ,
179
+ null ( ) ,
180
+ null_mut ( ) ,
80
181
) ;
81
182
}
82
183
}
83
184
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
+
84
214
#[ cfg( target_os = "emscripten" ) ]
85
215
extern "C" {
86
216
fn emscripten_futex_wake ( addr : * const AtomicU32 , count : libc:: c_int ) -> libc:: c_int ;
0 commit comments