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#ifndef V8_FUTEX_EMULATION_H_
6#define V8_FUTEX_EMULATION_H_
7
8#include <stdint.h>
9
10#include "src/allocation.h"
11#include "src/base/atomicops.h"
12#include "src/base/lazy-instance.h"
13#include "src/base/macros.h"
14#include "src/base/platform/condition-variable.h"
15#include "src/base/platform/mutex.h"
16
17// Support for emulating futexes, a low-level synchronization primitive. They
18// are natively supported by Linux, but must be emulated for other platforms.
19// This library emulates them on all platforms using mutexes and condition
20// variables for consistency.
21//
22// This is used by the Futex API defined in the SharedArrayBuffer draft spec,
23// found here: https://github.com/tc39/ecmascript_sharedmem
24
25namespace v8 {
26
27namespace base {
28class TimeDelta;
29} // base
30
31namespace internal {
32
33template <typename T>
34class Handle;
35class Isolate;
36class JSArrayBuffer;
37
38class AtomicsWaitWakeHandle {
39 public:
40 explicit AtomicsWaitWakeHandle(Isolate* isolate) : isolate_(isolate) {}
41
42 void Wake();
43 inline bool has_stopped() const { return stopped_; }
44
45 private:
46 Isolate* isolate_;
47 bool stopped_ = false;
48};
49
50class FutexWaitListNode {
51 public:
52 FutexWaitListNode()
53 : prev_(nullptr),
54 next_(nullptr),
55 backing_store_(nullptr),
56 wait_addr_(0),
57 waiting_(false),
58 interrupted_(false) {}
59
60 void NotifyWake();
61
62 private:
63 friend class FutexEmulation;
64 friend class FutexWaitList;
65 friend class ResetWaitingOnScopeExit;
66
67 base::ConditionVariable cond_;
68 // prev_ and next_ are protected by FutexEmulation::mutex_.
69 FutexWaitListNode* prev_;
70 FutexWaitListNode* next_;
71 void* backing_store_;
72 size_t wait_addr_;
73 // waiting_ and interrupted_ are protected by FutexEmulation::mutex_
74 // if this node is currently contained in FutexEmulation::wait_list_
75 // or an AtomicsWaitWakeHandle has access to it.
76 bool waiting_;
77 bool interrupted_;
78
79 DISALLOW_COPY_AND_ASSIGN(FutexWaitListNode);
80};
81
82
83class FutexWaitList {
84 public:
85 FutexWaitList();
86
87 void AddNode(FutexWaitListNode* node);
88 void RemoveNode(FutexWaitListNode* node);
89
90 private:
91 friend class FutexEmulation;
92
93 FutexWaitListNode* head_;
94 FutexWaitListNode* tail_;
95
96 DISALLOW_COPY_AND_ASSIGN(FutexWaitList);
97};
98
99class ResetWaitingOnScopeExit {
100 public:
101 explicit ResetWaitingOnScopeExit(FutexWaitListNode* node) : node_(node) {}
102 ~ResetWaitingOnScopeExit() { node_->waiting_ = false; }
103
104 private:
105 FutexWaitListNode* node_;
106
107 DISALLOW_COPY_AND_ASSIGN(ResetWaitingOnScopeExit);
108};
109
110class FutexEmulation : public AllStatic {
111 public:
112 // Pass to Wake() to wake all waiters.
113 static const uint32_t kWakeAll = UINT32_MAX;
114
115 // Check that array_buffer[addr] == value, and return "not-equal" if not. If
116 // they are equal, block execution on |isolate|'s thread until woken via
117 // |Wake|, or when the time given in |rel_timeout_ms| elapses. Note that
118 // |rel_timeout_ms| can be Infinity.
119 // If woken, return "ok", otherwise return "timed-out". The initial check and
120 // the decision to wait happen atomically.
121 static Object WaitJs(Isolate* isolate, Handle<JSArrayBuffer> array_buffer,
122 size_t addr, int32_t value, double rel_timeout_ms);
123
124 // Same as WaitJs above except it returns 0 (ok), 1 (not equal) and 2 (timed
125 // out) as expected by Wasm.
126 static Object Wait32(Isolate* isolate, Handle<JSArrayBuffer> array_buffer,
127 size_t addr, int32_t value, double rel_timeout_ms);
128
129 // Same as Wait32 above except it checks for an int64_t value in the
130 // array_buffer.
131 static Object Wait64(Isolate* isolate, Handle<JSArrayBuffer> array_buffer,
132 size_t addr, int64_t value, double rel_timeout_ms);
133
134 // Wake |num_waiters_to_wake| threads that are waiting on the given |addr|.
135 // |num_waiters_to_wake| can be kWakeAll, in which case all waiters are
136 // woken. The rest of the waiters will continue to wait. The return value is
137 // the number of woken waiters.
138 static Object Wake(Handle<JSArrayBuffer> array_buffer, size_t addr,
139 uint32_t num_waiters_to_wake);
140
141 // Return the number of threads waiting on |addr|. Should only be used for
142 // testing.
143 static Object NumWaitersForTesting(Handle<JSArrayBuffer> array_buffer,
144 size_t addr);
145
146 private:
147 friend class FutexWaitListNode;
148 friend class AtomicsWaitWakeHandle;
149
150 template <typename T>
151 static Object Wait(Isolate* isolate, Handle<JSArrayBuffer> array_buffer,
152 size_t addr, T value, double rel_timeout_ms);
153
154 // `mutex_` protects the composition of `wait_list_` (i.e. no elements may be
155 // added or removed without holding this mutex), as well as the `waiting_`
156 // and `interrupted_` fields for each individual list node that is currently
157 // part of the list. It must be the mutex used together with the `cond_`
158 // condition variable of such nodes.
159 static base::LazyMutex mutex_;
160 static base::LazyInstance<FutexWaitList>::type wait_list_;
161};
162} // namespace internal
163} // namespace v8
164
165#endif // V8_FUTEX_EMULATION_H_
166