futex-emulation.h 5.35 KB
Newer Older
binji's avatar
binji committed
1 2 3 4
// 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.

5 6
#ifndef V8_EXECUTION_FUTEX_EMULATION_H_
#define V8_EXECUTION_FUTEX_EMULATION_H_
binji's avatar
binji committed
7 8 9

#include <stdint.h>

10
#include "src/base/atomicops.h"
binji's avatar
binji committed
11 12 13 14
#include "src/base/lazy-instance.h"
#include "src/base/macros.h"
#include "src/base/platform/condition-variable.h"
#include "src/base/platform/mutex.h"
15
#include "src/utils/allocation.h"
binji's avatar
binji committed
16 17 18 19 20 21 22

// 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,
23
// found here: https://github.com/tc39/ecmascript_sharedmem
binji's avatar
binji committed
24 25 26 27 28

namespace v8 {

namespace base {
class TimeDelta;
29
}  // namespace base
binji's avatar
binji committed
30 31 32

namespace internal {

marja's avatar
marja committed
33 34
template <typename T>
class Handle;
binji's avatar
binji committed
35
class Isolate;
36
class JSArrayBuffer;
binji's avatar
binji committed
37

38 39 40 41 42 43 44 45 46 47 48 49
class AtomicsWaitWakeHandle {
 public:
  explicit AtomicsWaitWakeHandle(Isolate* isolate) : isolate_(isolate) {}

  void Wake();
  inline bool has_stopped() const { return stopped_; }

 private:
  Isolate* isolate_;
  bool stopped_ = false;
};

binji's avatar
binji committed
50 51 52 53 54 55 56
class FutexWaitListNode {
 public:
  FutexWaitListNode()
      : prev_(nullptr),
        next_(nullptr),
        backing_store_(nullptr),
        wait_addr_(0),
57 58 59 60
        waiting_(false),
        interrupted_(false) {}

  void NotifyWake();
binji's avatar
binji committed
61 62 63 64

 private:
  friend class FutexEmulation;
  friend class FutexWaitList;
65
  friend class ResetWaitingOnScopeExit;
binji's avatar
binji committed
66 67

  base::ConditionVariable cond_;
68
  // prev_ and next_ are protected by FutexEmulation::mutex_.
binji's avatar
binji committed
69 70 71 72
  FutexWaitListNode* prev_;
  FutexWaitListNode* next_;
  void* backing_store_;
  size_t wait_addr_;
73
  // waiting_ and interrupted_ are protected by FutexEmulation::mutex_
74 75
  // if this node is currently contained in FutexEmulation::wait_list_
  // or an AtomicsWaitWakeHandle has access to it.
binji's avatar
binji committed
76
  bool waiting_;
77
  bool interrupted_;
binji's avatar
binji committed
78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97

  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);
};

98 99 100 101 102 103 104 105 106 107
class ResetWaitingOnScopeExit {
 public:
  explicit ResetWaitingOnScopeExit(FutexWaitListNode* node) : node_(node) {}
  ~ResetWaitingOnScopeExit() { node_->waiting_ = false; }

 private:
  FutexWaitListNode* node_;

  DISALLOW_COPY_AND_ASSIGN(ResetWaitingOnScopeExit);
};
binji's avatar
binji committed
108 109 110

class FutexEmulation : public AllStatic {
 public:
111 112 113
  // Pass to Wake() to wake all waiters.
  static const uint32_t kWakeAll = UINT32_MAX;

114
  // Check that array_buffer[addr] == value, and return "not-equal" if not. If
binji's avatar
binji committed
115 116 117
  // 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.
118
  // If woken, return "ok", otherwise return "timed-out". The initial check and
binji's avatar
binji committed
119
  // the decision to wait happen atomically.
120 121 122 123 124 125
  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);
126 127 128

  // Same as WaitJs above except it returns 0 (ok), 1 (not equal) and 2 (timed
  // out) as expected by Wasm.
129 130
  static Object Wait32(Isolate* isolate, Handle<JSArrayBuffer> array_buffer,
                       size_t addr, int32_t value, double rel_timeout_ms);
131 132 133

  // Same as Wait32 above except it checks for an int64_t value in the
  // array_buffer.
134 135
  static Object Wait64(Isolate* isolate, Handle<JSArrayBuffer> array_buffer,
                       size_t addr, int64_t value, double rel_timeout_ms);
binji's avatar
binji committed
136 137

  // Wake |num_waiters_to_wake| threads that are waiting on the given |addr|.
138 139 140
  // |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.
141 142
  static Object Wake(Handle<JSArrayBuffer> array_buffer, size_t addr,
                     uint32_t num_waiters_to_wake);
binji's avatar
binji committed
143 144 145

  // Return the number of threads waiting on |addr|. Should only be used for
  // testing.
146 147
  static Object NumWaitersForTesting(Handle<JSArrayBuffer> array_buffer,
                                     size_t addr);
binji's avatar
binji committed
148 149

 private:
150
  friend class FutexWaitListNode;
151
  friend class AtomicsWaitWakeHandle;
152

153
  template <typename T>
154 155
  static Object Wait(Isolate* isolate, Handle<JSArrayBuffer> array_buffer,
                     size_t addr, T value, double rel_timeout_ms);
156

157 158 159 160 161
  // `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.
binji's avatar
binji committed
162 163 164
  static base::LazyMutex mutex_;
  static base::LazyInstance<FutexWaitList>::type wait_list_;
};
165 166
}  // namespace internal
}  // namespace v8
binji's avatar
binji committed
167

168
#endif  // V8_EXECUTION_FUTEX_EMULATION_H_