Commit edd6803f authored by Anna Henningsen's avatar Anna Henningsen Committed by Commit Bot

[SAB] Document and tighten FutexEmulation `mutex_` scope

Document what pieces of data the global `FutexEmulation::mutex_`
mutex protects from concurrent access, and reduce the scope
in which said mutex is locked during `FutexEmulation::Wait()`
to match that description more closely.

Change-Id: I0764efabac06814d83ed5c4af4eb7da34af47cab
Reviewed-on: https://chromium-review.googlesource.com/1074689
Commit-Queue: Ben Smith <binji@chromium.org>
Reviewed-by: 's avatarBen Smith <binji@chromium.org>
Cr-Commit-Position: refs/heads/master@{#53429}
parent 2301ffe7
...@@ -81,8 +81,6 @@ Object* FutexEmulation::Wait(Isolate* isolate, ...@@ -81,8 +81,6 @@ 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);
base::LockGuard<base::Mutex> lock_guard(mutex_.Pointer());
if (*p != value) { if (*p != value) {
return isolate->heap()->not_equal(); return isolate->heap()->not_equal();
} }
...@@ -116,72 +114,76 @@ Object* FutexEmulation::Wait(Isolate* isolate, ...@@ -116,72 +114,76 @@ Object* FutexEmulation::Wait(Isolate* isolate,
base::TimeTicks timeout_time = start_time + rel_timeout; base::TimeTicks timeout_time = start_time + rel_timeout;
base::TimeTicks current_time = start_time; base::TimeTicks current_time = start_time;
wait_list_.Pointer()->AddNode(node);
Object* result; Object* result;
while (true) { {
bool interrupted = node->interrupted_; base::LockGuard<base::Mutex> lock_guard(mutex_.Pointer());
node->interrupted_ = false; wait_list_.Pointer()->AddNode(node);
// Unlock the mutex here to prevent deadlock from lock ordering between while (true) {
// mutex_ and mutexes locked by HandleInterrupts. bool interrupted = node->interrupted_;
mutex_.Pointer()->Unlock(); node->interrupted_ = false;
// Because the mutex is unlocked, we have to be careful about not dropping // Unlock the mutex here to prevent deadlock from lock ordering between
// an interrupt. The notification can happen in three different places: // mutex_ and mutexes locked by HandleInterrupts.
// 1) Before Wait is called: the notification will be dropped, but mutex_.Pointer()->Unlock();
// interrupted_ will be set to 1. This will be checked below.
// 2) After interrupted has been checked here, but before mutex_ is // Because the mutex is unlocked, we have to be careful about not dropping
// acquired: interrupted is checked again below, with mutex_ locked. // an interrupt. The notification can happen in three different places:
// Because the wakeup signal also acquires mutex_, we know it will not // 1) Before Wait is called: the notification will be dropped, but
// be able to notify until mutex_ is released below, when waiting on the // interrupted_ will be set to 1. This will be checked below.
// condition variable. // 2) After interrupted has been checked here, but before mutex_ is
// 3) After the mutex is released in the call to WaitFor(): this // acquired: interrupted is checked again below, with mutex_ locked.
// notification will wake up the condition variable. node->waiting() will // Because the wakeup signal also acquires mutex_, we know it will not
// be false, so we'll loop and then check interrupts. // be able to notify until mutex_ is released below, when waiting on
if (interrupted) { // the condition variable.
Object* interrupt_object = isolate->stack_guard()->HandleInterrupts(); // 3) After the mutex is released in the call to WaitFor(): this
if (interrupt_object->IsException(isolate)) { // notification will wake up the condition variable. node->waiting() will
result = interrupt_object; // be false, so we'll loop and then check interrupts.
mutex_.Pointer()->Lock(); if (interrupted) {
break; Object* interrupt_object = isolate->stack_guard()->HandleInterrupts();
if (interrupt_object->IsException(isolate)) {
result = interrupt_object;
mutex_.Pointer()->Lock();
break;
}
} }
}
mutex_.Pointer()->Lock();
if (node->interrupted_) { mutex_.Pointer()->Lock();
// An interrupt occurred while the mutex_ was unlocked. Don't wait yet.
continue;
}
if (!node->waiting_) { if (node->interrupted_) {
result = isolate->heap()->ok(); // An interrupt occurred while the mutex_ was unlocked. Don't wait yet.
break; continue;
} }
// No interrupts, now wait. if (!node->waiting_) {
if (use_timeout) { result = isolate->heap()->ok();
current_time = base::TimeTicks::Now();
if (current_time >= timeout_time) {
result = isolate->heap()->timed_out();
break; break;
} }
base::TimeDelta time_until_timeout = timeout_time - current_time; // No interrupts, now wait.
DCHECK_GE(time_until_timeout.InMicroseconds(), 0); if (use_timeout) {
bool wait_for_result = current_time = base::TimeTicks::Now();
node->cond_.WaitFor(mutex_.Pointer(), time_until_timeout); if (current_time >= timeout_time) {
USE(wait_for_result); result = isolate->heap()->timed_out();
} else { break;
node->cond_.Wait(mutex_.Pointer()); }
base::TimeDelta time_until_timeout = timeout_time - current_time;
DCHECK_GE(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.
} }
// Spurious wakeup, interrupt or timeout. wait_list_.Pointer()->RemoveNode(node);
} }
wait_list_.Pointer()->RemoveNode(node);
node->waiting_ = false; node->waiting_ = false;
return result; return result;
......
...@@ -52,10 +52,13 @@ class FutexWaitListNode { ...@@ -52,10 +52,13 @@ class FutexWaitListNode {
friend class FutexWaitList; friend class FutexWaitList;
base::ConditionVariable cond_; base::ConditionVariable cond_;
// prev_ and next_ are protected by FutexEmulation::mutex_.
FutexWaitListNode* prev_; FutexWaitListNode* prev_;
FutexWaitListNode* next_; FutexWaitListNode* next_;
void* backing_store_; void* backing_store_;
size_t wait_addr_; size_t wait_addr_;
// waiting_ and interrupted_ are protected by FutexEmulation::mutex_
// if this node is currently contained in FutexEmulation::wait_list_.
bool waiting_; bool waiting_;
bool interrupted_; bool interrupted_;
...@@ -110,6 +113,11 @@ class FutexEmulation : public AllStatic { ...@@ -110,6 +113,11 @@ class FutexEmulation : public AllStatic {
private: private:
friend class FutexWaitListNode; friend class FutexWaitListNode;
// `mutex_` protects the composition of `wait_list_` (i.e. no elements may be
// added or removed without holding this mutex), as well as the `waiting_`
// and `interrupted_` fields for each individual list node that is currently
// part of the list. It must be the mutex used together with the `cond_`
// condition variable of such nodes.
static base::LazyMutex mutex_; static base::LazyMutex mutex_;
static base::LazyInstance<FutexWaitList>::type wait_list_; static base::LazyInstance<FutexWaitList>::type wait_list_;
}; };
......
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