804aaa5c69
This is a reland of 142dd775b4
Original change's description:
> cppgc-js,heap: Implement snapshots for embedder fields
>
> https://crrev.com/c/3293410 added concurrent processing of C++ objects
> found through V8 embedder fields. The CL missed that those embedder
> fields are not read atomically from JS objects. The problem is that
> embedder fields are only aligned to kTaggedSize on builds with pointer
> compression and are as such mis-aligned for atomic ops. This is not a
> problem for on-heap values as the upper 32bits are anyways computed
> from the cage. Is is a problem for generic C++ values though, as they
> are used with Oilpan.
>
> This CL adds the standard marker snapshot protocol for embedder fields.
>
> Marker:
> 1. Snapshot embedder fields
> 2. Try to mark host object
> 3. On success: process snapshot
>
> Main thread:
> 1. On setting embedder fields mark the object black first
> 2. Emit a write barrier for the embedder fields
>
> This will get simpler with the heap sandbox that uses a separate table
> for embedder fields. Once the sandbox is the default configuration, we
> can use it as dependency for the concurrent fast path.
>
> Bug: chromium:1285706
> Change-Id: I6b975ea561be08cda840ef0dd27a11627de93900
> Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3380983
> Reviewed-by: Dominik Inführ <dinfuehr@chromium.org>
> Commit-Queue: Michael Lippautz <mlippautz@chromium.org>
> Cr-Commit-Position: refs/heads/main@{#78604}
Bug: chromium:1285706
Change-Id: I024e50fc0757fbcd13cb9ffde027dff55f99d25c
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3386600
Reviewed-by: Dominik Inführ <dinfuehr@chromium.org>
Reviewed-by: Omer Katz <omerkatz@chromium.org>
Commit-Queue: Michael Lippautz <mlippautz@chromium.org>
Cr-Commit-Position: refs/heads/main@{#78631}
214 lines
8.3 KiB
C++
214 lines
8.3 KiB
C++
// Copyright 2019 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_MARKING_WORKLIST_H_
|
|
#define V8_HEAP_MARKING_WORKLIST_H_
|
|
|
|
#include <cstddef>
|
|
#include <memory>
|
|
#include <unordered_map>
|
|
#include <vector>
|
|
|
|
#include "src/heap/base/worklist.h"
|
|
#include "src/heap/cppgc-js/cpp-marking-state.h"
|
|
#include "src/heap/marking.h"
|
|
#include "src/objects/heap-object.h"
|
|
|
|
namespace v8 {
|
|
namespace internal {
|
|
|
|
class CppMarkingState;
|
|
class JSObject;
|
|
|
|
// The index of the main thread task used by concurrent/parallel GC.
|
|
const int kMainThreadTask = 0;
|
|
|
|
using MarkingWorklist = ::heap::base::Worklist<HeapObject, 64>;
|
|
using WrapperTracingWorklist = ::heap::base::Worklist<HeapObject, 16>;
|
|
|
|
// We piggyback on marking to compute object sizes per native context that is
|
|
// needed for the new memory measurement API. The algorithm works as follows:
|
|
// 1) At the start of marking we create a marking worklist for each context.
|
|
// The existing shared, on_hold, and embedder worklists continue to work
|
|
// as they did before, but they hold objects that are not attributed to any
|
|
// context yet.
|
|
// 2) Each marker has an active worklist where it pushes newly discovered
|
|
// objects. Initially the shared worklist is set as active for all markers.
|
|
// 3) When a marker pops an object from the active worklist:
|
|
// a) It checks if the object has a known context (e.g. JSObjects, Maps,
|
|
// Contexts know the context they belong to). If that's the case, then
|
|
// the marker changes its active worklist to the worklist corresponding
|
|
// to the context of the object.
|
|
// b) It account the size of object to the active context.
|
|
// c) It visits all pointers in the object and pushes new objects onto the
|
|
// active worklist.
|
|
// 4) When the active worklist becomes empty the marker selects any other
|
|
// non-empty worklist as the active worklist.
|
|
// 5) The write barrier pushes onto the shared worklist.
|
|
//
|
|
// The main invariant for context worklists:
|
|
// If object X is in the worklist of context C, then either
|
|
// a) X has a context and that context is C.
|
|
// b) X is retained by object Y that has context C.
|
|
//
|
|
// The algorithm allows us to attribute context-independent objects such as
|
|
// strings, numbers, FixedArrays to their retaining contexts. The algorithm is
|
|
// not precise for context-independent objects that are shared between multiple
|
|
// contexts. Such objects may be attributed to any retaining context.
|
|
|
|
// Named pair of native context address and its marking worklist.
|
|
// Since native contexts are allocated in the old generation, their addresses
|
|
// a stable across Scavenges and stay valid throughout the marking phase.
|
|
struct ContextWorklistPair {
|
|
Address context;
|
|
MarkingWorklist* worklist;
|
|
};
|
|
|
|
// A helper class that owns all global marking worklists.
|
|
class V8_EXPORT_PRIVATE MarkingWorklists {
|
|
public:
|
|
class Local;
|
|
// Fake addresses of special contexts used for per-context accounting.
|
|
// - kSharedContext is for objects that are not attributed to any context.
|
|
// - kOtherContext is for objects that are attributed to contexts that are
|
|
// not being measured.
|
|
static const Address kSharedContext = 0;
|
|
static const Address kOtherContext = 8;
|
|
|
|
MarkingWorklists() = default;
|
|
~MarkingWorklists();
|
|
|
|
// Calls the specified callback on each element of the deques and replaces
|
|
// the element with the result of the callback. If the callback returns
|
|
// nullptr then the element is removed from the deque.
|
|
// The callback must accept HeapObject and return HeapObject.
|
|
template <typename Callback>
|
|
void Update(Callback callback);
|
|
|
|
MarkingWorklist* shared() { return &shared_; }
|
|
MarkingWorklist* on_hold() { return &on_hold_; }
|
|
WrapperTracingWorklist* wrapper() { return &wrapper_; }
|
|
|
|
// A list of (context, worklist) pairs that was set up at the start of
|
|
// marking by CreateContextWorklists.
|
|
const std::vector<ContextWorklistPair>& context_worklists() const {
|
|
return context_worklists_;
|
|
}
|
|
// This should be invoked at the start of marking with the list of contexts
|
|
// that require object size accounting.
|
|
void CreateContextWorklists(const std::vector<Address>& contexts);
|
|
// This should be invoked at the end of marking. All worklists must be
|
|
// empty at that point.
|
|
void ReleaseContextWorklists();
|
|
|
|
void Clear();
|
|
void Print();
|
|
|
|
private:
|
|
// Prints the stats about the global pool of the worklist.
|
|
void PrintWorklist(const char* worklist_name, MarkingWorklist* worklist);
|
|
|
|
// Worklist used for most objects.
|
|
MarkingWorklist shared_;
|
|
|
|
// Concurrent marking uses this worklist to bail out of marking objects
|
|
// in new space's linear allocation area. Used to avoid black allocation
|
|
// for new space. This allow the compiler to remove write barriers
|
|
// for freshly allocatd objects.
|
|
MarkingWorklist on_hold_;
|
|
|
|
// Worklist for objects that potentially require embedder tracing, i.e.,
|
|
// these objects need to be handed over to the embedder to find the full
|
|
// transitive closure.
|
|
WrapperTracingWorklist wrapper_;
|
|
|
|
// Per-context worklists.
|
|
std::vector<ContextWorklistPair> context_worklists_;
|
|
// This is used only for lifetime management of the per-context worklists.
|
|
std::vector<std::unique_ptr<MarkingWorklist>> worklists_;
|
|
|
|
// Worklist used for objects that are attributed to contexts that are
|
|
// not being measured.
|
|
MarkingWorklist other_;
|
|
};
|
|
|
|
// A thread-local view of the marking worklists. It owns all local marking
|
|
// worklists and keeps track of the currently active local marking worklist
|
|
// for per-context marking. In order to avoid additional indirections for
|
|
// pushing and popping entries, the active_ worklist is not a pointer to
|
|
// Local but an actual instance of Local with the following invariants:
|
|
// - active_owner == worlist_by_context[active_context_].get()
|
|
// - *active_owner is empty (all fields are null) because its content has
|
|
// been moved to active_.
|
|
class V8_EXPORT_PRIVATE MarkingWorklists::Local {
|
|
public:
|
|
static constexpr Address kSharedContext = MarkingWorklists::kSharedContext;
|
|
static constexpr Address kOtherContext = MarkingWorklists::kOtherContext;
|
|
static constexpr std::nullptr_t kNoCppMarkingState = nullptr;
|
|
|
|
Local(
|
|
MarkingWorklists* global,
|
|
std::unique_ptr<CppMarkingState> cpp_marking_state = kNoCppMarkingState);
|
|
~Local();
|
|
|
|
inline void Push(HeapObject object);
|
|
inline bool Pop(HeapObject* object);
|
|
|
|
inline void PushOnHold(HeapObject object);
|
|
inline bool PopOnHold(HeapObject* object);
|
|
|
|
using WrapperSnapshot = CppMarkingState::EmbedderDataSnapshot;
|
|
inline bool ExtractWrapper(Map map, JSObject object,
|
|
WrapperSnapshot& snapshot);
|
|
inline void PushExtractedWrapper(const WrapperSnapshot& snapshot);
|
|
inline bool SupportsExtractWrapper();
|
|
inline void PushWrapper(HeapObject object);
|
|
inline bool PopWrapper(HeapObject* object);
|
|
|
|
void Publish();
|
|
bool IsEmpty();
|
|
bool IsWrapperEmpty() const;
|
|
// Publishes the local active marking worklist if its global worklist is
|
|
// empty. In the per-context marking mode it also publishes the shared
|
|
// worklist.
|
|
void ShareWork();
|
|
// Merges the on-hold worklist to the shared worklist.
|
|
void MergeOnHold();
|
|
|
|
// Returns true if wrapper objects could be directly pushed. Otherwise,
|
|
// objects need to be processed one by one.
|
|
inline bool PublishWrapper();
|
|
|
|
// Returns the context of the active worklist.
|
|
Address Context() const { return active_context_; }
|
|
inline Address SwitchToContext(Address context);
|
|
inline Address SwitchToShared();
|
|
bool IsPerContextMode() const { return is_per_context_mode_; }
|
|
|
|
CppMarkingState* cpp_marking_state() const {
|
|
return cpp_marking_state_.get();
|
|
}
|
|
|
|
private:
|
|
bool PopContext(HeapObject* object);
|
|
Address SwitchToContextSlow(Address context);
|
|
inline void SwitchToContext(Address context,
|
|
MarkingWorklist::Local* worklist);
|
|
MarkingWorklist::Local on_hold_;
|
|
WrapperTracingWorklist::Local wrapper_;
|
|
MarkingWorklist::Local active_;
|
|
Address active_context_;
|
|
MarkingWorklist::Local* active_owner_;
|
|
bool is_per_context_mode_;
|
|
std::unordered_map<Address, std::unique_ptr<MarkingWorklist::Local>>
|
|
worklist_by_context_;
|
|
|
|
std::unique_ptr<CppMarkingState> cpp_marking_state_;
|
|
};
|
|
|
|
} // namespace internal
|
|
} // namespace v8
|
|
|
|
#endif // V8_HEAP_MARKING_WORKLIST_H_
|