Commit 4da05e97 authored by Anna Henningsen's avatar Anna Henningsen Committed by Commit Bot

[SAB] Fix flake in Atomics.wait

As specified in https://tc39.github.io/ecma262/#sec-atomics-wait, the
critical section must occur before the load and comparison.

This slightly changes the `AtomicsWaitCallback` API, but in a
direction that arguably makes it more consistent.

As a drive-by fix, reset `node->waiting_` in case there
was an exception from the first callback.

Refs: https://chromium-review.googlesource.com/c/v8/v8/+/1095814
Bug: v8:7836
Cq-Include-Trybots: luci.chromium.try:linux_chromium_rel_ng
Change-Id: I577cdf76cedfe39bc61f783203b543c7c68fc238
Reviewed-on: https://chromium-review.googlesource.com/1096236Reviewed-by: 's avatarYang Guo <yangguo@chromium.org>
Commit-Queue: Ben Smith <binji@chromium.org>
Cr-Commit-Position: refs/heads/master@{#53673}
parent a0627084
...@@ -7626,7 +7626,9 @@ class V8_EXPORT Isolate { ...@@ -7626,7 +7626,9 @@ class V8_EXPORT Isolate {
/** `Atomics.wait()` was interrupted through |TerminateExecution()|. */ /** `Atomics.wait()` was interrupted through |TerminateExecution()|. */
kTerminatedExecution, kTerminatedExecution,
/** `Atomics.wait()` was stopped through |AtomicsWaitWakeHandle|. */ /** `Atomics.wait()` was stopped through |AtomicsWaitWakeHandle|. */
kAPIStopped kAPIStopped,
/** `Atomics.wait()` did not wait, as the initial condition was not met. */
kNotEqual
}; };
/** /**
...@@ -7674,10 +7676,6 @@ class V8_EXPORT Isolate { ...@@ -7674,10 +7676,6 @@ class V8_EXPORT Isolate {
* *
* This callback may schedule exceptions, *unless* |event| is equal to * This callback may schedule exceptions, *unless* |event| is equal to
* |kTerminatedExecution|. * |kTerminatedExecution|.
*
* This callback is not called if |value| did not match the expected value
* inside the SharedArrayBuffer and `Atomics.wait()` returns immediately
* because of that.
*/ */
typedef void (*AtomicsWaitCallback)(AtomicsWaitEvent event, typedef void (*AtomicsWaitCallback)(AtomicsWaitEvent event,
Local<SharedArrayBuffer> array_buffer, Local<SharedArrayBuffer> array_buffer,
......
...@@ -87,12 +87,7 @@ Object* FutexEmulation::Wait(Isolate* isolate, ...@@ -87,12 +87,7 @@ Object* FutexEmulation::Wait(Isolate* isolate,
int32_t* p = int32_t* p =
reinterpret_cast<int32_t*>(static_cast<int8_t*>(backing_store) + addr); reinterpret_cast<int32_t*>(static_cast<int8_t*>(backing_store) + addr);
if (*p != value) {
return isolate->heap()->not_equal();
}
FutexWaitListNode* node = isolate->futex_wait_list_node(); FutexWaitListNode* node = isolate->futex_wait_list_node();
node->backing_store_ = backing_store; node->backing_store_ = backing_store;
node->wait_addr_ = addr; node->wait_addr_ = addr;
node->waiting_ = true; node->waiting_ = true;
...@@ -116,24 +111,36 @@ Object* FutexEmulation::Wait(Isolate* isolate, ...@@ -116,24 +111,36 @@ Object* FutexEmulation::Wait(Isolate* isolate,
} }
} }
base::TimeTicks start_time = base::TimeTicks::Now();
base::TimeTicks timeout_time = start_time + rel_timeout;
base::TimeTicks current_time = start_time;
AtomicsWaitWakeHandle stop_handle(isolate); AtomicsWaitWakeHandle stop_handle(isolate);
isolate->RunAtomicsWaitCallback(AtomicsWaitEvent::kStartWait, array_buffer, isolate->RunAtomicsWaitCallback(AtomicsWaitEvent::kStartWait, array_buffer,
addr, value, rel_timeout_ms, &stop_handle); addr, value, rel_timeout_ms, &stop_handle);
if (isolate->has_scheduled_exception()) { if (isolate->has_scheduled_exception()) {
node->waiting_ = false;
return isolate->PromoteScheduledException(); return isolate->PromoteScheduledException();
} }
Object* result; Object* result;
AtomicsWaitEvent callback_result = AtomicsWaitEvent::kWokenUp; AtomicsWaitEvent callback_result = AtomicsWaitEvent::kWokenUp;
{ do { // Not really a loop, just makes it easier to break out early.
base::LockGuard<base::Mutex> lock_guard(mutex_.Pointer()); base::LockGuard<base::Mutex> lock_guard(mutex_.Pointer());
if (*p != value) {
result = isolate->heap()->not_equal();
callback_result = AtomicsWaitEvent::kNotEqual;
break;
}
base::TimeTicks timeout_time;
base::TimeTicks current_time;
if (use_timeout) {
current_time = base::TimeTicks::Now();
timeout_time = current_time + rel_timeout;
}
wait_list_.Pointer()->AddNode(node); wait_list_.Pointer()->AddNode(node);
while (true) { while (true) {
...@@ -205,13 +212,13 @@ Object* FutexEmulation::Wait(Isolate* isolate, ...@@ -205,13 +212,13 @@ Object* FutexEmulation::Wait(Isolate* isolate,
} }
wait_list_.Pointer()->RemoveNode(node); wait_list_.Pointer()->RemoveNode(node);
} } while (0);
node->waiting_ = false;
isolate->RunAtomicsWaitCallback(callback_result, array_buffer, addr, value, isolate->RunAtomicsWaitCallback(callback_result, array_buffer, addr, value,
rel_timeout_ms, nullptr); rel_timeout_ms, nullptr);
node->waiting_ = false;
if (isolate->has_scheduled_exception()) { if (isolate->has_scheduled_exception()) {
CHECK_NE(callback_result, AtomicsWaitEvent::kTerminatedExecution); CHECK_NE(callback_result, AtomicsWaitEvent::kTerminatedExecution);
result = isolate->PromoteScheduledException(); result = isolate->PromoteScheduledException();
......
...@@ -28175,6 +28175,12 @@ TEST(AtomicsWaitCallback) { ...@@ -28175,6 +28175,12 @@ TEST(AtomicsWaitCallback) {
{ {
v8::TryCatch try_catch(isolate); v8::TryCatch try_catch(isolate);
info.expected_offset = 8;
info.expected_timeout = std::numeric_limits<double>::infinity();
info.expected_value = 1;
info.expected_event = v8::Isolate::AtomicsWaitEvent::kNotEqual;
info.action = AtomicsWaitCallbackAction::KeepWaiting;
info.ncalls = 0;
CompileRun("Atomics.wait(int32arr, 1, 1);"); // real value is 0 != 1 CompileRun("Atomics.wait(int32arr, 1, 1);"); // real value is 0 != 1
CHECK_EQ(info.ncalls, 2); CHECK_EQ(info.ncalls, 2);
CHECK(!try_catch.HasCaught()); CHECK(!try_catch.HasCaught());
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment