summaryrefslogtreecommitdiff
path: root/deps/v8/src/heap/safepoint.h
blob: 7e935889b7a1e6bab1c56e578a7172f05c0b5411 (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
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
// Copyright 2020 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_HEAP_SAFEPOINT_H_
#define V8_HEAP_SAFEPOINT_H_

#include "src/base/platform/condition-variable.h"
#include "src/base/platform/mutex.h"
#include "src/common/globals.h"
#include "src/handles/persistent-handles.h"
#include "src/heap/local-heap.h"
#include "src/objects/visitors.h"

namespace v8 {
namespace internal {

class Heap;
class LocalHeap;
class PerClientSafepointData;
class RootVisitor;

// Used to bring all threads with heap access in an isolate to a safepoint such
// that e.g. a garbage collection can be performed.
class IsolateSafepoint final {
 public:
  explicit IsolateSafepoint(Heap* heap);

  // Iterate handles in local heaps
  void Iterate(RootVisitor* visitor);

  // Iterate local heaps
  template <typename Callback>
  void IterateLocalHeaps(Callback callback) {
    AssertActive();
    for (LocalHeap* current = local_heaps_head_; current;
         current = current->next_) {
      callback(current);
    }
  }

  void AssertActive() { local_heaps_mutex_.AssertHeld(); }

  V8_EXPORT_PRIVATE void AssertMainThreadIsOnlyThread();

 private:
  class Barrier {
    base::Mutex mutex_;
    base::ConditionVariable cv_resume_;
    base::ConditionVariable cv_stopped_;
    bool armed_;

    size_t stopped_ = 0;

    bool IsArmed() { return armed_; }

   public:
    Barrier() : armed_(false), stopped_(0) {}

    void Arm();
    void Disarm();
    void WaitUntilRunningThreadsInSafepoint(size_t running);

    void WaitInSafepoint();
    void WaitInUnpark();
    void NotifyPark();
  };

  enum class IncludeMainThread { kYes, kNo };

  // Wait until unpark operation is safe again.
  void WaitInUnpark();

  // Enter the safepoint from a running thread.
  void WaitInSafepoint();

  // Running thread reached a safepoint by parking itself.
  void NotifyPark();

  // Methods for entering/leaving local safepoint scopes.
  void EnterLocalSafepointScope();
  void LeaveLocalSafepointScope();

  // Methods for entering/leaving global safepoint scopes.
  void TryInitiateGlobalSafepointScope(Isolate* initiator,
                                       PerClientSafepointData* client_data);
  void InitiateGlobalSafepointScope(Isolate* initiator,
                                    PerClientSafepointData* client_data);
  void InitiateGlobalSafepointScopeRaw(Isolate* initiator,
                                       PerClientSafepointData* client_data);
  void LeaveGlobalSafepointScope(Isolate* initiator);

  // Blocks until all running threads reached a safepoint.
  void WaitUntilRunningThreadsInSafepoint(
      const PerClientSafepointData* client_data);

  IncludeMainThread ShouldIncludeMainThread(Isolate* initiator);

  void LockMutex(LocalHeap* local_heap);

  size_t SetSafepointRequestedFlags(IncludeMainThread include_main_thread);
  void ClearSafepointRequestedFlags(IncludeMainThread include_main_thread);

  template <typename Callback>
  void AddLocalHeap(LocalHeap* local_heap, Callback callback) {
    // Safepoint holds this lock in order to stop threads from starting or
    // stopping.
    base::RecursiveMutexGuard guard(&local_heaps_mutex_);

    // Additional code protected from safepoint
    callback();

    // Add list to doubly-linked list
    if (local_heaps_head_) local_heaps_head_->prev_ = local_heap;
    local_heap->prev_ = nullptr;
    local_heap->next_ = local_heaps_head_;
    local_heaps_head_ = local_heap;
  }

  template <typename Callback>
  void RemoveLocalHeap(LocalHeap* local_heap, Callback callback) {
    base::RecursiveMutexGuard guard(&local_heaps_mutex_);

    // Additional code protected from safepoint
    callback();

    // Remove list from doubly-linked list
    if (local_heap->next_) local_heap->next_->prev_ = local_heap->prev_;
    if (local_heap->prev_)
      local_heap->prev_->next_ = local_heap->next_;
    else
      local_heaps_head_ = local_heap->next_;
  }

  Isolate* isolate() const;
  Isolate* shared_heap_isolate() const;

  Barrier barrier_;
  Heap* heap_;

  // Mutex is used both for safepointing and adding/removing threads. A
  // RecursiveMutex is needed since we need to support nested SafepointScopes.
  base::RecursiveMutex local_heaps_mutex_;
  LocalHeap* local_heaps_head_;

  int active_safepoint_scopes_;

  friend class GlobalSafepoint;
  friend class GlobalSafepointScope;
  friend class Isolate;
  friend class IsolateSafepointScope;
  friend class LocalHeap;
};

class V8_NODISCARD IsolateSafepointScope {
 public:
  V8_EXPORT_PRIVATE explicit IsolateSafepointScope(Heap* heap);
  V8_EXPORT_PRIVATE ~IsolateSafepointScope();

 private:
  IsolateSafepoint* safepoint_;
};

// Used for reaching a global safepoint, a safepoint across all client isolates
// of the shared isolate.
class GlobalSafepoint final {
 public:
  explicit GlobalSafepoint(Isolate* isolate);

  void AppendClient(Isolate* client);
  void RemoveClient(Isolate* client);

  template <typename Callback>
  void IterateClientIsolates(Callback callback) {
    for (Isolate* current = clients_head_; current;
         current = current->global_safepoint_next_client_isolate_) {
      callback(current);
    }
  }

  void AssertNoClientsOnTearDown();

  void AssertActive() { clients_mutex_.AssertHeld(); }

 private:
  void EnterGlobalSafepointScope(Isolate* initiator);
  void LeaveGlobalSafepointScope(Isolate* initiator);

  Isolate* const shared_heap_isolate_;
  base::Mutex clients_mutex_;
  Isolate* clients_head_ = nullptr;

  friend class GlobalSafepointScope;
  friend class Isolate;
};

class V8_NODISCARD GlobalSafepointScope {
 public:
  V8_EXPORT_PRIVATE explicit GlobalSafepointScope(Isolate* initiator);
  V8_EXPORT_PRIVATE ~GlobalSafepointScope();

 private:
  Isolate* const initiator_;
  Isolate* const shared_heap_isolate_;
};

enum class SafepointKind { kIsolate, kGlobal };

class V8_NODISCARD SafepointScope {
 public:
  V8_EXPORT_PRIVATE explicit SafepointScope(Isolate* initiator,
                                            SafepointKind kind);

 private:
  base::Optional<IsolateSafepointScope> isolate_safepoint_;
  base::Optional<GlobalSafepointScope> global_safepoint_;
};

}  // namespace internal
}  // namespace v8

#endif  // V8_HEAP_SAFEPOINT_H_