139139#![ allow( clippy:: expl_impl_clone_on_copy) ]
140140#![ allow( unstable_name_collisions) ]
141141
142- use std:: {
143- cell:: UnsafeCell ,
144- sync:: { atomic:: Ordering , Condvar , Mutex } ,
145- } ;
142+ use std:: { cell:: UnsafeCell , sync:: atomic:: Ordering } ;
146143
147- use intrusive_collections:: { intrusive_adapter, LinkedList , LinkedListLink , UnsafeRef } ;
148144use sptr:: Strict ;
149145
150146use crate :: {
151147 builtins:: {
152148 array_buffer:: { utils:: SliceRef , SharedArrayBuffer } ,
153149 typed_array:: Element ,
154150 } ,
155- small_map:: { Entry , SmallMap } ,
156151 sys:: time:: { Duration , Instant } ,
157152 JsNativeError , JsResult ,
158153} ;
159154
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 } ;
173157
174- intrusive_adapter ! ( FutexWaiterAdapter = UnsafeRef < FutexWaiter > : FutexWaiter { link : LinkedListLink } ) ;
158+ use intrusive_collections :: { intrusive_adapter , LinkedList , LinkedListLink , UnsafeRef } ;
175159
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+ } ;
181164
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 ;
199211 } ;
200212
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+ } ;
202218
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+ }
206225 }
207- }
208226
209- if wl. get ( ) . is_empty ( ) {
210- wl. remove ( ) ;
211- }
227+ if wl. get ( ) . is_empty ( ) {
228+ wl. remove ( ) ;
229+ }
212230
213- max_count
214- }
231+ max_count
232+ }
215233
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+ } ;
235247
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) ;
252252 }
253253
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+ }
256275 }
257276 }
258277}
@@ -281,10 +300,7 @@ pub(super) unsafe fn wait<E: Element + PartialEq>(
281300 // 10. Let block be buffer.[[ArrayBufferData]].
282301 // 11. Let WL be GetWaiterList(block, indexedPosition).
283302 // 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 ( ) ?;
288304
289305 let time_info = timeout. map ( |timeout| ( Instant :: now ( ) , timeout) ) ;
290306
@@ -307,7 +323,7 @@ pub(super) unsafe fn wait<E: Element + PartialEq>(
307323 // 17. Perform AddWaiter(WL, W).
308324
309325 // 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 ( ) ) ;
311327 let waiter_ptr = waiter. get ( ) ;
312328
313329 // 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
385401
386402 // 7. Let WL be GetWaiterList(block, indexedPosition).
387403 // 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 ( ) ?;
392405
393406 // 9. Let S be RemoveWaiters(WL, c).
394407 // 10. For each element W of S, do
0 commit comments