aboutsummaryrefslogtreecommitdiff
path: root/deps/v8/src/futex-emulation.cc
diff options
context:
space:
mode:
Diffstat (limited to 'deps/v8/src/futex-emulation.cc')
-rw-r--r--deps/v8/src/futex-emulation.cc89
1 files changed, 67 insertions, 22 deletions
diff --git a/deps/v8/src/futex-emulation.cc b/deps/v8/src/futex-emulation.cc
index 5a0ce07f1a..b0e514e8af 100644
--- a/deps/v8/src/futex-emulation.cc
+++ b/deps/v8/src/futex-emulation.cc
@@ -21,6 +21,23 @@ base::LazyInstance<FutexWaitList>::type FutexEmulation::wait_list_ =
LAZY_INSTANCE_INITIALIZER;
+void FutexWaitListNode::NotifyWake() {
+ // Lock the FutexEmulation mutex before notifying. We know that the mutex
+ // will have been unlocked if we are currently waiting on the condition
+ // variable.
+ //
+ // The mutex may also not be locked if the other thread is currently handling
+ // interrupts, or if FutexEmulation::Wait was just called and the mutex
+ // hasn't been locked yet. In either of those cases, we set the interrupted
+ // flag to true, which will be tested after the mutex is re-locked.
+ base::LockGuard<base::Mutex> lock_guard(FutexEmulation::mutex_.Pointer());
+ if (waiting_) {
+ cond_.NotifyOne();
+ interrupted_ = true;
+ }
+}
+
+
FutexWaitList::FutexWaitList() : head_(nullptr), tail_(nullptr) {}
@@ -58,12 +75,6 @@ void FutexWaitList::RemoveNode(FutexWaitListNode* node) {
Object* FutexEmulation::Wait(Isolate* isolate,
Handle<JSArrayBuffer> array_buffer, size_t addr,
int32_t value, double rel_timeout_ms) {
- // We never want to wait longer than this amount of time; this way we can
- // interrupt this thread even if this is an "infinitely blocking" wait.
- // TODO(binji): come up with a better way of interrupting only when
- // necessary, rather than busy-waiting.
- const base::TimeDelta kMaxWaitTime = base::TimeDelta::FromMilliseconds(50);
-
DCHECK(addr < NumberToSize(isolate, array_buffer->byte_length()));
void* backing_store = array_buffer->backing_store();
@@ -103,41 +114,75 @@ 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;
wait_list_.Pointer()->AddNode(node);
Object* result;
while (true) {
- base::TimeTicks current_time = base::TimeTicks::Now();
- if (use_timeout && current_time > timeout_time) {
- result = Smi::FromInt(Result::kTimedOut);
- break;
+ bool interrupted = node->interrupted_;
+ node->interrupted_ = false;
+
+ // Unlock the mutex here to prevent deadlock from lock ordering between
+ // mutex_ and mutexes locked by HandleInterrupts.
+ mutex_.Pointer()->Unlock();
+
+ // Because the mutex is unlocked, we have to be careful about not dropping
+ // an interrupt. The notification can happen in three different places:
+ // 1) Before Wait is called: the notification will be dropped, but
+ // interrupted_ will be set to 1. This will be checked below.
+ // 2) After interrupted has been checked here, but before mutex_ is
+ // acquired: interrupted is checked again below, with mutex_ locked.
+ // Because the wakeup signal also acquires mutex_, we know it will not
+ // be able to notify until mutex_ is released below, when waiting on the
+ // condition variable.
+ // 3) After the mutex is released in the call to WaitFor(): this
+ // notification will wake up the condition variable. node->waiting() will
+ // be false, so we'll loop and then check interrupts.
+ if (interrupted) {
+ Object* interrupt_object = isolate->stack_guard()->HandleInterrupts();
+ if (interrupt_object->IsException()) {
+ result = interrupt_object;
+ mutex_.Pointer()->Lock();
+ break;
+ }
}
- base::TimeDelta time_until_timeout = timeout_time - current_time;
- base::TimeDelta time_to_wait =
- (use_timeout && time_until_timeout < kMaxWaitTime) ? time_until_timeout
- : kMaxWaitTime;
+ mutex_.Pointer()->Lock();
- bool wait_for_result = node->cond_.WaitFor(mutex_.Pointer(), time_to_wait);
- USE(wait_for_result);
+ if (node->interrupted_) {
+ // An interrupt occured while the mutex_ was unlocked. Don't wait yet.
+ continue;
+ }
if (!node->waiting_) {
result = Smi::FromInt(Result::kOk);
break;
}
- // Spurious wakeup or timeout. Potentially handle interrupts before
- // continuing to wait.
- Object* interrupt_object = isolate->stack_guard()->HandleInterrupts();
- if (interrupt_object->IsException()) {
- result = interrupt_object;
- break;
+ // No interrupts, now wait.
+ if (use_timeout) {
+ current_time = base::TimeTicks::Now();
+ if (current_time >= timeout_time) {
+ result = Smi::FromInt(Result::kTimedOut);
+ break;
+ }
+
+ base::TimeDelta time_until_timeout = timeout_time - current_time;
+ DCHECK(time_until_timeout.InMicroseconds() >= 0);
+ bool wait_for_result =
+ node->cond_.WaitFor(mutex_.Pointer(), time_until_timeout);
+ USE(wait_for_result);
+ } else {
+ node->cond_.Wait(mutex_.Pointer());
}
+
+ // Spurious wakeup, interrupt or timeout.
}
wait_list_.Pointer()->RemoveNode(node);
+ node->waiting_ = false;
return result;
}