Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(593)

Side by Side Diff: src/futex-emulation.cc

Issue 1230303005: Signal a blocked futex if the isolate is interrupted; don't busy-wait (Closed) Base URL: https://chromium.googlesource.com/v8/v8.git@master
Patch Set: remove atomic waiting_ Created 5 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « src/futex-emulation.h ('k') | test/cctest/test-api.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright 2015 the V8 project authors. All rights reserved. 1 // Copyright 2015 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "src/futex-emulation.h" 5 #include "src/futex-emulation.h"
6 6
7 #include <limits> 7 #include <limits>
8 8
9 #include "src/base/macros.h" 9 #include "src/base/macros.h"
10 #include "src/base/platform/time.h" 10 #include "src/base/platform/time.h"
11 #include "src/conversions.h" 11 #include "src/conversions.h"
12 #include "src/handles-inl.h" 12 #include "src/handles-inl.h"
13 #include "src/isolate.h" 13 #include "src/isolate.h"
14 #include "src/list-inl.h" 14 #include "src/list-inl.h"
15 15
16 namespace v8 { 16 namespace v8 {
17 namespace internal { 17 namespace internal {
18 18
19 base::LazyMutex FutexEmulation::mutex_ = LAZY_MUTEX_INITIALIZER; 19 base::LazyMutex FutexEmulation::mutex_ = LAZY_MUTEX_INITIALIZER;
20 base::LazyInstance<FutexWaitList>::type FutexEmulation::wait_list_ = 20 base::LazyInstance<FutexWaitList>::type FutexEmulation::wait_list_ =
21 LAZY_INSTANCE_INITIALIZER; 21 LAZY_INSTANCE_INITIALIZER;
22 22
23 23
24 void FutexWaitListNode::NotifyWake() {
25 // Lock the FutexEmulation mutex before notifying. We know that the mutex
26 // will have been unlocked if we are currently waiting on the condition
27 // variable.
28 //
29 // The mutex may also not be locked if the other thread is currently handling
30 // interrupts, or if FutexEmulation::Wait was just called and the mutex
31 // hasn't been locked yet. In either of those cases, we set the interrupted
32 // flag to true, which will be tested after the mutex is re-locked.
33 base::LockGuard<base::Mutex> lock_guard(FutexEmulation::mutex_.Pointer());
34 if (waiting_) {
35 cond_.NotifyOne();
36 interrupted_ = true;
37 }
38 }
39
40
24 FutexWaitList::FutexWaitList() : head_(nullptr), tail_(nullptr) {} 41 FutexWaitList::FutexWaitList() : head_(nullptr), tail_(nullptr) {}
25 42
26 43
27 void FutexWaitList::AddNode(FutexWaitListNode* node) { 44 void FutexWaitList::AddNode(FutexWaitListNode* node) {
28 DCHECK(node->prev_ == nullptr && node->next_ == nullptr); 45 DCHECK(node->prev_ == nullptr && node->next_ == nullptr);
29 if (tail_) { 46 if (tail_) {
30 tail_->next_ = node; 47 tail_->next_ = node;
31 } else { 48 } else {
32 head_ = node; 49 head_ = node;
33 } 50 }
(...skipping 17 matching lines...) Expand all
51 tail_ = node->prev_; 68 tail_ = node->prev_;
52 } 69 }
53 70
54 node->prev_ = node->next_ = nullptr; 71 node->prev_ = node->next_ = nullptr;
55 } 72 }
56 73
57 74
58 Object* FutexEmulation::Wait(Isolate* isolate, 75 Object* FutexEmulation::Wait(Isolate* isolate,
59 Handle<JSArrayBuffer> array_buffer, size_t addr, 76 Handle<JSArrayBuffer> array_buffer, size_t addr,
60 int32_t value, double rel_timeout_ms) { 77 int32_t value, double rel_timeout_ms) {
61 // We never want to wait longer than this amount of time; this way we can
62 // interrupt this thread even if this is an "infinitely blocking" wait.
63 // TODO(binji): come up with a better way of interrupting only when
64 // necessary, rather than busy-waiting.
65 const base::TimeDelta kMaxWaitTime = base::TimeDelta::FromMilliseconds(50);
66
67 DCHECK(addr < NumberToSize(isolate, array_buffer->byte_length())); 78 DCHECK(addr < NumberToSize(isolate, array_buffer->byte_length()));
68 79
69 void* backing_store = array_buffer->backing_store(); 80 void* backing_store = array_buffer->backing_store();
70 int32_t* p = 81 int32_t* p =
71 reinterpret_cast<int32_t*>(static_cast<int8_t*>(backing_store) + addr); 82 reinterpret_cast<int32_t*>(static_cast<int8_t*>(backing_store) + addr);
72 83
73 base::LockGuard<base::Mutex> lock_guard(mutex_.Pointer()); 84 base::LockGuard<base::Mutex> lock_guard(mutex_.Pointer());
74 85
75 if (*p != value) { 86 if (*p != value) {
76 return Smi::FromInt(Result::kNotEqual); 87 return Smi::FromInt(Result::kNotEqual);
(...skipping 19 matching lines...) Expand all
96 // infinite. 107 // infinite.
97 use_timeout = false; 108 use_timeout = false;
98 } else { 109 } else {
99 rel_timeout = base::TimeDelta::FromNanoseconds( 110 rel_timeout = base::TimeDelta::FromNanoseconds(
100 static_cast<int64_t>(rel_timeout_ns)); 111 static_cast<int64_t>(rel_timeout_ns));
101 } 112 }
102 } 113 }
103 114
104 base::TimeTicks start_time = base::TimeTicks::Now(); 115 base::TimeTicks start_time = base::TimeTicks::Now();
105 base::TimeTicks timeout_time = start_time + rel_timeout; 116 base::TimeTicks timeout_time = start_time + rel_timeout;
117 base::TimeTicks current_time = start_time;
106 118
107 wait_list_.Pointer()->AddNode(node); 119 wait_list_.Pointer()->AddNode(node);
108 120
109 Object* result; 121 Object* result;
110 122
111 while (true) { 123 while (true) {
112 base::TimeTicks current_time = base::TimeTicks::Now(); 124 bool interrupted = node->interrupted_;
113 if (use_timeout && current_time > timeout_time) { 125 node->interrupted_ = false;
114 result = Smi::FromInt(Result::kTimedOut); 126
115 break; 127 // Unlock the mutex here to prevent deadlock from lock ordering between
128 // mutex_ and mutexes locked by HandleInterrupts.
129 mutex_.Pointer()->Unlock();
130
131 // Because the mutex is unlocked, we have to be careful about not dropping
132 // an interrupt. The notification can happen in three different places:
133 // 1) Before Wait is called: the notification will be dropped, but
134 // interrupted_ will be set to 1. This will be checked below.
135 // 2) After interrupted has been checked here, but before mutex_ is
136 // acquired: interrupted is checked again below, with mutex_ locked.
137 // Because the wakeup signal also acquires mutex_, we know it will not
138 // be able to notify until mutex_ is released below, when waiting on the
139 // condition variable.
140 // 3) After the mutex is released in the call to WaitFor(): this
141 // notification will wake up the condition variable. node->waiting() will
142 // be false, so we'll loop and then check interrupts.
143 if (interrupted) {
144 Object* interrupt_object = isolate->stack_guard()->HandleInterrupts();
145 if (interrupt_object->IsException()) {
146 result = interrupt_object;
147 mutex_.Pointer()->Lock();
148 break;
149 }
116 } 150 }
117 151
118 base::TimeDelta time_until_timeout = timeout_time - current_time; 152 mutex_.Pointer()->Lock();
119 base::TimeDelta time_to_wait =
120 (use_timeout && time_until_timeout < kMaxWaitTime) ? time_until_timeout
121 : kMaxWaitTime;
122 153
123 bool wait_for_result = node->cond_.WaitFor(mutex_.Pointer(), time_to_wait); 154 if (node->interrupted_) {
124 USE(wait_for_result); 155 // An interrupt occured while the mutex_ was unlocked. Don't wait yet.
156 continue;
157 }
125 158
126 if (!node->waiting_) { 159 if (!node->waiting_) {
127 result = Smi::FromInt(Result::kOk); 160 result = Smi::FromInt(Result::kOk);
128 break; 161 break;
129 } 162 }
130 163
131 // Spurious wakeup or timeout. Potentially handle interrupts before 164 // No interrupts, now wait.
132 // continuing to wait. 165 if (use_timeout) {
133 Object* interrupt_object = isolate->stack_guard()->HandleInterrupts(); 166 current_time = base::TimeTicks::Now();
134 if (interrupt_object->IsException()) { 167 if (current_time >= timeout_time) {
135 result = interrupt_object; 168 result = Smi::FromInt(Result::kTimedOut);
136 break; 169 break;
170 }
171
172 base::TimeDelta time_until_timeout = timeout_time - current_time;
173 DCHECK(time_until_timeout.InMicroseconds() >= 0);
174 bool wait_for_result =
175 node->cond_.WaitFor(mutex_.Pointer(), time_until_timeout);
176 USE(wait_for_result);
177 } else {
178 node->cond_.Wait(mutex_.Pointer());
137 } 179 }
180
181 // Spurious wakeup, interrupt or timeout.
138 } 182 }
139 183
140 wait_list_.Pointer()->RemoveNode(node); 184 wait_list_.Pointer()->RemoveNode(node);
185 node->waiting_ = false;
141 186
142 return result; 187 return result;
143 } 188 }
144 189
145 190
146 Object* FutexEmulation::Wake(Isolate* isolate, 191 Object* FutexEmulation::Wake(Isolate* isolate,
147 Handle<JSArrayBuffer> array_buffer, size_t addr, 192 Handle<JSArrayBuffer> array_buffer, size_t addr,
148 int num_waiters_to_wake) { 193 int num_waiters_to_wake) {
149 DCHECK(addr < NumberToSize(isolate, array_buffer->byte_length())); 194 DCHECK(addr < NumberToSize(isolate, array_buffer->byte_length()));
150 195
(...skipping 71 matching lines...) Expand 10 before | Expand all | Expand 10 after
222 } 267 }
223 268
224 node = node->next_; 269 node = node->next_;
225 } 270 }
226 271
227 return Smi::FromInt(waiters); 272 return Smi::FromInt(waiters);
228 } 273 }
229 274
230 } // namespace internal 275 } // namespace internal
231 } // namespace v8 276 } // namespace v8
OLDNEW
« no previous file with comments | « src/futex-emulation.h ('k') | test/cctest/test-api.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698