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 : #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 :
25 : namespace v8 {
26 :
27 : namespace base {
28 : class TimeDelta;
29 : } // base
30 :
31 : namespace internal {
32 :
33 : template <typename T>
34 : class Handle;
35 : class Isolate;
36 : class JSArrayBuffer;
37 :
38 : class AtomicsWaitWakeHandle {
39 : public:
40 935 : 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 :
50 62867 : class FutexWaitListNode {
51 : public:
52 : FutexWaitListNode()
53 : : prev_(nullptr),
54 : next_(nullptr),
55 : backing_store_(nullptr),
56 : wait_addr_(0),
57 : waiting_(false),
58 62883 : 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 :
83 : class 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 :
99 : class ResetWaitingOnScopeExit {
100 : public:
101 : explicit ResetWaitingOnScopeExit(FutexWaitListNode* node) : node_(node) {}
102 935 : ~ResetWaitingOnScopeExit() { node_->waiting_ = false; }
103 :
104 : private:
105 : FutexWaitListNode* node_;
106 :
107 : DISALLOW_COPY_AND_ASSIGN(ResetWaitingOnScopeExit);
108 : };
109 :
110 : class 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_
|