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/objects-inl.h"
15 :
16 : namespace v8 {
17 : namespace internal {
18 :
19 : base::LazyMutex FutexEmulation::mutex_ = LAZY_MUTEX_INITIALIZER;
20 : base::LazyInstance<FutexWaitList>::type FutexEmulation::wait_list_ =
21 : LAZY_INSTANCE_INITIALIZER;
22 :
23 :
24 22916 : 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 22916 : if (waiting_) {
35 5 : cond_.NotifyOne();
36 5 : interrupted_ = true;
37 : }
38 22916 : }
39 :
40 :
41 16 : FutexWaitList::FutexWaitList() : head_(nullptr), tail_(nullptr) {}
42 :
43 :
44 0 : void FutexWaitList::AddNode(FutexWaitListNode* node) {
45 : DCHECK(node->prev_ == nullptr && node->next_ == nullptr);
46 173 : if (tail_) {
47 30 : tail_->next_ = node;
48 : } else {
49 143 : head_ = node;
50 : }
51 :
52 173 : node->prev_ = tail_;
53 173 : node->next_ = nullptr;
54 173 : tail_ = node;
55 0 : }
56 :
57 :
58 0 : void FutexWaitList::RemoveNode(FutexWaitListNode* node) {
59 173 : if (node->prev_) {
60 4 : node->prev_->next_ = node->next_;
61 : } else {
62 169 : head_ = node->next_;
63 : }
64 :
65 173 : if (node->next_) {
66 30 : node->next_->prev_ = node->prev_;
67 : } else {
68 143 : tail_ = node->prev_;
69 : }
70 :
71 173 : node->prev_ = node->next_ = nullptr;
72 0 : }
73 :
74 :
75 192 : Object* FutexEmulation::Wait(Isolate* isolate,
76 : Handle<JSArrayBuffer> array_buffer, size_t addr,
77 : int32_t value, double rel_timeout_ms) {
78 : DCHECK(addr < NumberToSize(array_buffer->byte_length()));
79 :
80 : void* backing_store = array_buffer->backing_store();
81 : int32_t* p =
82 192 : reinterpret_cast<int32_t*>(static_cast<int8_t*>(backing_store) + addr);
83 :
84 : base::LockGuard<base::Mutex> lock_guard(mutex_.Pointer());
85 :
86 192 : if (*p != value) {
87 19 : return isolate->heap()->not_equal();
88 : }
89 :
90 173 : FutexWaitListNode* node = isolate->futex_wait_list_node();
91 :
92 173 : node->backing_store_ = backing_store;
93 173 : node->wait_addr_ = addr;
94 173 : node->waiting_ = true;
95 :
96 173 : bool use_timeout = rel_timeout_ms != V8_INFINITY;
97 :
98 : base::TimeDelta rel_timeout;
99 173 : if (use_timeout) {
100 : // Convert to nanoseconds.
101 36 : double rel_timeout_ns = rel_timeout_ms *
102 : base::Time::kNanosecondsPerMicrosecond *
103 36 : base::Time::kMicrosecondsPerMillisecond;
104 36 : if (rel_timeout_ns >
105 : static_cast<double>(std::numeric_limits<int64_t>::max())) {
106 : // 2**63 nanoseconds is 292 years. Let's just treat anything greater as
107 : // infinite.
108 : use_timeout = false;
109 : } else {
110 72 : rel_timeout = base::TimeDelta::FromNanoseconds(
111 36 : static_cast<int64_t>(rel_timeout_ns));
112 : }
113 : }
114 :
115 173 : base::TimeTicks start_time = base::TimeTicks::Now();
116 : base::TimeTicks timeout_time = start_time + rel_timeout;
117 : base::TimeTicks current_time = start_time;
118 :
119 : wait_list_.Pointer()->AddNode(node);
120 :
121 : Object* result;
122 :
123 : while (true) {
124 318 : bool interrupted = node->interrupted_;
125 318 : node->interrupted_ = false;
126 :
127 : // Unlock the mutex here to prevent deadlock from lock ordering between
128 : // mutex_ and mutexes locked by HandleInterrupts.
129 318 : 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 318 : if (interrupted) {
144 5 : Object* interrupt_object = isolate->stack_guard()->HandleInterrupts();
145 5 : if (interrupt_object->IsException(isolate)) {
146 : result = interrupt_object;
147 5 : mutex_.Pointer()->Lock();
148 5 : break;
149 : }
150 : }
151 :
152 313 : mutex_.Pointer()->Lock();
153 :
154 313 : if (node->interrupted_) {
155 : // An interrupt occurred while the mutex_ was unlocked. Don't wait yet.
156 : continue;
157 : }
158 :
159 313 : if (!node->waiting_) {
160 132 : result = isolate->heap()->ok();
161 132 : break;
162 : }
163 :
164 : // No interrupts, now wait.
165 181 : if (use_timeout) {
166 45 : current_time = base::TimeTicks::Now();
167 45 : if (current_time >= timeout_time) {
168 36 : result = isolate->heap()->timed_out();
169 36 : break;
170 : }
171 :
172 9 : base::TimeDelta time_until_timeout = timeout_time - current_time;
173 : DCHECK_GE(time_until_timeout.InMicroseconds(), 0);
174 : bool wait_for_result =
175 9 : node->cond_.WaitFor(mutex_.Pointer(), time_until_timeout);
176 : USE(wait_for_result);
177 : } else {
178 136 : node->cond_.Wait(mutex_.Pointer());
179 : }
180 :
181 : // Spurious wakeup, interrupt or timeout.
182 : }
183 :
184 : wait_list_.Pointer()->RemoveNode(node);
185 173 : node->waiting_ = false;
186 :
187 173 : return result;
188 : }
189 :
190 138 : Object* FutexEmulation::Wake(Isolate* isolate,
191 : Handle<JSArrayBuffer> array_buffer, size_t addr,
192 : uint32_t num_waiters_to_wake) {
193 : DCHECK(addr < NumberToSize(array_buffer->byte_length()));
194 :
195 : int waiters_woken = 0;
196 : void* backing_store = array_buffer->backing_store();
197 :
198 : base::LockGuard<base::Mutex> lock_guard(mutex_.Pointer());
199 138 : FutexWaitListNode* node = wait_list_.Pointer()->head_;
200 408 : while (node && num_waiters_to_wake > 0) {
201 132 : if (backing_store == node->backing_store_ && addr == node->wait_addr_) {
202 132 : node->waiting_ = false;
203 132 : node->cond_.NotifyOne();
204 132 : if (num_waiters_to_wake != kWakeAll) {
205 132 : --num_waiters_to_wake;
206 : }
207 132 : waiters_woken++;
208 : }
209 :
210 132 : node = node->next_;
211 : }
212 :
213 138 : return Smi::FromInt(waiters_woken);
214 : }
215 :
216 :
217 9829762 : Object* FutexEmulation::NumWaitersForTesting(Isolate* isolate,
218 : Handle<JSArrayBuffer> array_buffer,
219 : size_t addr) {
220 : DCHECK(addr < NumberToSize(array_buffer->byte_length()));
221 : void* backing_store = array_buffer->backing_store();
222 :
223 : base::LockGuard<base::Mutex> lock_guard(mutex_.Pointer());
224 :
225 : int waiters = 0;
226 9829762 : FutexWaitListNode* node = wait_list_.Pointer()->head_;
227 20355987 : while (node) {
228 696463 : if (backing_store == node->backing_store_ && addr == node->wait_addr_ &&
229 : node->waiting_) {
230 696463 : waiters++;
231 : }
232 :
233 696463 : node = node->next_;
234 : }
235 :
236 9829762 : return Smi::FromInt(waiters);
237 : }
238 :
239 : } // namespace internal
240 : } // namespace v8
|