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