139
139
#![ allow( clippy:: expl_impl_clone_on_copy) ]
140
140
#![ allow( unstable_name_collisions) ]
141
141
142
- use std:: {
143
- cell:: UnsafeCell ,
144
- sync:: { atomic:: Ordering , Condvar , Mutex } ,
145
- } ;
142
+ use std:: { cell:: UnsafeCell , sync:: atomic:: Ordering } ;
146
143
147
- use intrusive_collections:: { intrusive_adapter, LinkedList , LinkedListLink , UnsafeRef } ;
148
144
use sptr:: Strict ;
149
145
150
146
use crate :: {
151
147
builtins:: {
152
148
array_buffer:: { utils:: SliceRef , SharedArrayBuffer } ,
153
149
typed_array:: Element ,
154
150
} ,
155
- small_map:: { Entry , SmallMap } ,
156
151
sys:: time:: { Duration , Instant } ,
157
152
JsNativeError , JsResult ,
158
153
} ;
159
154
160
- /// Map of shared data addresses and its corresponding list of agents waiting on that location.
161
- pub ( crate ) static CRITICAL_SECTION : Mutex < FutexWaiters > = Mutex :: new ( FutexWaiters {
162
- waiters : SmallMap :: new ( ) ,
163
- } ) ;
164
-
165
- /// A waiter of a memory address.
166
- #[ derive( Debug , Default ) ]
167
- pub ( crate ) struct FutexWaiter {
168
- pub ( super ) link : LinkedListLink ,
169
- pub ( super ) cond_var : Condvar ,
170
- pub ( super ) waiting : bool ,
171
- addr : usize ,
172
- }
155
+ mod sync {
156
+ use std:: sync:: { Condvar , Mutex , MutexGuard } ;
173
157
174
- intrusive_adapter ! ( FutexWaiterAdapter = UnsafeRef < FutexWaiter > : FutexWaiter { link : LinkedListLink } ) ;
158
+ use intrusive_collections :: { intrusive_adapter , LinkedList , LinkedListLink , UnsafeRef } ;
175
159
176
- /// List of memory addresses and its corresponding list of waiters for that address.
177
- #[ derive( Debug ) ]
178
- pub ( crate ) struct FutexWaiters {
179
- waiters : SmallMap < usize , LinkedList < FutexWaiterAdapter > , 16 > ,
180
- }
160
+ use crate :: {
161
+ small_map:: { Entry , SmallMap } ,
162
+ JsNativeError , JsResult ,
163
+ } ;
181
164
182
- impl FutexWaiters {
183
- /// Notifies at most `max_count` waiters that are waiting on the address `addr`, and
184
- /// returns the number of waiters that were notified.
185
- ///
186
- /// Equivalent to [`RemoveWaiters`][remove] and [`NotifyWaiter`][notify], but in a single operation.
187
- ///
188
- /// [remove]: https://tc39.es/ecma262/#sec-removewaiters
189
- /// [notify]: https://tc39.es/ecma262/#sec-notifywaiter
190
- pub ( crate ) fn notify_many ( & mut self , addr : usize , max_count : u64 ) -> u64 {
191
- let Entry :: Occupied ( mut wl) = self . waiters . entry ( addr) else {
192
- return 0 ;
193
- } ;
194
-
195
- for i in 0 ..max_count {
196
- let Some ( elem) = wl. get_mut ( ) . pop_front ( ) else {
197
- wl. remove ( ) ;
198
- return i;
165
+ /// A waiter of a memory address.
166
+ #[ derive( Debug , Default ) ]
167
+ pub ( crate ) struct FutexWaiter {
168
+ pub ( super ) link : LinkedListLink ,
169
+ pub ( super ) cond_var : Condvar ,
170
+ pub ( super ) waiting : bool ,
171
+ addr : usize ,
172
+ }
173
+
174
+ intrusive_adapter ! ( FutexWaiterAdapter = UnsafeRef <FutexWaiter >: FutexWaiter { link: LinkedListLink } ) ;
175
+
176
+ /// List of memory addresses and its corresponding list of waiters for that address.
177
+ #[ derive( Debug ) ]
178
+ pub ( super ) struct FutexWaiters {
179
+ waiters : SmallMap < usize , LinkedList < FutexWaiterAdapter > , 16 > ,
180
+ }
181
+
182
+ // SAFETY: `FutexWaiters` is not constructable outside its `get` method, and it's only exposed by
183
+ // a global lock, meaning the inner data of `FutexWaiters` (which includes non-Send pointers)
184
+ // can only be accessed by a single thread at once.
185
+ unsafe impl Send for FutexWaiters { }
186
+
187
+ impl FutexWaiters {
188
+ /// Gets the map of all shared data addresses and its corresponding list of agents waiting on that location.
189
+ pub ( super ) fn get ( ) -> JsResult < MutexGuard < ' static , Self > > {
190
+ static CRITICAL_SECTION : Mutex < FutexWaiters > = Mutex :: new ( FutexWaiters {
191
+ waiters : SmallMap :: new ( ) ,
192
+ } ) ;
193
+
194
+ CRITICAL_SECTION . lock ( ) . map_err ( |_| {
195
+ JsNativeError :: typ ( )
196
+ . with_message ( "failed to synchronize with the agent cluster" )
197
+ . into ( )
198
+ } )
199
+ }
200
+
201
+ /// Notifies at most `max_count` waiters that are waiting on the address `addr`, and
202
+ /// returns the number of waiters that were notified.
203
+ ///
204
+ /// Equivalent to [`RemoveWaiters`][remove] and [`NotifyWaiter`][notify], but in a single operation.
205
+ ///
206
+ /// [remove]: https://tc39.es/ecma262/#sec-removewaiters
207
+ /// [notify]: https://tc39.es/ecma262/#sec-notifywaiter
208
+ pub ( super ) fn notify_many ( & mut self , addr : usize , max_count : u64 ) -> u64 {
209
+ let Entry :: Occupied ( mut wl) = self . waiters . entry ( addr) else {
210
+ return 0 ;
199
211
} ;
200
212
201
- elem. cond_var . notify_one ( ) ;
213
+ for i in 0 ..max_count {
214
+ let Some ( elem) = wl. get_mut ( ) . pop_front ( ) else {
215
+ wl. remove ( ) ;
216
+ return i;
217
+ } ;
202
218
203
- // SAFETY: all elements of the waiters list are guaranteed to be valid.
204
- unsafe {
205
- ( * UnsafeRef :: into_raw ( elem) ) . waiting = false ;
219
+ elem. cond_var . notify_one ( ) ;
220
+
221
+ // SAFETY: all elements of the waiters list are guaranteed to be valid.
222
+ unsafe {
223
+ ( * UnsafeRef :: into_raw ( elem) ) . waiting = false ;
224
+ }
206
225
}
207
- }
208
226
209
- if wl. get ( ) . is_empty ( ) {
210
- wl. remove ( ) ;
211
- }
227
+ if wl. get ( ) . is_empty ( ) {
228
+ wl. remove ( ) ;
229
+ }
212
230
213
- max_count
214
- }
231
+ max_count
232
+ }
215
233
216
- /// # Safety
217
- ///
218
- /// - `node` must NOT be linked to an existing waiter list.
219
- /// - `node` must always point to a valid instance of `FutexWaiter` until `node` is
220
- /// removed from its linked list. This can happen by either `remove_waiter` or `notify_many`.
221
- pub ( crate ) unsafe fn add_waiter ( & mut self , node : * mut FutexWaiter , addr : usize ) {
222
- // SAFETY: `node` must point to a valid instance.
223
- let node = unsafe {
224
- debug_assert ! ( !( * node) . link. is_linked( ) ) ;
225
- ( * node) . waiting = true ;
226
- ( * node) . addr = addr;
227
- UnsafeRef :: from_raw ( node)
228
- } ;
229
-
230
- self . waiters
231
- . entry ( addr)
232
- . or_insert_with ( || LinkedList :: new ( FutexWaiterAdapter :: new ( ) ) )
233
- . push_back ( node) ;
234
- }
234
+ /// # Safety
235
+ ///
236
+ /// - `node` must NOT be linked to an existing waiter list.
237
+ /// - `node` must always point to a valid instance of `FutexWaiter` until `node` is
238
+ /// removed from its linked list. This can happen by either `remove_waiter` or `notify_many`.
239
+ pub ( super ) unsafe fn add_waiter ( & mut self , node : * mut FutexWaiter , addr : usize ) {
240
+ // SAFETY: `node` must point to a valid instance.
241
+ let node = unsafe {
242
+ debug_assert ! ( !( * node) . link. is_linked( ) ) ;
243
+ ( * node) . waiting = true ;
244
+ ( * node) . addr = addr;
245
+ UnsafeRef :: from_raw ( node)
246
+ } ;
235
247
236
- /// # Safety
237
- ///
238
- /// - `node` must point to a valid instance of `FutexWaiter`.
239
- /// - `node` must be inside the wait list associated with `node.addr`.
240
- pub ( crate ) unsafe fn remove_waiter ( & mut self , node : * mut FutexWaiter ) {
241
- // SAFETY: `node` must point to a valid instance.
242
- let addr = unsafe { ( * node) . addr } ;
243
-
244
- let mut wl = match self . waiters . entry ( addr) {
245
- Entry :: Occupied ( wl) => wl,
246
- Entry :: Vacant ( _) => return ,
247
- } ;
248
-
249
- // SAFETY: `node` must be inside the wait list associated with `node.addr`.
250
- unsafe {
251
- wl. get_mut ( ) . cursor_mut_from_ptr ( node) . remove ( ) ;
248
+ self . waiters
249
+ . entry ( addr)
250
+ . or_insert_with ( || LinkedList :: new ( FutexWaiterAdapter :: new ( ) ) )
251
+ . push_back ( node) ;
252
252
}
253
253
254
- if wl. get ( ) . is_empty ( ) {
255
- wl. remove ( ) ;
254
+ /// # Safety
255
+ ///
256
+ /// - `node` must point to a valid instance of `FutexWaiter`.
257
+ /// - `node` must be inside the wait list associated with `node.addr`.
258
+ pub ( super ) unsafe fn remove_waiter ( & mut self , node : * mut FutexWaiter ) {
259
+ // SAFETY: `node` must point to a valid instance.
260
+ let addr = unsafe { ( * node) . addr } ;
261
+
262
+ let mut wl = match self . waiters . entry ( addr) {
263
+ Entry :: Occupied ( wl) => wl,
264
+ Entry :: Vacant ( _) => return ,
265
+ } ;
266
+
267
+ // SAFETY: `node` must be inside the wait list associated with `node.addr`.
268
+ unsafe {
269
+ wl. get_mut ( ) . cursor_mut_from_ptr ( node) . remove ( ) ;
270
+ }
271
+
272
+ if wl. get ( ) . is_empty ( ) {
273
+ wl. remove ( ) ;
274
+ }
256
275
}
257
276
}
258
277
}
@@ -281,10 +300,7 @@ pub(super) unsafe fn wait<E: Element + PartialEq>(
281
300
// 10. Let block be buffer.[[ArrayBufferData]].
282
301
// 11. Let WL be GetWaiterList(block, indexedPosition).
283
302
// 12. Perform EnterCriticalSection(WL).
284
- let mut waiters = CRITICAL_SECTION . lock ( ) . map_err ( |_| {
285
- // avoids exposing internals of our implementation.
286
- JsNativeError :: typ ( ) . with_message ( "failed to synchronize with the agent cluster" )
287
- } ) ?;
303
+ let mut waiters = sync:: FutexWaiters :: get ( ) ?;
288
304
289
305
let time_info = timeout. map ( |timeout| ( Instant :: now ( ) , timeout) ) ;
290
306
@@ -307,7 +323,7 @@ pub(super) unsafe fn wait<E: Element + PartialEq>(
307
323
// 17. Perform AddWaiter(WL, W).
308
324
309
325
// ensure we can have aliased pointers to the waiter in a sound way.
310
- let waiter = UnsafeCell :: new ( FutexWaiter :: default ( ) ) ;
326
+ let waiter = UnsafeCell :: new ( sync :: FutexWaiter :: default ( ) ) ;
311
327
let waiter_ptr = waiter. get ( ) ;
312
328
313
329
// SAFETY: waiter is valid and we call `remove_node` below.
@@ -385,10 +401,7 @@ pub(super) fn notify(buffer: &SharedArrayBuffer, offset: usize, count: u64) -> J
385
401
386
402
// 7. Let WL be GetWaiterList(block, indexedPosition).
387
403
// 8. Perform EnterCriticalSection(WL).
388
- let mut waiters = CRITICAL_SECTION . lock ( ) . map_err ( |_| {
389
- // avoids exposing internals of our implementation.
390
- JsNativeError :: typ ( ) . with_message ( "failed to synchronize with the agent cluster" )
391
- } ) ?;
404
+ let mut waiters = sync:: FutexWaiters :: get ( ) ?;
392
405
393
406
// 9. Let S be RemoveWaiters(WL, c).
394
407
// 10. For each element W of S, do
0 commit comments