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

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: merge master 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
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 cond_.NotifyOne();
35 interrupted_ = true;
36 }
37
38
24 FutexWaitList::FutexWaitList() : head_(nullptr), tail_(nullptr) {} 39 FutexWaitList::FutexWaitList() : head_(nullptr), tail_(nullptr) {}
25 40
26 41
27 void FutexWaitList::AddNode(FutexWaitListNode* node) { 42 void FutexWaitList::AddNode(FutexWaitListNode* node) {
28 DCHECK(node->prev_ == nullptr && node->next_ == nullptr); 43 DCHECK(node->prev_ == nullptr && node->next_ == nullptr);
29 if (tail_) { 44 if (tail_) {
30 tail_->next_ = node; 45 tail_->next_ = node;
31 } else { 46 } else {
32 head_ = node; 47 head_ = node;
33 } 48 }
(...skipping 17 matching lines...) Expand all
51 tail_ = node->prev_; 66 tail_ = node->prev_;
52 } 67 }
53 68
54 node->prev_ = node->next_ = nullptr; 69 node->prev_ = node->next_ = nullptr;
55 } 70 }
56 71
57 72
58 Object* FutexEmulation::Wait(Isolate* isolate, 73 Object* FutexEmulation::Wait(Isolate* isolate,
59 Handle<JSArrayBuffer> array_buffer, size_t addr, 74 Handle<JSArrayBuffer> array_buffer, size_t addr,
60 int32_t value, double rel_timeout_ms) { 75 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())); 76 DCHECK(addr < NumberToSize(isolate, array_buffer->byte_length()));
68 77
69 void* backing_store = array_buffer->backing_store(); 78 void* backing_store = array_buffer->backing_store();
70 int32_t* p = 79 int32_t* p =
71 reinterpret_cast<int32_t*>(static_cast<int8_t*>(backing_store) + addr); 80 reinterpret_cast<int32_t*>(static_cast<int8_t*>(backing_store) + addr);
72 81
73 base::LockGuard<base::Mutex> lock_guard(mutex_.Pointer()); 82 base::LockGuard<base::Mutex> lock_guard(mutex_.Pointer());
74 83
75 if (*p != value) { 84 if (*p != value) {
76 return Smi::FromInt(Result::kNotEqual); 85 return Smi::FromInt(Result::kNotEqual);
77 } 86 }
78 87
79 FutexWaitListNode* node = isolate->futex_wait_list_node(); 88 FutexWaitListNode* node = isolate->futex_wait_list_node();
80 89
81 node->backing_store_ = backing_store; 90 node->backing_store_ = backing_store;
82 node->wait_addr_ = addr; 91 node->wait_addr_ = addr;
83 node->waiting_ = true; 92 node->set_waiting(true);
84 93
85 bool use_timeout = rel_timeout_ms != V8_INFINITY; 94 bool use_timeout = rel_timeout_ms != V8_INFINITY;
86 95
87 base::TimeDelta rel_timeout; 96 base::TimeDelta rel_timeout;
88 if (use_timeout) { 97 if (use_timeout) {
89 // Convert to nanoseconds. 98 // Convert to nanoseconds.
90 double rel_timeout_ns = rel_timeout_ms * 99 double rel_timeout_ns = rel_timeout_ms *
91 base::Time::kNanosecondsPerMicrosecond * 100 base::Time::kNanosecondsPerMicrosecond *
92 base::Time::kMicrosecondsPerMillisecond; 101 base::Time::kMicrosecondsPerMillisecond;
93 if (rel_timeout_ns > 102 if (rel_timeout_ns >
94 static_cast<double>(std::numeric_limits<int64_t>::max())) { 103 static_cast<double>(std::numeric_limits<int64_t>::max())) {
95 // 2**63 nanoseconds is 292 years. Let's just treat anything greater as 104 // 2**63 nanoseconds is 292 years. Let's just treat anything greater as
96 // infinite. 105 // infinite.
97 use_timeout = false; 106 use_timeout = false;
98 } else { 107 } else {
99 rel_timeout = base::TimeDelta::FromNanoseconds( 108 rel_timeout = base::TimeDelta::FromNanoseconds(
100 static_cast<int64_t>(rel_timeout_ns)); 109 static_cast<int64_t>(rel_timeout_ns));
101 } 110 }
102 } 111 }
103 112
104 base::TimeTicks start_time = base::TimeTicks::Now(); 113 base::TimeTicks start_time = base::TimeTicks::Now();
105 base::TimeTicks timeout_time = start_time + rel_timeout; 114 base::TimeTicks timeout_time = start_time + rel_timeout;
115 base::TimeTicks current_time = start_time;
106 116
107 wait_list_.Pointer()->AddNode(node); 117 wait_list_.Pointer()->AddNode(node);
108 118
109 Object* result; 119 Object* result;
110 120
111 while (true) { 121 while (true) {
112 base::TimeTicks current_time = base::TimeTicks::Now(); 122 bool interrupted = node->interrupted_;
113 if (use_timeout && current_time > timeout_time) { 123 node->interrupted_ = false;
114 result = Smi::FromInt(Result::kTimedOut); 124
115 break; 125 // Unlock the mutex here to prevent deadlock from lock ordering between
126 // mutex_ and mutexes locked by HandleInterrupts.
127 mutex_.Pointer()->Unlock();
128
129 // Because the mutex is unlocked, we have to be careful about not dropping
130 // an interrupt. The notification can happen in three different places:
131 // 1) Before Wait is called: the notification will be dropped, but
132 // interrupted_ will be set to 1. This will be checked below.
133 // 2) After interrupted has been checked here, but before mutex_ is
134 // acquired: interrupted is checked again below, with mutex_ locked.
135 // Because the wakeup signal also acquires mutex_, we know it will not
136 // be able to notify until mutex_ is released below, when waiting on the
137 // condition variable.
138 // 3) After the mutex is released in the call to WaitFor(): this
139 // notification will wake up the condition variable. node->waiting() will
140 // be false, so we'll loop and then check interrupts.
141 if (interrupted) {
142 Object* interrupt_object = isolate->stack_guard()->HandleInterrupts();
143 if (interrupt_object->IsException()) {
144 result = interrupt_object;
145 mutex_.Pointer()->Lock();
146 break;
147 }
116 } 148 }
117 149
118 base::TimeDelta time_until_timeout = timeout_time - current_time; 150 mutex_.Pointer()->Lock();
119 base::TimeDelta time_to_wait =
120 (use_timeout && time_until_timeout < kMaxWaitTime) ? time_until_timeout
121 : kMaxWaitTime;
122 151
123 bool wait_for_result = node->cond_.WaitFor(mutex_.Pointer(), time_to_wait); 152 if (node->interrupted_) {
124 USE(wait_for_result); 153 // An interrupt occured while the mutex_ was unlocked. Don't wait yet.
154 continue;
155 }
125 156
126 if (!node->waiting_) { 157 if (!node->waiting()) {
127 result = Smi::FromInt(Result::kOk); 158 result = Smi::FromInt(Result::kOk);
128 break; 159 break;
129 } 160 }
130 161
131 // Spurious wakeup or timeout. Potentially handle interrupts before 162 // No interrupts, now wait.
132 // continuing to wait. 163 if (use_timeout) {
133 Object* interrupt_object = isolate->stack_guard()->HandleInterrupts(); 164 current_time = base::TimeTicks::Now();
134 if (interrupt_object->IsException()) { 165 if (current_time >= timeout_time) {
135 result = interrupt_object; 166 result = Smi::FromInt(Result::kTimedOut);
136 break; 167 break;
168 }
169
170 base::TimeDelta time_until_timeout = timeout_time - current_time;
171 DCHECK(time_until_timeout.InMicroseconds() >= 0);
172 bool wait_for_result =
173 node->cond_.WaitFor(mutex_.Pointer(), time_until_timeout);
174 USE(wait_for_result);
175 } else {
176 node->cond_.Wait(mutex_.Pointer());
137 } 177 }
178
179 // Spurious wakeup, interrupt or timeout.
138 } 180 }
139 181
140 wait_list_.Pointer()->RemoveNode(node); 182 wait_list_.Pointer()->RemoveNode(node);
141 183
142 return result; 184 return result;
143 } 185 }
144 186
145 187
146 Object* FutexEmulation::Wake(Isolate* isolate, 188 Object* FutexEmulation::Wake(Isolate* isolate,
147 Handle<JSArrayBuffer> array_buffer, size_t addr, 189 Handle<JSArrayBuffer> array_buffer, size_t addr,
148 int num_waiters_to_wake) { 190 int num_waiters_to_wake) {
149 DCHECK(addr < NumberToSize(isolate, array_buffer->byte_length())); 191 DCHECK(addr < NumberToSize(isolate, array_buffer->byte_length()));
150 192
151 int waiters_woken = 0; 193 int waiters_woken = 0;
152 void* backing_store = array_buffer->backing_store(); 194 void* backing_store = array_buffer->backing_store();
153 195
154 base::LockGuard<base::Mutex> lock_guard(mutex_.Pointer()); 196 base::LockGuard<base::Mutex> lock_guard(mutex_.Pointer());
155 FutexWaitListNode* node = wait_list_.Pointer()->head_; 197 FutexWaitListNode* node = wait_list_.Pointer()->head_;
156 while (node && num_waiters_to_wake > 0) { 198 while (node && num_waiters_to_wake > 0) {
157 if (backing_store == node->backing_store_ && addr == node->wait_addr_) { 199 if (backing_store == node->backing_store_ && addr == node->wait_addr_) {
158 node->waiting_ = false; 200 node->set_waiting(false);
159 node->cond_.NotifyOne(); 201 node->cond_.NotifyOne();
160 --num_waiters_to_wake; 202 --num_waiters_to_wake;
161 waiters_woken++; 203 waiters_woken++;
162 } 204 }
163 205
164 node = node->next_; 206 node = node->next_;
165 } 207 }
166 208
167 return Smi::FromInt(waiters_woken); 209 return Smi::FromInt(waiters_woken);
168 } 210 }
(...skipping 14 matching lines...) Expand all
183 if (*p != value) { 225 if (*p != value) {
184 return Smi::FromInt(Result::kNotEqual); 226 return Smi::FromInt(Result::kNotEqual);
185 } 227 }
186 228
187 // Wake |num_waiters_to_wake| 229 // Wake |num_waiters_to_wake|
188 int waiters_woken = 0; 230 int waiters_woken = 0;
189 FutexWaitListNode* node = wait_list_.Pointer()->head_; 231 FutexWaitListNode* node = wait_list_.Pointer()->head_;
190 while (node) { 232 while (node) {
191 if (backing_store == node->backing_store_ && addr == node->wait_addr_) { 233 if (backing_store == node->backing_store_ && addr == node->wait_addr_) {
192 if (num_waiters_to_wake > 0) { 234 if (num_waiters_to_wake > 0) {
193 node->waiting_ = false; 235 node->set_waiting(false);
194 node->cond_.NotifyOne(); 236 node->cond_.NotifyOne();
195 --num_waiters_to_wake; 237 --num_waiters_to_wake;
196 waiters_woken++; 238 waiters_woken++;
197 } else { 239 } else {
198 node->wait_addr_ = addr2; 240 node->wait_addr_ = addr2;
199 } 241 }
200 } 242 }
201 243
202 node = node->next_; 244 node = node->next_;
203 } 245 }
(...skipping 18 matching lines...) Expand all
222 } 264 }
223 265
224 node = node->next_; 266 node = node->next_;
225 } 267 }
226 268
227 return Smi::FromInt(waiters); 269 return Smi::FromInt(waiters);
228 } 270 }
229 271
230 } // namespace internal 272 } // namespace internal
231 } // namespace v8 273 } // namespace v8
OLDNEW
« src/futex-emulation.h ('K') | « 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