• 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_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:
AtomicsWaitWakeHandle(Isolate * isolate)40   explicit AtomicsWaitWakeHandle(Isolate* isolate) : isolate_(isolate) {}
41 
42   void Wake();
has_stopped()43   inline bool has_stopped() const { return stopped_; }
44 
45  private:
46   Isolate* isolate_;
47   bool stopped_ = false;
48 };
49 
50 class FutexWaitListNode {
51  public:
FutexWaitListNode()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 
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:
ResetWaitingOnScopeExit(FutexWaitListNode * node)101   explicit ResetWaitingOnScopeExit(FutexWaitListNode* node) : node_(node) {}
~ResetWaitingOnScopeExit()102   ~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* Wait(Isolate* isolate, Handle<JSArrayBuffer> array_buffer,
122                       size_t addr, int32_t value, double rel_timeout_ms);
123 
124   // Wake |num_waiters_to_wake| threads that are waiting on the given |addr|.
125   // |num_waiters_to_wake| can be kWakeAll, in which case all waiters are
126   // woken. The rest of the waiters will continue to wait. The return value is
127   // the number of woken waiters.
128   static Object* Wake(Handle<JSArrayBuffer> array_buffer, size_t addr,
129                       uint32_t num_waiters_to_wake);
130 
131   // Return the number of threads waiting on |addr|. Should only be used for
132   // testing.
133   static Object* NumWaitersForTesting(Handle<JSArrayBuffer> array_buffer,
134                                       size_t addr);
135 
136  private:
137   friend class FutexWaitListNode;
138   friend class AtomicsWaitWakeHandle;
139 
140   // `mutex_` protects the composition of `wait_list_` (i.e. no elements may be
141   // added or removed without holding this mutex), as well as the `waiting_`
142   // and `interrupted_` fields for each individual list node that is currently
143   // part of the list. It must be the mutex used together with the `cond_`
144   // condition variable of such nodes.
145   static base::LazyMutex mutex_;
146   static base::LazyInstance<FutexWaitList>::type wait_list_;
147 };
148 }  // namespace internal
149 }  // namespace v8
150 
151 #endif  // V8_FUTEX_EMULATION_H_
152