5 #include "src/futex-emulation.h" 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 #include "src/objects/js-array-buffer-inl.h" 20 using AtomicsWaitEvent = v8::Isolate::AtomicsWaitEvent;
22 base::LazyMutex FutexEmulation::mutex_ = LAZY_MUTEX_INITIALIZER;
23 base::LazyInstance<FutexWaitList>::type FutexEmulation::wait_list_ =
24 LAZY_INSTANCE_INITIALIZER;
27 void FutexWaitListNode::NotifyWake() {
33 base::MutexGuard lock_guard(FutexEmulation::mutex_.Pointer());
40 FutexWaitList::FutexWaitList() : head_(nullptr), tail_(nullptr) {}
43 void FutexWaitList::AddNode(FutexWaitListNode* node) {
44 DCHECK(node->prev_ ==
nullptr && node->next_ ==
nullptr);
52 node->next_ =
nullptr;
57 void FutexWaitList::RemoveNode(FutexWaitListNode* node) {
59 node->prev_->next_ = node->next_;
65 node->next_->prev_ = node->prev_;
70 node->prev_ = node->next_ =
nullptr;
73 void AtomicsWaitWakeHandle::Wake() {
79 base::MutexGuard lock_guard(FutexEmulation::mutex_.Pointer());
82 isolate_->futex_wait_list_node()->NotifyWake();
85 enum WaitReturnValue :
int { kOk = 0, kNotEqual = 1, kTimedOut = 2 };
87 Object* FutexEmulation::WaitJs(Isolate* isolate,
88 Handle<JSArrayBuffer> array_buffer,
size_t addr,
89 int32_t value,
double rel_timeout_ms) {
90 Object* res = Wait(isolate, array_buffer, addr, value, rel_timeout_ms);
92 int val = Smi::ToInt(res);
94 case WaitReturnValue::kOk:
95 return ReadOnlyRoots(isolate).ok();
96 case WaitReturnValue::kNotEqual:
97 return ReadOnlyRoots(isolate).not_equal();
98 case WaitReturnValue::kTimedOut:
99 return ReadOnlyRoots(isolate).timed_out();
107 Object* FutexEmulation::Wait(Isolate* isolate,
108 Handle<JSArrayBuffer> array_buffer,
size_t addr,
109 int32_t value,
double rel_timeout_ms) {
110 DCHECK_LT(addr, array_buffer->byte_length());
112 bool use_timeout = rel_timeout_ms != V8_INFINITY;
114 base::TimeDelta rel_timeout;
117 double rel_timeout_ns = rel_timeout_ms *
118 base::Time::kNanosecondsPerMicrosecond *
119 base::Time::kMicrosecondsPerMillisecond;
121 static_cast<double>(std::numeric_limits<int64_t>::max())) {
126 rel_timeout = base::TimeDelta::FromNanoseconds(
127 static_cast<int64_t>(rel_timeout_ns));
131 AtomicsWaitWakeHandle stop_handle(isolate);
133 isolate->RunAtomicsWaitCallback(AtomicsWaitEvent::kStartWait, array_buffer,
134 addr, value, rel_timeout_ms, &stop_handle);
136 if (isolate->has_scheduled_exception()) {
137 return isolate->PromoteScheduledException();
141 AtomicsWaitEvent callback_result = AtomicsWaitEvent::kWokenUp;
144 base::MutexGuard lock_guard(mutex_.Pointer());
145 void* backing_store = array_buffer->backing_store();
147 FutexWaitListNode* node = isolate->futex_wait_list_node();
148 node->backing_store_ = backing_store;
149 node->wait_addr_ = addr;
150 node->waiting_ =
true;
154 ResetWaitingOnScopeExit reset_waiting(node);
157 reinterpret_cast<int32_t*
>(
static_cast<int8_t*
>(backing_store) + addr);
159 result = Smi::FromInt(WaitReturnValue::kNotEqual);
160 callback_result = AtomicsWaitEvent::kNotEqual;
164 base::TimeTicks timeout_time;
165 base::TimeTicks current_time;
168 current_time = base::TimeTicks::Now();
169 timeout_time = current_time + rel_timeout;
172 wait_list_.Pointer()->AddNode(node);
175 bool interrupted = node->interrupted_;
176 node->interrupted_ =
false;
180 mutex_.Pointer()->Unlock();
195 Object* interrupt_object = isolate->stack_guard()->HandleInterrupts();
196 if (interrupt_object->IsException(isolate)) {
197 result = interrupt_object;
198 callback_result = AtomicsWaitEvent::kTerminatedExecution;
199 mutex_.Pointer()->Lock();
204 mutex_.Pointer()->Lock();
206 if (node->interrupted_) {
211 if (stop_handle.has_stopped()) {
212 node->waiting_ =
false;
213 callback_result = AtomicsWaitEvent::kAPIStopped;
216 if (!node->waiting_) {
217 result = Smi::FromInt(WaitReturnValue::kOk);
223 current_time = base::TimeTicks::Now();
224 if (current_time >= timeout_time) {
225 result = Smi::FromInt(WaitReturnValue::kTimedOut);
226 callback_result = AtomicsWaitEvent::kTimedOut;
230 base::TimeDelta time_until_timeout = timeout_time - current_time;
231 DCHECK_GE(time_until_timeout.InMicroseconds(), 0);
232 bool wait_for_result =
233 node->cond_.WaitFor(mutex_.Pointer(), time_until_timeout);
234 USE(wait_for_result);
236 node->cond_.Wait(mutex_.Pointer());
242 wait_list_.Pointer()->RemoveNode(node);
245 isolate->RunAtomicsWaitCallback(callback_result, array_buffer, addr, value,
246 rel_timeout_ms,
nullptr);
248 if (isolate->has_scheduled_exception()) {
249 CHECK_NE(callback_result, AtomicsWaitEvent::kTerminatedExecution);
250 result = isolate->PromoteScheduledException();
256 Object* FutexEmulation::Wake(Handle<JSArrayBuffer> array_buffer,
size_t addr,
258 DCHECK_LT(addr, array_buffer->byte_length());
260 int waiters_woken = 0;
261 void* backing_store = array_buffer->backing_store();
263 base::MutexGuard lock_guard(mutex_.Pointer());
264 FutexWaitListNode* node = wait_list_.Pointer()->head_;
265 while (node && num_waiters_to_wake > 0) {
266 if (backing_store == node->backing_store_ && addr == node->wait_addr_) {
267 node->waiting_ =
false;
268 node->cond_.NotifyOne();
269 if (num_waiters_to_wake != kWakeAll) {
270 --num_waiters_to_wake;
278 return Smi::FromInt(waiters_woken);
281 Object* FutexEmulation::NumWaitersForTesting(Handle<JSArrayBuffer> array_buffer,
283 DCHECK_LT(addr, array_buffer->byte_length());
284 void* backing_store = array_buffer->backing_store();
286 base::MutexGuard lock_guard(mutex_.Pointer());
289 FutexWaitListNode* node = wait_list_.Pointer()->head_;
291 if (backing_store == node->backing_store_ && addr == node->wait_addr_ &&
299 return Smi::FromInt(waiters);