• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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_EXECUTION_FUTEX_EMULATION_H_
6 #define V8_EXECUTION_FUTEX_EMULATION_H_
7 
8 #include <stdint.h>
9 
10 #include "include/v8-persistent-handle.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 #include "src/base/platform/time.h"
17 #include "src/tasks/cancelable-task.h"
18 #include "src/utils/allocation.h"
19 
20 // Support for emulating futexes, a low-level synchronization primitive. They
21 // are natively supported by Linux, but must be emulated for other platforms.
22 // This library emulates them on all platforms using mutexes and condition
23 // variables for consistency.
24 //
25 // This is used by the Futex API defined in the SharedArrayBuffer draft spec,
26 // found here: https://github.com/tc39/ecmascript_sharedmem
27 
28 namespace v8 {
29 
30 class Promise;
31 
32 namespace base {
33 class TimeDelta;
34 }  // namespace base
35 
36 namespace internal {
37 
38 class BackingStore;
39 class FutexWaitList;
40 
41 template <typename T>
42 class Handle;
43 class Isolate;
44 class JSArrayBuffer;
45 
46 class AtomicsWaitWakeHandle {
47  public:
AtomicsWaitWakeHandle(Isolate * isolate)48   explicit AtomicsWaitWakeHandle(Isolate* isolate) : isolate_(isolate) {}
49 
50   void Wake();
has_stopped()51   inline bool has_stopped() const { return stopped_; }
52 
53  private:
54   Isolate* isolate_;
55   bool stopped_ = false;
56 };
57 
58 class FutexWaitListNode {
59  public:
60   // Create a sync FutexWaitListNode.
61   FutexWaitListNode() = default;
62 
63   // Create an async FutexWaitListNode.
64   FutexWaitListNode(const std::shared_ptr<BackingStore>& backing_store,
65                     size_t wait_addr, Handle<JSObject> promise_capability,
66                     Isolate* isolate);
67   ~FutexWaitListNode();
68   FutexWaitListNode(const FutexWaitListNode&) = delete;
69   FutexWaitListNode& operator=(const FutexWaitListNode&) = delete;
70 
71   void NotifyWake();
72 
IsAsync()73   bool IsAsync() const { return isolate_for_async_waiters_ != nullptr; }
74 
75   // Returns false if the cancelling failed, true otherwise.
76   bool CancelTimeoutTask();
77 
78   class V8_NODISCARD ResetWaitingOnScopeExit {
79    public:
ResetWaitingOnScopeExit(FutexWaitListNode * node)80     explicit ResetWaitingOnScopeExit(FutexWaitListNode* node) : node_(node) {}
~ResetWaitingOnScopeExit()81     ~ResetWaitingOnScopeExit() { node_->waiting_ = false; }
82     ResetWaitingOnScopeExit(const ResetWaitingOnScopeExit&) = delete;
83     ResetWaitingOnScopeExit& operator=(const ResetWaitingOnScopeExit&) = delete;
84 
85    private:
86     FutexWaitListNode* node_;
87   };
88 
89  private:
90   friend class FutexEmulation;
91   friend class FutexWaitList;
92 
93   // Set only for async FutexWaitListNodes.
94   Isolate* isolate_for_async_waiters_ = nullptr;
95   std::shared_ptr<TaskRunner> task_runner_;
96   CancelableTaskManager* cancelable_task_manager_ = nullptr;
97 
98   base::ConditionVariable cond_;
99   // prev_ and next_ are protected by FutexEmulation::mutex_.
100   FutexWaitListNode* prev_ = nullptr;
101   FutexWaitListNode* next_ = nullptr;
102 
103   std::weak_ptr<BackingStore> backing_store_;
104   size_t wait_addr_ = 0;
105 
106   // The memory location the FutexWaitListNode is waiting on. Equals
107   // backing_store_->buffer_start() + wait_addr_ at FutexWaitListNode creation
108   // time. Storing the wait_location_ separately is needed, since we can't
109   // necessarily reconstruct it, because the BackingStore might get deleted
110   // while the FutexWaitListNode is still alive. FutexWaitListNode must know its
111   // wait location, since they are stored in per-location lists, and to remove
112   // the node, we need to be able to find the list it's on (to be able to
113   // update the head and tail of the list).
114   int8_t* wait_location_ = nullptr;
115 
116   // waiting_ and interrupted_ are protected by FutexEmulation::mutex_
117   // if this node is currently contained in FutexEmulation::wait_list_
118   // or an AtomicsWaitWakeHandle has access to it.
119   bool waiting_ = false;
120   bool interrupted_ = false;
121 
122   // Only for async FutexWaitListNodes. Weak Global handle. Must not be
123   // synchronously resolved by a non-owner Isolate.
124   v8::Global<v8::Promise> promise_;
125 
126   // Only for async FutexWaitListNodes. Weak Global handle.
127   v8::Global<v8::Context> native_context_;
128 
129   // Only for async FutexWaitListNodes. If async_timeout_time_ is
130   // base::TimeTicks(), this async waiter doesn't have a timeout or has already
131   // been notified. Values other than base::TimeTicks() are used for async
132   // waiters with an active timeout.
133   base::TimeTicks async_timeout_time_;
134 
135   CancelableTaskManager::Id timeout_task_id_ =
136       CancelableTaskManager::kInvalidTaskId;
137 };
138 
139 class FutexEmulation : public AllStatic {
140  public:
141   enum WaitMode { kSync = 0, kAsync };
142   enum class CallType { kIsNotWasm = 0, kIsWasm };
143 
144   // Pass to Wake() to wake all waiters.
145   static const uint32_t kWakeAll = UINT32_MAX;
146 
147   // Check that array_buffer[addr] == value, and return "not-equal" if not. If
148   // they are equal, block execution on |isolate|'s thread until woken via
149   // |Wake|, or when the time given in |rel_timeout_ms| elapses. Note that
150   // |rel_timeout_ms| can be Infinity.
151   // If woken, return "ok", otherwise return "timed-out". The initial check and
152   // the decision to wait happen atomically.
153   static Object WaitJs32(Isolate* isolate, WaitMode mode,
154                          Handle<JSArrayBuffer> array_buffer, size_t addr,
155                          int32_t value, double rel_timeout_ms);
156 
157   // An version of WaitJs32 for int64_t values.
158   static Object WaitJs64(Isolate* isolate, WaitMode mode,
159                          Handle<JSArrayBuffer> array_buffer, size_t addr,
160                          int64_t value, double rel_timeout_ms);
161 
162   // Same as WaitJs above except it returns 0 (ok), 1 (not equal) and 2 (timed
163   // out) as expected by Wasm.
164   V8_EXPORT_PRIVATE static Object WaitWasm32(Isolate* isolate,
165                                              Handle<JSArrayBuffer> array_buffer,
166                                              size_t addr, int32_t value,
167                                              int64_t rel_timeout_ns);
168 
169   // Same as Wait32 above except it checks for an int64_t value in the
170   // array_buffer.
171   V8_EXPORT_PRIVATE static Object WaitWasm64(Isolate* isolate,
172                                              Handle<JSArrayBuffer> array_buffer,
173                                              size_t addr, int64_t value,
174                                              int64_t rel_timeout_ns);
175 
176   // Wake |num_waiters_to_wake| threads that are waiting on the given |addr|.
177   // |num_waiters_to_wake| can be kWakeAll, in which case all waiters are
178   // woken. The rest of the waiters will continue to wait. The return value is
179   // the number of woken waiters.
180   V8_EXPORT_PRIVATE static Object Wake(Handle<JSArrayBuffer> array_buffer,
181                                        size_t addr,
182                                        uint32_t num_waiters_to_wake);
183 
184   // Called before |isolate| dies. Removes async waiters owned by |isolate|.
185   static void IsolateDeinit(Isolate* isolate);
186 
187   // Return the number of threads or async waiters waiting on |addr|. Should
188   // only be used for testing.
189   static Object NumWaitersForTesting(Handle<JSArrayBuffer> array_buffer,
190                                      size_t addr);
191 
192   // Return the number of async waiters (which belong to |isolate|) waiting.
193   // Should only be used for testing.
194   static Object NumAsyncWaitersForTesting(Isolate* isolate);
195 
196   // Return the number of async waiters which were waiting for |addr| and are
197   // now waiting for the Promises to be resolved. Should only be used for
198   // testing.
199   static Object NumUnresolvedAsyncPromisesForTesting(
200       Handle<JSArrayBuffer> array_buffer, size_t addr);
201 
202  private:
203   friend class FutexWaitListNode;
204   friend class AtomicsWaitWakeHandle;
205   friend class ResolveAsyncWaiterPromisesTask;
206   friend class AsyncWaiterTimeoutTask;
207 
208   template <typename T>
209   static Object Wait(Isolate* isolate, WaitMode mode,
210                      Handle<JSArrayBuffer> array_buffer, size_t addr, T value,
211                      double rel_timeout_ms);
212 
213   template <typename T>
214   static Object Wait(Isolate* isolate, WaitMode mode,
215                      Handle<JSArrayBuffer> array_buffer, size_t addr, T value,
216                      bool use_timeout, int64_t rel_timeout_ns,
217                      CallType call_type = CallType::kIsNotWasm);
218 
219   template <typename T>
220   static Object WaitSync(Isolate* isolate, Handle<JSArrayBuffer> array_buffer,
221                          size_t addr, T value, bool use_timeout,
222                          int64_t rel_timeout_ns, CallType call_type);
223 
224   template <typename T>
225   static Object WaitAsync(Isolate* isolate, Handle<JSArrayBuffer> array_buffer,
226                           size_t addr, T value, bool use_timeout,
227                           int64_t rel_timeout_ns, CallType call_type);
228 
229   // Resolve the Promises of the async waiters which belong to |isolate|.
230   static void ResolveAsyncWaiterPromises(Isolate* isolate);
231 
232   static void ResolveAsyncWaiterPromise(FutexWaitListNode* node);
233 
234   static void HandleAsyncWaiterTimeout(FutexWaitListNode* node);
235 
236   static void NotifyAsyncWaiter(FutexWaitListNode* node);
237 
238   // Remove the node's Promise from the NativeContext's Promise set.
239   static void CleanupAsyncWaiterPromise(FutexWaitListNode* node);
240 };
241 }  // namespace internal
242 }  // namespace v8
243 
244 #endif  // V8_EXECUTION_FUTEX_EMULATION_H_
245