55use std:: {
66 hint:: assert_unchecked,
77 ops:: ControlFlow ,
8- sync:: {
9- Arc ,
10- atomic:: { AtomicBool , Ordering as StdOrdering } ,
11- } ,
8+ sync:: Arc ,
129 thread:: { self , JoinHandle } ,
1310 time:: Duration ,
1411} ;
@@ -1421,6 +1418,7 @@ fn do_wait_critical<'gc, const IS_ASYNC: bool, const IS_I64: bool>(
14211418 gc : NoGcScope < ' gc , ' _ > ,
14221419) -> Value < ' gc > {
14231420 let slot = buffer. as_slice ( agent) . slice_from ( byte_index_in_buffer) ;
1421+ let data_block = buffer. get_data_block ( agent) . clone ( ) ;
14241422 // 14. Let WL be GetWaiterList(block, byteIndexInBuffer).
14251423 // 15. If mode is sync, then
14261424 // a. Let promiseCapability be blocking.
@@ -1429,6 +1427,14 @@ fn do_wait_critical<'gc, const IS_ASYNC: bool, const IS_I64: bool>(
14291427 // a. Let promiseCapability be ! NewPromiseCapability(%Promise%).
14301428 // b. Let resultObject be OrdinaryObjectCreate(%Object.prototype%).
14311429 // 17. Perform EnterCriticalSection(WL).
1430+
1431+ // SAFETY: buffer is a valid SharedArrayBuffer and cannot be detached. A 0-sized SAB would
1432+ // have a dangling data block, but Atomics.wait requires `byteIndex` to be within bounds,
1433+ // so a 0-sized SAB would have been rejected earlier with a RangeError.
1434+ let waiters = unsafe { data_block. get_or_init_waiters ( ) } ;
1435+ let waiter_record = WaiterRecord :: new_shared ( ) ;
1436+ let mut guard = waiters. lock ( ) . unwrap ( ) ;
1437+
14321438 // 18. Let elementType be TypedArrayElementType(typedArray).
14331439 // 19. Let w be GetValueFromBuffer(buffer, byteIndexInBuffer, elementType, true, seq-cst).
14341440 let v_not_equal_to_w = if IS_I64 {
@@ -1447,6 +1453,8 @@ fn do_wait_critical<'gc, const IS_ASYNC: bool, const IS_I64: bool>(
14471453 // 20. If v ≠ w, then
14481454 if v_not_equal_to_w {
14491455 // a. Perform LeaveCriticalSection(WL).
1456+ drop ( guard) ;
1457+
14501458 // b. If mode is sync, return "not-equal".
14511459 if !IS_ASYNC {
14521460 return BUILTIN_STRING_MEMORY . not_equal . into ( ) ;
@@ -1464,6 +1472,7 @@ fn do_wait_critical<'gc, const IS_ASYNC: bool, const IS_I64: bool>(
14641472 // timeouts. Asynchronous immediate timeouts have special handling
14651473 // in order to fail fast and avoid unnecessary Promise jobs.
14661474 // b. Perform LeaveCriticalSection(WL).
1475+ drop ( guard) ;
14671476 // c. Perform ! CreateDataPropertyOrThrow(resultObject, "async", false).
14681477 // d. Perform ! CreateDataPropertyOrThrow(resultObject, "value", "timed-out").
14691478 let result_object =
@@ -1484,32 +1493,10 @@ fn do_wait_critical<'gc, const IS_ASYNC: bool, const IS_I64: bool>(
14841493 // [[Result]]: "ok"
14851494 // }.
14861495 // 28. Perform AddWaiter(WL, waiterRecord).
1496+ guard. push_to_list ( byte_index_in_buffer, waiter_record. clone ( ) ) ;
14871497 // 29. If mode is sync, then
14881498 if !IS_ASYNC {
1489- let data_block = buffer. get_data_block ( agent) ;
1490- // SAFETY: buffer is a valid SharedArrayBuffer and cannot be detached. A 0-sized SAB would
1491- // have a dangling data block, but Atomics.wait requires `byteIndex` to be within bounds,
1492- // so a 0-sized SAB would have been rejected earlier with a RangeError.
1493- let waiters = unsafe { data_block. get_or_init_waiters ( ) } ;
1494- let waiter_record = WaiterRecord :: new_shared ( ) ;
1495- let mut guard = waiters. lock ( ) . unwrap ( ) ;
1496-
1497- // Re-read value under critical section to avoid TOCTOU race.
1498- let slot = data_block. as_racy_slice ( ) . slice_from ( byte_index_in_buffer) ;
1499- let v_changed = if IS_I64 {
1500- let slot = unsafe { slot. as_aligned :: < u64 > ( ) . unwrap_unchecked ( ) } ;
1501- v as u64 != slot. load ( Ordering :: SeqCst )
1502- } else {
1503- let slot = unsafe { slot. as_aligned :: < u32 > ( ) . unwrap_unchecked ( ) } ;
1504- v as i32 as u32 != slot. load ( Ordering :: SeqCst )
1505- } ;
1506- if v_changed {
1507- return BUILTIN_STRING_MEMORY . not_equal . into ( ) ;
1508- }
1509-
15101499 // a. Perform SuspendThisAgent(WL, waiterRecord).
1511- guard. push_to_list ( byte_index_in_buffer, waiter_record. clone ( ) ) ;
1512-
15131500 if t == u64:: MAX {
15141501 waiter_record. wait ( guard) ;
15151502 } else {
@@ -1532,17 +1519,21 @@ fn do_wait_critical<'gc, const IS_ASYNC: bool, const IS_I64: bool>(
15321519 let promise = Global :: new ( agent, promise_capability. promise . unbind ( ) ) ;
15331520 // 30. Else if timeoutTime is finite, then
15341521 // a. Perform EnqueueAtomicsWaitAsyncTimeoutJob(WL, waiterRecord).
1535- let buffer = buffer. get_data_block ( agent) . clone ( ) ;
1522+
1523+ let data_block_clone = data_block. clone ( ) ;
15361524 enqueue_atomics_wait_async_job :: < IS_I64 > (
15371525 agent,
1538- buffer ,
1526+ data_block_clone ,
15391527 byte_index_in_buffer,
1540- v ,
1528+ waiter_record . clone ( ) ,
15411529 t,
15421530 promise,
15431531 gc,
15441532 ) ;
1533+
15451534 // 31. Perform LeaveCriticalSection(WL).
1535+ drop ( guard) ;
1536+
15461537 // 33. Perform ! CreateDataPropertyOrThrow(resultObject, "async", true).
15471538 // 34. Perform ! CreateDataPropertyOrThrow(resultObject, "value", promiseCapability.[[Promise]]).
15481539 let result_object =
@@ -1672,7 +1663,7 @@ impl WaitAsyncJob {
16721663 // c. Perform LeaveCriticalSection(WL).
16731664 let promise_capability = PromiseCapability :: from_promise ( promise, true ) ;
16741665 let result = match result {
1675- WaitResult :: Ok | WaitResult :: NotEqual => BUILTIN_STRING_MEMORY . ok . into ( ) ,
1666+ WaitResult :: Ok => BUILTIN_STRING_MEMORY . ok . into ( ) ,
16761667 WaitResult :: TimedOut => BUILTIN_STRING_MEMORY . timed_out . into ( ) ,
16771668 } ;
16781669 unwrap_try ( promise_capability. try_resolve ( agent, result, gc) ) ;
@@ -1688,43 +1679,21 @@ impl WaitAsyncJob {
16881679/// unused.
16891680fn enqueue_atomics_wait_async_job < const IS_I64 : bool > (
16901681 agent : & mut Agent ,
1691- buffer : SharedDataBlock ,
1682+ data_block : SharedDataBlock ,
16921683 byte_index_in_buffer : usize ,
1693- v : i64 ,
1684+ waiter_record : Arc < WaiterRecord > ,
16941685 t : u64 ,
16951686 promise : Global < Promise > ,
16961687 gc : NoGcScope ,
16971688) {
16981689 // 1. Let timeoutJob be a new Job Abstract Closure with no parameters that
16991690 // captures WL and waiterRecord and performs the following steps when
17001691 // called:
1701- let signal = Arc :: new ( AtomicBool :: new ( false ) ) ;
1702- let s = signal. clone ( ) ;
17031692 let handle = thread:: spawn ( move || {
17041693 // SAFETY: buffer is a cloned SharedDataBlock; non-dangling.
1705- let waiters = unsafe { buffer. get_or_init_waiters ( ) } ;
1706- let waiter_record = WaiterRecord :: new_shared ( ) ;
1694+ let waiters = unsafe { data_block. get_or_init_waiters ( ) } ;
17071695 let mut guard = waiters. lock ( ) . unwrap ( ) ;
17081696
1709- // Re-check the value under the critical section.
1710- let slot = buffer. as_racy_slice ( ) . slice_from ( byte_index_in_buffer) ;
1711- let v_not_equal = if IS_I64 {
1712- let slot = unsafe { slot. as_aligned :: < u64 > ( ) . unwrap_unchecked ( ) } ;
1713- v as u64 != slot. load ( Ordering :: SeqCst )
1714- } else {
1715- let slot = unsafe { slot. as_aligned :: < u32 > ( ) . unwrap_unchecked ( ) } ;
1716- v as i32 as u32 != slot. load ( Ordering :: SeqCst )
1717- } ;
1718-
1719- // Signal the main thread that we have the lock and are about to sleep.
1720- s. store ( true , StdOrdering :: Release ) ;
1721-
1722- if v_not_equal {
1723- return WaitResult :: NotEqual ;
1724- }
1725-
1726- guard. push_to_list ( byte_index_in_buffer, waiter_record. clone ( ) ) ;
1727-
17281697 if t == u64:: MAX {
17291698 waiter_record. wait ( guard) ;
17301699 } else {
@@ -1735,6 +1704,8 @@ fn enqueue_atomics_wait_async_job<const IS_I64: bool>(
17351704 guard. remove_from_list ( byte_index_in_buffer, waiter_record) ;
17361705
17371706 // 31. Perform LeaveCriticalSection(WL).
1707+ drop ( guard) ;
1708+
17381709 // 32. If mode is sync, return waiterRecord.[[Result]].
17391710 return WaitResult :: TimedOut ;
17401711 }
@@ -1749,9 +1720,6 @@ fn enqueue_atomics_wait_async_job<const IS_I64: bool>(
17491720 _has_timeout : t != u64:: MAX ,
17501721 } ) ) ) ,
17511722 } ;
1752- while !signal. load ( StdOrdering :: Acquire ) {
1753- // Wait until the thread has started up and is about to go to sleep.
1754- }
17551723 // 2. Let now be the time value (UTC) identifying the current time.
17561724 // 3. Let currentRealm be the current Realm Record.
17571725 // 4. Perform HostEnqueueTimeoutJob(timeoutJob, currentRealm, 𝔽(waiterRecord.[[TimeoutTime]]) - now).
0 commit comments