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

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: fix windows build 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 cond_.NotifyOne();
35 set_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::Time start_time = base::Time::NowFromSystemTime(); 113 base::TimeTicks start_time = base::TimeTicks::Now();
105 base::Time 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::Time current_time = base::Time::NowFromSystemTime(); 122 // Unlock the mutex here to prevent deadlock from lock ordering between
113 if (use_timeout && current_time > timeout_time) { 123 // mutex_ and mutexes locked by HandleInterrupts.
114 result = Smi::FromInt(Result::kTimedOut); 124 mutex_.Pointer()->Unlock();
115 break; 125
126 // Because the mutex is unlocked, we have to be careful about not dropping
127 // an interrupt. The notification can happen in three different places:
128 // 1) Before Wait is called: the notification will be dropped, but
129 // interrupted_ will be set to 1. This will be checked below.
130 // 2) After interrupted has been checked here, but before mutex_ is
131 // acquired: interrupted is checked again below, with mutex_ locked.
132 // Because the wakeup signal also acquires mutex_, we know it will not
133 // be able to notify until mutex_ is released below, when waiting on the
134 // condition variable.
135 // 3) After the mutex is released in the call to WaitFor(): this
136 // notification will wake up the condition variable. node->waiting() will
137 // be false, so we'll loop and then check interrupts.
138 if (node->CheckInterruptedAndClear()) {
Jarin 2015/08/11 08:21:49 Why can't you read and clear the interrupt flag wh
binji 2015/08/11 14:51:17 Good point, I'll change this.
139 Object* interrupt_object = isolate->stack_guard()->HandleInterrupts();
140 if (interrupt_object->IsException()) {
141 result = interrupt_object;
142 mutex_.Pointer()->Lock();
143 break;
144 }
116 } 145 }
117 146
118 base::TimeDelta time_until_timeout = timeout_time - current_time; 147 mutex_.Pointer()->Lock();
119 base::TimeDelta time_to_wait =
120 (use_timeout && time_until_timeout < kMaxWaitTime) ? time_until_timeout
121 : kMaxWaitTime;
122 148
123 bool wait_for_result = node->cond_.WaitFor(mutex_.Pointer(), time_to_wait); 149 if (node->interrupted()) {
124 USE(wait_for_result); 150 // An interrupt occured while the mutex_ was unlocked. Don't wait yet.
151 continue;
152 }
125 153
126 if (!node->waiting_) { 154 // No interrupts, now wait.
155 if (use_timeout) {
156 current_time = base::TimeTicks::Now();
157 if (current_time >= timeout_time) {
158 result = Smi::FromInt(Result::kTimedOut);
159 break;
160 }
161
162 base::TimeDelta time_until_timeout = timeout_time - current_time;
163 DCHECK(time_until_timeout.InMicroseconds() >= 0);
164 bool wait_for_result =
165 node->cond_.WaitFor(mutex_.Pointer(), time_until_timeout);
166 USE(wait_for_result);
167 } else {
168 node->cond_.Wait(mutex_.Pointer());
169 }
170
171 if (!node->waiting()) {
127 result = Smi::FromInt(Result::kOk); 172 result = Smi::FromInt(Result::kOk);
128 break; 173 break;
129 } 174 }
130 175
131 // Spurious wakeup or timeout. Potentially handle interrupts before 176 // Spurious wakeup, interrupt or timeout.
132 // continuing to wait.
133 Object* interrupt_object = isolate->stack_guard()->HandleInterrupts();
134 if (interrupt_object->IsException()) {
135 result = interrupt_object;
136 break;
137 }
138 } 177 }
139 178
140 wait_list_.Pointer()->RemoveNode(node); 179 wait_list_.Pointer()->RemoveNode(node);
141 180
142 return result; 181 return result;
143 } 182 }
144 183
145 184
146 Object* FutexEmulation::Wake(Isolate* isolate, 185 Object* FutexEmulation::Wake(Isolate* isolate,
147 Handle<JSArrayBuffer> array_buffer, size_t addr, 186 Handle<JSArrayBuffer> array_buffer, size_t addr,
148 int num_waiters_to_wake) { 187 int num_waiters_to_wake) {
149 DCHECK(addr < NumberToSize(isolate, array_buffer->byte_length())); 188 DCHECK(addr < NumberToSize(isolate, array_buffer->byte_length()));
150 189
151 int waiters_woken = 0; 190 int waiters_woken = 0;
152 void* backing_store = array_buffer->backing_store(); 191 void* backing_store = array_buffer->backing_store();
153 192
154 base::LockGuard<base::Mutex> lock_guard(mutex_.Pointer()); 193 base::LockGuard<base::Mutex> lock_guard(mutex_.Pointer());
155 FutexWaitListNode* node = wait_list_.Pointer()->head_; 194 FutexWaitListNode* node = wait_list_.Pointer()->head_;
156 while (node && num_waiters_to_wake > 0) { 195 while (node && num_waiters_to_wake > 0) {
157 if (backing_store == node->backing_store_ && addr == node->wait_addr_) { 196 if (backing_store == node->backing_store_ && addr == node->wait_addr_) {
158 node->waiting_ = false; 197 node->set_waiting(false);
159 node->cond_.NotifyOne(); 198 node->cond_.NotifyOne();
160 --num_waiters_to_wake; 199 --num_waiters_to_wake;
161 waiters_woken++; 200 waiters_woken++;
162 } 201 }
163 202
164 node = node->next_; 203 node = node->next_;
165 } 204 }
166 205
167 return Smi::FromInt(waiters_woken); 206 return Smi::FromInt(waiters_woken);
168 } 207 }
(...skipping 14 matching lines...) Expand all
183 if (*p != value) { 222 if (*p != value) {
184 return Smi::FromInt(Result::kNotEqual); 223 return Smi::FromInt(Result::kNotEqual);
185 } 224 }
186 225
187 // Wake |num_waiters_to_wake| 226 // Wake |num_waiters_to_wake|
188 int waiters_woken = 0; 227 int waiters_woken = 0;
189 FutexWaitListNode* node = wait_list_.Pointer()->head_; 228 FutexWaitListNode* node = wait_list_.Pointer()->head_;
190 while (node) { 229 while (node) {
191 if (backing_store == node->backing_store_ && addr == node->wait_addr_) { 230 if (backing_store == node->backing_store_ && addr == node->wait_addr_) {
192 if (num_waiters_to_wake > 0) { 231 if (num_waiters_to_wake > 0) {
193 node->waiting_ = false; 232 node->set_waiting(false);
194 node->cond_.NotifyOne(); 233 node->cond_.NotifyOne();
195 --num_waiters_to_wake; 234 --num_waiters_to_wake;
196 waiters_woken++; 235 waiters_woken++;
197 } else { 236 } else {
198 node->wait_addr_ = addr2; 237 node->wait_addr_ = addr2;
199 } 238 }
200 } 239 }
201 240
202 node = node->next_; 241 node = node->next_;
203 } 242 }
(...skipping 18 matching lines...) Expand all
222 } 261 }
223 262
224 node = node->next_; 263 node = node->next_;
225 } 264 }
226 265
227 return Smi::FromInt(waiters); 266 return Smi::FromInt(waiters);
228 } 267 }
229 268
230 } // namespace internal 269 } // namespace internal
231 } // namespace v8 270 } // 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