summaryrefslogtreecommitdiff
path: root/deps/v8/src/futex-emulation.h
blob: a1580099d6a79fabacf511647f288e8352df28c7 (plain)
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
// 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_FUTEX_EMULATION_H_
#define V8_FUTEX_EMULATION_H_

#include <stdint.h>

#include "src/allocation.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"

// 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;
}  // base

namespace internal {

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),
        backing_store_(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_;
  void* 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* Wait(Isolate* isolate, Handle<JSArrayBuffer> array_buffer,
                      size_t addr, int32_t value, double rel_timeout_ms);

  // 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.
  static Object* Wake(Isolate* isolate, 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(Isolate* isolate,
                                      Handle<JSArrayBuffer> array_buffer,
                                      size_t addr);

 private:
  friend class FutexWaitListNode;
  friend class AtomicsWaitWakeHandle;

  // `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_FUTEX_EMULATION_H_