Line data Source code
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
3 : // found in the LICENSE file.
4 :
5 : #include "src/futex-emulation.h"
6 :
7 : #include <limits>
8 :
9 : #include "src/base/macros.h"
10 : #include "src/base/platform/time.h"
11 : #include "src/conversions.h"
12 : #include "src/handles-inl.h"
13 : #include "src/isolate.h"
14 : #include "src/list-inl.h"
15 : #include "src/objects-inl.h"
16 :
17 : namespace v8 {
18 : namespace internal {
19 :
20 : base::LazyMutex FutexEmulation::mutex_ = LAZY_MUTEX_INITIALIZER;
21 : base::LazyInstance<FutexWaitList>::type FutexEmulation::wait_list_ =
22 : LAZY_INSTANCE_INITIALIZER;
23 :
24 :
25 16467 : void FutexWaitListNode::NotifyWake() {
26 : // Lock the FutexEmulation mutex before notifying. We know that the mutex
27 : // will have been unlocked if we are currently waiting on the condition
28 : // variable.
29 : //
30 : // The mutex may also not be locked if the other thread is currently handling
31 : // interrupts, or if FutexEmulation::Wait was just called and the mutex
32 : // hasn't been locked yet. In either of those cases, we set the interrupted
33 : // flag to true, which will be tested after the mutex is re-locked.
34 : base::LockGuard<base::Mutex> lock_guard(FutexEmulation::mutex_.Pointer());
35 16467 : if (waiting_) {
36 6 : cond_.NotifyOne();
37 6 : interrupted_ = true;
38 : }
39 16467 : }
40 :
41 :
42 12 : FutexWaitList::FutexWaitList() : head_(nullptr), tail_(nullptr) {}
43 :
44 :
45 0 : void FutexWaitList::AddNode(FutexWaitListNode* node) {
46 : DCHECK(node->prev_ == nullptr && node->next_ == nullptr);
47 244 : if (tail_) {
48 42 : tail_->next_ = node;
49 : } else {
50 202 : head_ = node;
51 : }
52 :
53 244 : node->prev_ = tail_;
54 244 : node->next_ = nullptr;
55 244 : tail_ = node;
56 0 : }
57 :
58 :
59 0 : void FutexWaitList::RemoveNode(FutexWaitListNode* node) {
60 244 : if (node->prev_) {
61 7 : node->prev_->next_ = node->next_;
62 : } else {
63 237 : head_ = node->next_;
64 : }
65 :
66 244 : if (node->next_) {
67 42 : node->next_->prev_ = node->prev_;
68 : } else {
69 202 : tail_ = node->prev_;
70 : }
71 :
72 244 : node->prev_ = node->next_ = nullptr;
73 0 : }
74 :
75 :
76 272 : Object* FutexEmulation::Wait(Isolate* isolate,
77 : Handle<JSArrayBuffer> array_buffer, size_t addr,
78 : int32_t value, double rel_timeout_ms) {
79 : DCHECK(addr < NumberToSize(array_buffer->byte_length()));
80 :
81 : void* backing_store = array_buffer->backing_store();
82 : int32_t* p =
83 272 : reinterpret_cast<int32_t*>(static_cast<int8_t*>(backing_store) + addr);
84 :
85 : base::LockGuard<base::Mutex> lock_guard(mutex_.Pointer());
86 :
87 272 : if (*p != value) {
88 28 : return isolate->heap()->not_equal();
89 : }
90 :
91 244 : FutexWaitListNode* node = isolate->futex_wait_list_node();
92 :
93 244 : node->backing_store_ = backing_store;
94 244 : node->wait_addr_ = addr;
95 244 : node->waiting_ = true;
96 :
97 244 : bool use_timeout = rel_timeout_ms != V8_INFINITY;
98 :
99 : base::TimeDelta rel_timeout;
100 244 : if (use_timeout) {
101 : // Convert to nanoseconds.
102 56 : double rel_timeout_ns = rel_timeout_ms *
103 : base::Time::kNanosecondsPerMicrosecond *
104 56 : base::Time::kMicrosecondsPerMillisecond;
105 56 : if (rel_timeout_ns >
106 : static_cast<double>(std::numeric_limits<int64_t>::max())) {
107 : // 2**63 nanoseconds is 292 years. Let's just treat anything greater as
108 : // infinite.
109 : use_timeout = false;
110 : } else {
111 112 : rel_timeout = base::TimeDelta::FromNanoseconds(
112 56 : static_cast<int64_t>(rel_timeout_ns));
113 : }
114 : }
115 :
116 244 : base::TimeTicks start_time = base::TimeTicks::Now();
117 : base::TimeTicks timeout_time = start_time + rel_timeout;
118 : base::TimeTicks current_time = start_time;
119 :
120 : wait_list_.Pointer()->AddNode(node);
121 :
122 : Object* result;
123 :
124 : while (true) {
125 442 : bool interrupted = node->interrupted_;
126 442 : node->interrupted_ = false;
127 :
128 : // Unlock the mutex here to prevent deadlock from lock ordering between
129 : // mutex_ and mutexes locked by HandleInterrupts.
130 442 : mutex_.Pointer()->Unlock();
131 :
132 : // Because the mutex is unlocked, we have to be careful about not dropping
133 : // an interrupt. The notification can happen in three different places:
134 : // 1) Before Wait is called: the notification will be dropped, but
135 : // interrupted_ will be set to 1. This will be checked below.
136 : // 2) After interrupted has been checked here, but before mutex_ is
137 : // acquired: interrupted is checked again below, with mutex_ locked.
138 : // Because the wakeup signal also acquires mutex_, we know it will not
139 : // be able to notify until mutex_ is released below, when waiting on the
140 : // condition variable.
141 : // 3) After the mutex is released in the call to WaitFor(): this
142 : // notification will wake up the condition variable. node->waiting() will
143 : // be false, so we'll loop and then check interrupts.
144 442 : if (interrupted) {
145 6 : Object* interrupt_object = isolate->stack_guard()->HandleInterrupts();
146 6 : if (interrupt_object->IsException(isolate)) {
147 : result = interrupt_object;
148 6 : mutex_.Pointer()->Lock();
149 6 : break;
150 : }
151 : }
152 :
153 436 : mutex_.Pointer()->Lock();
154 :
155 436 : if (node->interrupted_) {
156 : // An interrupt occured while the mutex_ was unlocked. Don't wait yet.
157 : continue;
158 : }
159 :
160 436 : if (!node->waiting_) {
161 182 : result = isolate->heap()->ok();
162 182 : break;
163 : }
164 :
165 : // No interrupts, now wait.
166 254 : if (use_timeout) {
167 70 : current_time = base::TimeTicks::Now();
168 70 : if (current_time >= timeout_time) {
169 56 : result = isolate->heap()->timed_out();
170 56 : break;
171 : }
172 :
173 14 : base::TimeDelta time_until_timeout = timeout_time - current_time;
174 : DCHECK(time_until_timeout.InMicroseconds() >= 0);
175 : bool wait_for_result =
176 14 : node->cond_.WaitFor(mutex_.Pointer(), time_until_timeout);
177 : USE(wait_for_result);
178 : } else {
179 184 : node->cond_.Wait(mutex_.Pointer());
180 : }
181 :
182 : // Spurious wakeup, interrupt or timeout.
183 : }
184 :
185 : wait_list_.Pointer()->RemoveNode(node);
186 244 : node->waiting_ = false;
187 :
188 244 : return result;
189 : }
190 :
191 168 : Object* FutexEmulation::Wake(Isolate* isolate,
192 : Handle<JSArrayBuffer> array_buffer, size_t addr,
193 : uint32_t num_waiters_to_wake) {
194 : DCHECK(addr < NumberToSize(array_buffer->byte_length()));
195 :
196 : int waiters_woken = 0;
197 : void* backing_store = array_buffer->backing_store();
198 :
199 : base::LockGuard<base::Mutex> lock_guard(mutex_.Pointer());
200 168 : FutexWaitListNode* node = wait_list_.Pointer()->head_;
201 518 : while (node && num_waiters_to_wake > 0) {
202 182 : if (backing_store == node->backing_store_ && addr == node->wait_addr_) {
203 182 : node->waiting_ = false;
204 182 : node->cond_.NotifyOne();
205 182 : if (num_waiters_to_wake != kWakeAll) {
206 182 : --num_waiters_to_wake;
207 : }
208 182 : waiters_woken++;
209 : }
210 :
211 182 : node = node->next_;
212 : }
213 :
214 168 : return Smi::FromInt(waiters_woken);
215 : }
216 :
217 :
218 18691990 : Object* FutexEmulation::NumWaitersForTesting(Isolate* isolate,
219 : Handle<JSArrayBuffer> array_buffer,
220 : size_t addr) {
221 : DCHECK(addr < NumberToSize(array_buffer->byte_length()));
222 : void* backing_store = array_buffer->backing_store();
223 :
224 : base::LockGuard<base::Mutex> lock_guard(mutex_.Pointer());
225 :
226 : int waiters = 0;
227 18691990 : FutexWaitListNode* node = wait_list_.Pointer()->head_;
228 38732824 : while (node) {
229 1348844 : if (backing_store == node->backing_store_ && addr == node->wait_addr_ &&
230 : node->waiting_) {
231 1348844 : waiters++;
232 : }
233 :
234 1348844 : node = node->next_;
235 : }
236 :
237 18691990 : return Smi::FromInt(waiters);
238 : }
239 :
240 : } // namespace internal
241 : } // namespace v8
|