v8
V8 is Google’s open source high-performance JavaScript and WebAssembly engine, written in C++.
Loading...
Searching...
No Matches
local-heap.h
Go to the documentation of this file.
1// Copyright 2020 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_HEAP_LOCAL_HEAP_H_
6#define V8_HEAP_LOCAL_HEAP_H_
7
8#include <atomic>
9#include <memory>
10
11#if V8_OS_DARWIN
12#include "pthread.h"
13#endif
14
15#include "src/base/logging.h"
16#include "src/base/macros.h"
24#include "src/heap/base/stack.h"
26
27namespace v8 {
28namespace internal {
29
30class Heap;
31class LocalHandles;
32class MarkingBarrier;
33class MutablePageMetadata;
34class Safepoint;
35
36// Do not use this variable directly, use LocalHeap::Current() instead.
37// Defined outside of LocalHeap because LocalHeap uses V8_EXPORT_PRIVATE.
38__attribute__((tls_model(V8_TLS_MODEL))) extern thread_local LocalHeap*
39 g_current_local_heap_ V8_CONSTINIT;
40
41// LocalHeap is used by the GC to track all threads with heap access in order to
42// stop them before performing a collection. LocalHeaps can be either Parked or
43// Running and are in Parked mode when initialized.
44// Running: Thread is allowed to access the heap but needs to give the GC the
45// chance to run regularly by manually invoking Safepoint(). The
46// thread can be parked using ParkedScope.
47// Parked: Heap access is not allowed, so the GC will not stop this thread
48// for a collection. Useful when threads do not need heap access for
49// some time or for blocking operations like locking a mutex.
51 public:
52 using GCEpilogueCallback = void(void*);
53
54 explicit LocalHeap(
56 std::unique_ptr<PersistentHandles> persistent_handles = nullptr);
57 ~LocalHeap();
58
59 // Frequently invoked by local thread to check whether safepoint was requested
60 // from the main thread.
61 void Safepoint() {
62 DCHECK(AllowSafepoints::IsAllowed());
63 ThreadState current = state_.load_relaxed();
64
65 if (V8_UNLIKELY(current.IsRunningWithSlowPathFlag())) {
66 SafepointSlowPath();
67 }
68 }
69
70 LocalHandles* handles() { return handles_.get(); }
71
72 template <typename T>
74 if (!persistent_handles_) {
75 EnsurePersistentHandles();
76 }
77 return persistent_handles_->NewHandle(object);
78 }
79
80 template <typename T, template <typename> typename HandleType>
82 requires(std::is_convertible_v<HandleType<T>, DirectHandle<T>>)
83 {
84 return NewPersistentHandle(*object);
85 }
86
87 template <typename T>
89 static_assert(kTaggedCanConvertToRawObjects);
90 return NewPersistentHandle(Tagged<T>(object));
91 }
92
93 template <typename T, template <typename> typename MaybeHandleType>
95 MaybeHandleType<T> maybe_handle)
96 requires(std::is_convertible_v<MaybeHandleType<T>, MaybeDirectHandle<T>>)
97 {
99 if (maybe_handle.ToHandle(&handle)) {
100 return NewPersistentHandle(handle);
101 }
102 return kNullMaybeHandle;
103 }
104
105 void AttachPersistentHandles(
106 std::unique_ptr<PersistentHandles> persistent_handles);
107 std::unique_ptr<PersistentHandles> DetachPersistentHandles();
108#ifdef DEBUG
109 bool HasPersistentHandles() { return !!persistent_handles_; }
110 bool ContainsPersistentHandle(Address* location);
111 bool ContainsLocalHandle(Address* location);
112 bool IsHandleDereferenceAllowed();
113#endif
114
115 bool IsParked() const;
116 bool IsRunning() const;
117
118 bool IsRetryOfFailedAllocation() const { return allocation_failed_; }
119
120 void SetRetryOfFailedAllocation(bool value) { allocation_failed_ = value; }
121
122 Heap* heap() const { return heap_; }
123 Heap* AsHeap() const { return heap(); }
124
125 // Heap root getters.
126#define ROOT_ACCESSOR(type, name, CamelName) inline Tagged<type> name();
128#undef ROOT_ACCESSOR
129
130 MarkingBarrier* marking_barrier() { return marking_barrier_.get(); }
131
132 // Give up all LABs. Used for e.g. full GCs.
133 void FreeLinearAllocationAreas();
134
135#if DEBUG
136 void VerifyLinearAllocationAreas() const;
137#endif // DEBUG
138
139 // Make all LABs iterable.
140 void MakeLinearAllocationAreasIterable();
141
142 // Mark/Unmark all LABs except for new and shared space. Use for black
143 // allocation.
144 void MarkLinearAllocationAreasBlack();
145 void UnmarkLinearAllocationsArea();
146
147 // Mark/Unmark linear allocation areas in shared heap black. Used for black
148 // allocation.
149 void MarkSharedLinearAllocationAreasBlack();
150 void UnmarkSharedLinearAllocationsArea();
151
152 // Free all LABs and reset free-lists except for the new and shared space.
153 // Used on black allocation.
154 void FreeLinearAllocationAreasAndResetFreeLists();
155 void FreeSharedLinearAllocationAreasAndResetFreeLists();
156
157 // Fetches a pointer to the local heap from the thread local storage.
158 // It is intended to be used in handle and write barrier code where it is
159 // difficult to get a pointer to the current instance of local heap otherwise.
160 // The result may be a nullptr if there is no local heap instance associated
161 // with the current thread.
162 V8_TLS_DECLARE_GETTER(Current, LocalHeap*, g_current_local_heap_)
163
164 static void SetCurrent(LocalHeap* local_heap);
165
166#ifdef DEBUG
167 void VerifyCurrent() const;
168#endif
169
170 // Allocate an uninitialized object.
171 V8_WARN_UNUSED_RESULT inline AllocationResult AllocateRaw(
172 int size_in_bytes, AllocationType allocation,
173 AllocationOrigin origin = AllocationOrigin::kRuntime,
174 AllocationAlignment alignment = kTaggedAligned);
175
176 // Allocate an uninitialized object.
177 template <HeapAllocator::AllocationRetryMode mode>
178 Tagged<HeapObject> AllocateRawWith(
179 int size_in_bytes, AllocationType allocation,
180 AllocationOrigin origin = AllocationOrigin::kRuntime,
181 AllocationAlignment alignment = kTaggedAligned);
182
183 // Allocates an uninitialized object and crashes when object
184 // cannot be allocated.
185 V8_WARN_UNUSED_RESULT inline Address AllocateRawOrFail(
186 int size_in_bytes, AllocationType allocation,
187 AllocationOrigin origin = AllocationOrigin::kRuntime,
188 AllocationAlignment alignment = kTaggedAligned);
189
190 void NotifyObjectSizeChange(Tagged<HeapObject> object, int old_size,
191 int new_size,
192 ClearRecordedSlots clear_recorded_slots);
193
194 bool is_main_thread() const { return is_main_thread_; }
196 return is_main_thread() && heap_ == heap;
197 }
198 V8_INLINE bool is_in_trampoline() const;
199
201 return heap_->deserialization_complete();
202 }
203 ReadOnlySpace* read_only_space() { return heap_->read_only_space(); }
204
205 // Adds a callback that is invoked with the given |data| after each GC.
206 // The callback is invoked on the main thread before any background thread
207 // resumes. The callback must not allocate or make any other calls that
208 // can trigger GC.
209 void AddGCEpilogueCallback(GCEpilogueCallback* callback, void* data,
211 GCCallbacksInSafepoint::GCType::kAll);
212 void RemoveGCEpilogueCallback(GCEpilogueCallback* callback, void* data);
213
214 // Weakens StrongDescriptorArray objects into regular DescriptorArray objects.
215 void WeakenDescriptorArrays(
216 GlobalHandleVector<DescriptorArray> strong_descriptor_arrays);
217
218 // Used to make SetupMainThread() available to unit tests.
219 void SetUpMainThreadForTesting();
220
221 // Execute the callback while the local heap is parked. All threads must
222 // always park via these methods, not directly with `ParkedScope`.
223 // The callback must be a callable object, expecting either no parameters or a
224 // const ParkedScope&, which serves as a witness for parking. The first
225 // variant checks if we are on the main thread or not. Use the other two
226 // variants if this already known.
227 template <typename Callback>
228 V8_INLINE void ExecuteWhileParked(Callback callback);
229 template <typename Callback>
230 V8_INLINE void ExecuteMainThreadWhileParked(Callback callback);
231 template <typename Callback>
232 V8_INLINE void ExecuteBackgroundThreadWhileParked(Callback callback);
233
234#if V8_OS_DARWIN
235 pthread_t thread_handle() { return thread_handle_; }
236#endif
237
238 HeapAllocator* allocator() { return &heap_allocator_; }
239
240 private:
244
245 class ThreadState final {
246 public:
247 static constexpr ThreadState Parked() {
248 return ThreadState(ParkedBit::kMask);
249 }
250 static constexpr ThreadState Running() { return ThreadState(0); }
251
252 constexpr bool IsRunning() const { return !ParkedBit::decode(raw_state_); }
253
255 return ThreadState(raw_state_ & ~ParkedBit::kMask);
256 }
257
258 constexpr bool IsParked() const { return ParkedBit::decode(raw_state_); }
259
261 return ThreadState(ParkedBit::kMask | raw_state_);
262 }
263
264 constexpr bool IsSafepointRequested() const {
265 return SafepointRequestedBit::decode(raw_state_);
266 }
267
268 constexpr bool IsCollectionRequested() const {
269 return CollectionRequestedBit::decode(raw_state_);
270 }
271
272 constexpr bool IsRunningWithSlowPathFlag() const {
273 return IsRunning() && (raw_state_ & (SafepointRequestedBit::kMask |
274 CollectionRequestedBit::kMask));
275 }
276
277 private:
278 constexpr explicit ThreadState(uint8_t value) : raw_state_(value) {}
279
280 constexpr uint8_t raw() const { return raw_state_; }
281
282 uint8_t raw_state_;
283
284 friend class LocalHeap;
285 };
286
287 class AtomicThreadState final {
288 public:
289 constexpr explicit AtomicThreadState(ThreadState state)
290 : raw_state_(state.raw()) {}
291
293 return raw_state_.compare_exchange_strong(expected.raw_state_,
294 updated.raw());
295 }
296
297 bool CompareExchangeWeak(ThreadState& expected, ThreadState updated) {
298 return raw_state_.compare_exchange_weak(expected.raw_state_,
299 updated.raw());
300 }
301
303 return ThreadState(raw_state_.fetch_or(ParkedBit::kMask));
304 }
305
307 return ThreadState(raw_state_.fetch_or(SafepointRequestedBit::kMask));
308 }
309
311 return ThreadState(raw_state_.fetch_and(~SafepointRequestedBit::kMask));
312 }
313
315 return ThreadState(raw_state_.fetch_or(CollectionRequestedBit::kMask));
316 }
317
319 return ThreadState(raw_state_.fetch_and(~CollectionRequestedBit::kMask));
320 }
321
323 return ThreadState(raw_state_.load(std::memory_order_relaxed));
324 }
325
326 private:
327 std::atomic<uint8_t> raw_state_;
328 };
329
330#ifdef DEBUG
331 bool IsSafeForConservativeStackScanning() const;
332#endif
333
334 template <typename Callback>
335 V8_INLINE void ExecuteWithStackMarker(Callback callback);
336
337 void Park() {
338 DCHECK(AllowSafepoints::IsAllowed());
339 DCHECK(IsSafeForConservativeStackScanning());
340 ThreadState expected = ThreadState::Running();
341 if (!state_.CompareExchangeWeak(expected, ThreadState::Parked())) {
342 ParkSlowPath();
343 }
344 }
345
346 void Unpark() {
347 DCHECK(AllowSafepoints::IsAllowed());
348 ThreadState expected = ThreadState::Parked();
349 if (!state_.CompareExchangeWeak(expected, ThreadState::Running())) {
350 UnparkSlowPath();
351 }
352 }
353
354 void ParkSlowPath();
355 void UnparkSlowPath();
356 void EnsureParkedBeforeDestruction();
357 void SafepointSlowPath();
358 void SleepInSafepoint();
359 void SleepInUnpark();
360
361 template <typename Callback>
362 V8_INLINE void ParkAndExecuteCallback(Callback callback);
363
364 void EnsurePersistentHandles();
365
366 void InvokeGCEpilogueCallbacksInSafepoint(
368
369 // Set up this LocalHeap as main thread.
370 void SetUpMainThread(LinearAllocationArea& new_allocation_info,
371 LinearAllocationArea& old_allocation_info);
372
373 void SetUpMarkingBarrier();
374 void SetUpSharedMarking();
375
379
381
382#if V8_OS_DARWIN
383 pthread_t thread_handle_;
384#endif
385
388
389 Isolate* saved_current_isolate_ = nullptr;
390
393
394 std::unique_ptr<LocalHandles> handles_;
395 std::unique_ptr<PersistentHandles> persistent_handles_;
396 std::unique_ptr<MarkingBarrier> marking_barrier_;
397
399
401
402 MarkingBarrier* saved_marking_barrier_ = nullptr;
403
404 // Stack information for the thread using this local heap.
406
407 friend class CollectionBarrier;
408 friend class GlobalSafepoint;
409 friend class Heap;
410 friend class Isolate;
411 friend class IsolateSafepoint;
413 friend class ParkedScope;
414 friend class UnparkedScope;
415};
416
417} // namespace internal
418} // namespace v8
419
420#endif // V8_HEAP_LOCAL_HEAP_H_
Builtins::Kind kind
Definition builtins.cc:40
bool CompareExchangeStrong(ThreadState &expected, ThreadState updated)
Definition local-heap.h:292
bool CompareExchangeWeak(ThreadState &expected, ThreadState updated)
Definition local-heap.h:297
constexpr AtomicThreadState(ThreadState state)
Definition local-heap.h:289
constexpr bool IsRunning() const
Definition local-heap.h:252
constexpr bool IsCollectionRequested() const
Definition local-heap.h:268
constexpr ThreadState SetRunning() const V8_WARN_UNUSED_RESULT
Definition local-heap.h:254
static constexpr ThreadState Parked()
Definition local-heap.h:247
constexpr bool IsParked() const
Definition local-heap.h:258
constexpr bool IsRunningWithSlowPathFlag() const
Definition local-heap.h:272
constexpr ThreadState(uint8_t value)
Definition local-heap.h:278
constexpr ThreadState SetParked() const V8_WARN_UNUSED_RESULT
Definition local-heap.h:260
constexpr bool IsSafepointRequested() const
Definition local-heap.h:264
static constexpr ThreadState Running()
Definition local-heap.h:250
constexpr uint8_t raw() const
Definition local-heap.h:280
LocalHandles * handles()
Definition local-heap.h:70
bool is_main_thread() const
Definition local-heap.h:194
V8_NO_UNIQUE_ADDRESS PtrComprCageAccessScope ptr_compr_cage_access_scope_
Definition local-heap.h:377
void(void *) GCEpilogueCallback
Definition local-heap.h:52
Heap * heap() const
Definition local-heap.h:122
void SetRetryOfFailedAllocation(bool value)
Definition local-heap.h:120
IndirectHandle< T > NewPersistentHandle(HandleType< T > object)
Definition local-heap.h:81
::heap::base::Stack stack_
Definition local-heap.h:405
AtomicThreadState state_
Definition local-heap.h:380
MaybeIndirectHandle< T > NewPersistentMaybeHandle(MaybeHandleType< T > maybe_handle)
Definition local-heap.h:94
bool IsRetryOfFailedAllocation() const
Definition local-heap.h:118
bool is_main_thread_for(Heap *heap) const
Definition local-heap.h:195
std::unique_ptr< MarkingBarrier > marking_barrier_
Definition local-heap.h:396
IndirectHandle< T > NewPersistentHandle(T object)
Definition local-heap.h:88
ReadOnlySpace * read_only_space()
Definition local-heap.h:203
HeapAllocator heap_allocator_
Definition local-heap.h:400
MarkingBarrier * marking_barrier()
Definition local-heap.h:130
Heap * AsHeap() const
Definition local-heap.h:123
HeapAllocator * allocator()
Definition local-heap.h:238
GCCallbacksInSafepoint gc_epilogue_callbacks_
Definition local-heap.h:398
IndirectHandle< T > NewPersistentHandle(Tagged< T > object)
Definition local-heap.h:73
std::unique_ptr< LocalHandles > handles_
Definition local-heap.h:394
bool deserialization_complete() const
Definition local-heap.h:200
std::unique_ptr< PersistentHandles > persistent_handles_
Definition local-heap.h:395
enum v8::internal::@1270::DeoptimizableCodeIterator::@67 state_
#define ROOT_ACCESSOR(Type, name, CamelName)
TNode< Object > callback
V8_INLINE IndirectHandle< T > handle(Tagged< T > object, Isolate *isolate)
Definition handles-inl.h:72
ClearRecordedSlots
Definition heap.h:137
constexpr NullMaybeHandleType kNullMaybeHandle
thread_local Isolate::PerIsolateThreadData *g_current_per_isolate_thread_data_ V8_CONSTINIT
Definition isolate.cc:522
__attribute__((tls_model(V8_TLS_MODEL))) extern thread_local Isolate *g_current_isolate_ V8_CONSTINIT
#define MUTABLE_ROOT_LIST(V)
Definition roots.h:483
#define DCHECK(condition)
Definition logging.h:482
#define V8_EXPORT_PRIVATE
Definition macros.h:460
#define V8_TLS_MODEL
#define V8_TLS_DECLARE_GETTER(Name, Type, Member)
Heap * heap_
#define V8_NO_UNIQUE_ADDRESS
Definition v8config.h:722
#define V8_INLINE
Definition v8config.h:500
#define V8_WARN_UNUSED_RESULT
Definition v8config.h:671
#define V8_UNLIKELY(condition)
Definition v8config.h:660
std::unique_ptr< ValueMirror > value