heap: Uniform object construction

Switch to a uniform object construction that does not differentiate between
mixins and regular garabge collected objects and as a consequence can interrupt
both construction cases with garbage collections.

Object construction now uniformely works as follows:
1. The object under construction is marked so in the HeapObjectHeader.
2. Upon discovering such an object it is delayed in a separate marking worklist.
3. Upon hitting the main atomic pause all such objects are conservatively
   scanned for pointers without using the Trace method.

Special cases:
a. Mixins in construction: The HeapObjectHeader cannot retrived for such
   objects. A default GetTraceDescriptor() implementation returning a sentinel
   marker is used to discover that such objects should be delayed.
b. Upon reaching a safepoint (e.g. no stack), in-construction objects are moved
   to a marking worklist that, similar to the regular marking worklist, allows for
   incremental processing.

Effects:
- No more TLS access for no-GC scope on mixin construction.
- No more memory needed for the no-GC scope marker in mixin classes
- MakeGarbageCollected should require less binary size as implementations can be
  aligned.
- Incremental marking Start and Step operations are safe to be called with stack
  (even though it is preferable to not do so).
- Object construction is safe to be used with a concurrent marker as it is ok
  for any objects (mixins as well as normal ones) to be published to the object
  graph during constructors.

Change-Id: Ia39b5e76d551ed194612091167e2370b2d6a4e5f
Bug: 911662
Reviewed-on: https://chromium-review.googlesource.com/c/1401070
Commit-Queue: Michael Lippautz <mlippautz@chromium.org>
Reviewed-by: Kentaro Hara <haraken@chromium.org>
Cr-Commit-Position: refs/heads/master@{#621637}
diff --git a/third_party/blink/renderer/core/dom/live_node_list_registry.cc b/third_party/blink/renderer/core/dom/live_node_list_registry.cc
index 7250fafc..1ca504d3 100644
--- a/third_party/blink/renderer/core/dom/live_node_list_registry.cc
+++ b/third_party/blink/renderer/core/dom/live_node_list_registry.cc
@@ -44,7 +44,7 @@
 
 void LiveNodeListRegistry::ClearWeakMembers(Visitor*) {
   auto* it = std::remove_if(data_.begin(), data_.end(), [](Entry entry) {
-    return !ObjectAliveTrait<LiveNodeListBase>::IsHeapObjectAlive(entry.first);
+    return !ThreadHeap::IsHeapObjectAlive(entry.first);
   });
   if (it == data_.end())
     return;
diff --git a/third_party/blink/renderer/core/dom/shadow_root.cc b/third_party/blink/renderer/core/dom/shadow_root.cc
index 04bd6dd..7e88116 100644
--- a/third_party/blink/renderer/core/dom/shadow_root.cc
+++ b/third_party/blink/renderer/core/dom/shadow_root.cc
@@ -56,7 +56,6 @@
 }
 
 struct SameSizeAsShadowRoot : public DocumentFragment, public TreeScope {
-  char empty_class_fields_due_to_gc_mixin_marker[1];
   Member<void*> member[3];
   unsigned counters_and_flags[1];
 };
diff --git a/third_party/blink/renderer/modules/webmidi/midi_dispatcher.h b/third_party/blink/renderer/modules/webmidi/midi_dispatcher.h
index f40a3cc4..30be342 100644
--- a/third_party/blink/renderer/modules/webmidi/midi_dispatcher.h
+++ b/third_party/blink/renderer/modules/webmidi/midi_dispatcher.h
@@ -18,6 +18,7 @@
                        public midi::mojom::blink::MidiSessionClient {
  public:
   static MIDIDispatcher& Instance();
+  MIDIDispatcher();
   ~MIDIDispatcher() override;
 
   void Trace(Visitor* visitor);
@@ -44,10 +45,6 @@
                     base::TimeTicks timestamp) override;
 
  private:
-  friend class ConstructTrait<MIDIDispatcher>;
-
-  MIDIDispatcher();
-
   midi::mojom::blink::MidiSessionProvider& GetMidiSessionProvider();
   midi::mojom::blink::MidiSession& GetMidiSession();
 
diff --git a/third_party/blink/renderer/platform/heap/garbage_collected.h b/third_party/blink/renderer/platform/heap/garbage_collected.h
index 7326e43..6bb10aa 100644
--- a/third_party/blink/renderer/platform/heap/garbage_collected.h
+++ b/third_party/blink/renderer/platform/heap/garbage_collected.h
@@ -47,12 +47,17 @@
   static const bool value = sizeof(CheckMarker<T>(nullptr)) == sizeof(YesType);
 };
 
+// TraceDescriptor is used to describe how to trace an object.
 struct TraceDescriptor {
   STACK_ALLOCATED();
 
  public:
+  // The adjusted base pointer of the object that should be traced.
   void* base_object_payload;
+  // A callback for tracing the object.
   TraceCallback callback;
+  // Indicator whether this object can be traced recursively or whether it
+  // requires iterative tracing.
   bool can_trace_eagerly;
 };
 
@@ -72,11 +77,14 @@
 //   USING_GARBAGE_COLLECTED_MIXIN(A);
 // };
 //
-// With the helper, as long as we are using Member<B>, TypeTrait<B> will
-// dispatch dynamically to retrieve the necessary tracing and header methods.
-// Note that this is only enabled for Member<B>. For Member<A> which we can
-// compute the necessary methods and pointers statically and this dynamic
-// dispatch is not used.
+// The classes involved and the helper macros allow for properly handling
+// definitions for Member<B> and friends. The mechanisms for handling Member<B>
+// involve dynamic dispatch. Note that for Member<A> all methods and pointers
+// are statically computed and no dynamic dispatch is involved.
+//
+// Note that garbage collections are allowed during mixin construction as
+// conservative scanning of objects does not rely on the Trace method but rather
+// scans the object field by field.
 class PLATFORM_EXPORT GarbageCollectedMixin {
  public:
   typedef int IsGarbageCollectedMixinMarker;
@@ -86,7 +94,8 @@
   // these objects can processed later on. This is necessary as
   // not-fully-constructed mixin objects potentially require being processed
   // as part emitting a write barrier for incremental marking. See
-  // IncrementalMarkingTest::WriteBarrierDuringMixinConstruction as an example.
+  // |IncrementalMarkingTest::WriteBarrierDuringMixinConstruction| as an
+  // example.
   //
   // The not-fully-constructed objects are handled as follows:
   //   1. Write barrier or marking of not fully constructed mixin gets called.
@@ -127,95 +136,34 @@
                                                                              \
  private:
 
-// A C++ object's vptr will be initialized to its leftmost base's vtable after
-// the constructors of all its subclasses have run, so if a subclass constructor
-// tries to access any of the vtbl entries of its leftmost base prematurely,
-// it'll find an as-yet incorrect vptr and fail. Which is exactly what a
-// garbage collector will try to do if it tries to access the leftmost base
-// while one of the subclass constructors of a GC mixin object triggers a GC.
-// It is consequently not safe to allow any GCs while these objects are under
-// (sub constructor) construction.
-//
-// To prevent GCs in that restricted window of a mixin object's construction:
-//
-//  - The initial allocation of the mixin object will enter a no GC scope.
-//    This is done by overriding 'operator new' for mixin instances.
-//  - When the constructor for the mixin is invoked, after all the
-//    derived constructors have run, it will invoke the constructor
-//    for a field whose only purpose is to leave the GC scope.
-//    GarbageCollectedMixinConstructorMarker's constructor takes care of
-//    this and the field is declared by way of USING_GARBAGE_COLLECTED_MIXIN().
-
-#define DEFINE_GARBAGE_COLLECTED_MIXIN_CONSTRUCTOR_MARKER(TYPE)           \
- public:                                                                  \
-  GarbageCollectedMixinConstructorMarker<ThreadingTrait<TYPE>::kAffinity> \
-      mixin_constructor_marker_;                                          \
-                                                                          \
+// The Oilpan GC plugin checks for proper usages of the
+// USING_GARBAGE_COLLECTED_MIXIN macro using a typedef marker.
+#define DEFINE_GARBAGE_COLLECTED_MIXIN_CONSTRUCTOR_MARKER(TYPE) \
+ public:                                                        \
+  typedef int HasUsingGarbageCollectedMixinMacro;               \
+                                                                \
  private:
 
-// Mixins that wrap/nest others requires extra handling:
-//
-//  class A : public GarbageCollected<A>, public GarbageCollectedMixin {
-//  USING_GARBAGE_COLLECTED_MIXIN(A);
-//  ...
-//  }'
-//  public B final : public A, public SomeOtherMixinInterface {
-//  USING_GARBAGE_COLLECTED_MIXIN(B);
-//  ...
-//  };
-//
-// The "operator new" for B will enter the forbidden GC scope, but
-// upon construction, two GarbageCollectedMixinConstructorMarker constructors
-// will run -- one for A (first) and another for B (secondly). Only
-// the second one should leave the forbidden GC scope. This is realized by
-// recording the address of B's GarbageCollectedMixinConstructorMarker
-// when the "operator new" for B runs, and leaving the forbidden GC scope
-// when the constructor of the recorded GarbageCollectedMixinConstructorMarker
-// runs.
+// The USING_GARBAGE_COLLECTED_MIXIN macro defines all methods and markers
+// needed for handling mixins.
 #define USING_GARBAGE_COLLECTED_MIXIN(TYPE)    \
   IS_GARBAGE_COLLECTED_TYPE();                 \
   DEFINE_GARBAGE_COLLECTED_MIXIN_METHODS(TYPE) \
   DEFINE_GARBAGE_COLLECTED_MIXIN_CONSTRUCTOR_MARKER(TYPE)
 
-// An empty class with a constructor that's arranged invoked when all derived
-// constructors of a mixin instance have completed and it is safe to allow GCs
-// again. See AllocateObjectTrait<> comment for more.
-//
-// USING_GARBAGE_COLLECTED_MIXIN() declares a
-// GarbageCollectedMixinConstructorMarker<> private field. By following Blink
-// convention of using the macro at the top of a class declaration, its
-// constructor will run first.
-class GarbageCollectedMixinConstructorMarkerBase {};
-template <ThreadAffinity affinity>
-class GarbageCollectedMixinConstructorMarker
-    : public GarbageCollectedMixinConstructorMarkerBase {
- public:
-  GarbageCollectedMixinConstructorMarker() {
-    // FIXME: if prompt conservative GCs are needed, forced GCs that
-    // were denied while within this scope, could now be performed.
-    // For now, assume the next out-of-line allocation request will
-    // happen soon enough and take care of it. Mixin objects aren't
-    // overly common.
-    ThreadState* state = ThreadStateFor<affinity>::GetState();
-    state->LeaveGCForbiddenScopeIfNeeded(this);
-  }
-};
-
 // Merge two or more Mixins into one:
 //
 //  class A : public GarbageCollectedMixin {};
 //  class B : public GarbageCollectedMixin {};
 //  class C : public A, public B {
 //    // C::GetTraceDescriptor is now ambiguous because there are two
-//    candidates:
-//    // A::GetTraceDescriptor and B::GetTraceDescriptor.  Ditto for other
-//    functions.
+//    // candidates: A::GetTraceDescriptor and B::GetTraceDescriptor.  Ditto for
+//    // other functions.
 //
 //    MERGE_GARBAGE_COLLECTED_MIXINS();
 //    // The macro defines C::GetTraceDescriptor, etc. so that they are no
-//    longer
-//    // ambiguous. USING_GARBAGE_COLLECTED_MIXIN(TYPE) overrides them later
-//    // and provides the implementations.
+//    // longer ambiguous. USING_GARBAGE_COLLECTED_MIXIN(TYPE) overrides them
+//    // later and provides the implementations.
 //  };
 #define MERGE_GARBAGE_COLLECTED_MIXINS()                                 \
  public:                                                                 \
diff --git a/third_party/blink/renderer/platform/heap/heap.cc b/third_party/blink/renderer/platform/heap/heap.cc
index 5dddc0e..cf507ab 100644
--- a/third_party/blink/renderer/platform/heap/heap.cc
+++ b/third_party/blink/renderer/platform/heap/heap.cc
@@ -165,10 +165,31 @@
 
 void ThreadHeap::DecommitCallbackStacks() {
   marking_worklist_.reset(nullptr);
-  not_fully_constructed_worklist_.reset(nullptr);
   previously_not_fully_constructed_worklist_.reset(nullptr);
   weak_callback_worklist_.reset(nullptr);
   ephemeron_callbacks_.clear();
+
+  // The fixed point iteration may have found not-fully-constructed objects.
+  // Such objects should have already been found through the stack scan though
+  // and should thus already be marked.
+  if (!not_fully_constructed_worklist_->IsGlobalEmpty()) {
+#if DCHECK_IS_ON()
+    NotFullyConstructedItem item;
+    while (not_fully_constructed_worklist_->Pop(WorklistTaskId::MainThread,
+                                                &item)) {
+      BasePage* const page = PageFromObject(item);
+      HeapObjectHeader* const header =
+          page->IsLargeObjectPage()
+              ? static_cast<LargeObjectPage*>(page)->ObjectHeader()
+              : static_cast<NormalPage*>(page)->FindHeaderFromAddress(
+                    reinterpret_cast<Address>(const_cast<void*>(item)));
+      DCHECK(header->IsMarked());
+    }
+#else
+    not_fully_constructed_worklist_->Clear();
+#endif
+  }
+  not_fully_constructed_worklist_.reset(nullptr);
 }
 
 HeapCompact* ThreadHeap::Compaction() {
@@ -260,8 +281,6 @@
 }  // namespace
 
 bool ThreadHeap::AdvanceMarking(MarkingVisitor* visitor, TimeTicks deadline) {
-  // const size_t kDeadlineCheckInterval = 2500;
-  // size_t processed_callback_count = 0;
   bool finished;
   // Ephemeron fixed point loop.
   do {
@@ -271,11 +290,13 @@
       ThreadHeapStatsCollector::Scope stats_scope(
           stats_collector(), ThreadHeapStatsCollector::kMarkProcessWorklist);
 
-      finished =
-          DrainWorklistWithDeadline(deadline, marking_worklist_.get(),
-                                    [visitor](const MarkingItem& item) {
-                                      item.callback(visitor, item.object);
-                                    });
+      finished = DrainWorklistWithDeadline(
+          deadline, marking_worklist_.get(),
+          [visitor](const MarkingItem& item) {
+            DCHECK(!HeapObjectHeader::FromPayload(item.object)
+                        ->IsInConstruction());
+            item.callback(visitor, item.object);
+          });
       if (!finished)
         return false;
 
@@ -600,6 +621,12 @@
   if (header->IsMarked())
     return;
 
+  if (header->IsInConstruction()) {
+    not_fully_constructed_worklist_->Push(WorklistTaskId::MainThread,
+                                          header->Payload());
+    return;
+  }
+
   // Mark and push trace callback.
   header->Mark();
   marking_worklist_->Push(
diff --git a/third_party/blink/renderer/platform/heap/heap.h b/third_party/blink/renderer/platform/heap/heap.h
index 6919e657..0f81c2b 100644
--- a/third_party/blink/renderer/platform/heap/heap.h
+++ b/third_party/blink/renderer/platform/heap/heap.h
@@ -121,6 +121,8 @@
 template <typename T>
 class UntracedMember;
 
+namespace internal {
+
 template <typename T, bool = NeedsAdjustPointer<T>::value>
 class ObjectAliveTrait;
 
@@ -143,10 +145,17 @@
   NO_SANITIZE_ADDRESS
   static bool IsHeapObjectAlive(const T* object) {
     static_assert(sizeof(T), "T must be fully defined");
-    return object->GetHeapObjectHeader()->IsMarked();
+    const HeapObjectHeader* header = object->GetHeapObjectHeader();
+    if (header == BlinkGC::kNotFullyConstructedObject) {
+      // Objects under construction are always alive.
+      return true;
+    }
+    return header->IsMarked();
   }
 };
 
+}  // namespace internal
+
 class PLATFORM_EXPORT ThreadHeap {
  public:
   explicit ThreadHeap(ThreadState*);
@@ -173,7 +182,7 @@
       return true;
     DCHECK(&ThreadState::Current()->Heap() ==
            &PageFromObject(object)->Arena()->GetThreadState()->Heap());
-    return ObjectAliveTrait<T>::IsHeapObjectAlive(object);
+    return internal::ObjectAliveTrait<T>::IsHeapObjectAlive(object);
   }
   template <typename T>
   static inline bool IsHeapObjectAlive(const Member<T>& member) {
@@ -546,53 +555,18 @@
   DISALLOW_COPY_AND_ASSIGN(GarbageCollected);
 };
 
-template <typename T, bool is_mixin = IsGarbageCollectedMixin<T>::value>
-class ConstructTrait {
- public:
-};
-
-template <typename T>
-class ConstructTrait<T, false> {
- public:
-  template <typename... Args>
-  static T* Construct(Args&&... args) {
-    void* memory =
-        T::AllocateObject(sizeof(T), IsEagerlyFinalizedType<T>::value);
-    HeapObjectHeader* header = HeapObjectHeader::FromPayload(memory);
-    header->MarkIsInConstruction();
-    // Placement new as regular operator new() is deleted.
-    T* object = ::new (memory) T(std::forward<Args>(args)...);
-    header->UnmarkIsInConstruction();
-    return object;
-  }
-};
-
-template <typename T>
-class ConstructTrait<T, true> {
- public:
-  template <typename... Args>
-  NO_SANITIZE_UNRELATED_CAST static T* Construct(Args&&... args) {
-    void* memory =
-        T::AllocateObject(sizeof(T), IsEagerlyFinalizedType<T>::value);
-    HeapObjectHeader* header = HeapObjectHeader::FromPayload(memory);
-    header->MarkIsInConstruction();
-    ThreadState* state =
-        ThreadStateFor<ThreadingTrait<T>::kAffinity>::GetState();
-    state->EnterGCForbiddenScopeIfNeeded(
-        &(reinterpret_cast<T*>(memory)->mixin_constructor_marker_));
-    // Placement new as regular operator new() is deleted.
-    T* object = ::new (memory) T(std::forward<Args>(args)...);
-    header->UnmarkIsInConstruction();
-    return object;
-  }
-};
-
 // Constructs an instance of T, which is a garbage collected type.
 template <typename T, typename... Args>
 T* MakeGarbageCollected(Args&&... args) {
   static_assert(WTF::IsGarbageCollectedType<T>::value,
                 "T needs to be a garbage collected object");
-  return ConstructTrait<T>::Construct(std::forward<Args>(args)...);
+  void* memory = T::AllocateObject(sizeof(T), IsEagerlyFinalizedType<T>::value);
+  HeapObjectHeader* header = HeapObjectHeader::FromPayload(memory);
+  header->MarkIsInConstruction();
+  // Placement new as regular operator new() is deleted.
+  T* object = ::new (memory) T(std::forward<Args>(args)...);
+  header->UnmarkIsInConstruction();
+  return object;
 }
 
 // Assigning class types to their arenas.
@@ -753,7 +727,7 @@
       // preserved to avoid reviving objects in containers.
       return;
     }
-    if (!ObjectAliveTrait<T>::IsHeapObjectAlive(contents))
+    if (!ThreadHeap::IsHeapObjectAlive(contents))
       *cell = nullptr;
   }
 }
diff --git a/third_party/blink/renderer/platform/heap/heap_test.cc b/third_party/blink/renderer/platform/heap/heap_test.cc
index 4b1c81e..c042269 100644
--- a/third_party/blink/renderer/platform/heap/heap_test.cc
+++ b/third_party/blink/renderer/platform/heap/heap_test.cc
@@ -5367,59 +5367,6 @@
   return true;
 }
 
-static bool CheckGCForbidden() {
-  DCHECK(ThreadState::Current()->IsGCForbidden());
-  return true;
-}
-
-class MixinClass : public GarbageCollectedMixin {
- public:
-  MixinClass() : dummy_(CheckGCForbidden()) {}
-
- private:
-  bool dummy_;
-};
-
-class ClassWithGarbageCollectingMixinConstructor
-    : public GarbageCollected<ClassWithGarbageCollectingMixinConstructor>,
-      public MixinClass {
-  USING_GARBAGE_COLLECTED_MIXIN(ClassWithGarbageCollectingMixinConstructor);
-
- public:
-  static int trace_called_;
-
-  ClassWithGarbageCollectingMixinConstructor()
-      : trace_counter_(TraceCounter::Create()),
-        wrapper_(IntWrapper::Create(32)) {}
-
-  void Trace(blink::Visitor* visitor) override {
-    trace_called_++;
-    visitor->Trace(trace_counter_);
-    visitor->Trace(wrapper_);
-  }
-
-  void Verify() {
-    EXPECT_EQ(32, wrapper_->Value());
-    EXPECT_EQ(0, trace_counter_->TraceCount());
-    EXPECT_EQ(0, trace_called_);
-  }
-
- private:
-  Member<TraceCounter> trace_counter_;
-  Member<IntWrapper> wrapper_;
-};
-
-int ClassWithGarbageCollectingMixinConstructor::trace_called_ = 0;
-
-// Regression test for out of bounds call through vtable.
-// Passes if it doesn't crash.
-TEST(HeapTest, GarbageCollectionDuringMixinConstruction) {
-  ClassWithGarbageCollectingMixinConstructor::trace_called_ = 0;
-  ClassWithGarbageCollectingMixinConstructor* a =
-      MakeGarbageCollected<ClassWithGarbageCollectingMixinConstructor>();
-  a->Verify();
-}
-
 template <typename T>
 class TraceIfNeededTester
     : public GarbageCollectedFinalized<TraceIfNeededTester<T>> {
@@ -5928,11 +5875,7 @@
   USING_GARBAGE_COLLECTED_MIXIN(TestMixinAllocationA);
 
  public:
-  TestMixinAllocationA() {
-    // Completely wrong in general, but test only
-    // runs this constructor while constructing another mixin.
-    DCHECK(ThreadState::Current()->IsGCForbidden());
-  }
+  TestMixinAllocationA() = default;
 
   void Trace(blink::Visitor* visitor) override {}
 };
@@ -5942,14 +5885,8 @@
 
  public:
   TestMixinAllocationB()
-      : a_(MakeGarbageCollected<TestMixinAllocationA>())  // Construct object
-                                                          // during a mixin
-                                                          // construction.
-  {
-    // Completely wrong in general, but test only
-    // runs this constructor while constructing another mixin.
-    DCHECK(ThreadState::Current()->IsGCForbidden());
-  }
+      // Construct object during a mixin construction.
+      : a_(MakeGarbageCollected<TestMixinAllocationA>()) {}
 
   void Trace(blink::Visitor* visitor) override {
     visitor->Trace(a_);
@@ -5994,47 +5931,6 @@
   }
 };
 
-class TestMixinAllocatingObject final
-    : public TestMixinAllocationB,
-      public ObjectWithLargeAmountsOfAllocationInConstructor {
-  USING_GARBAGE_COLLECTED_MIXIN(TestMixinAllocatingObject);
-
- public:
-  static TestMixinAllocatingObject* Create(ClassWithMember* member) {
-    return MakeGarbageCollected<TestMixinAllocatingObject>(member);
-  }
-
-  TestMixinAllocatingObject(ClassWithMember* member)
-      : ObjectWithLargeAmountsOfAllocationInConstructor(600, member),
-        trace_counter_(TraceCounter::Create()) {
-    DCHECK(!ThreadState::Current()->IsGCForbidden());
-    ConservativelyCollectGarbage();
-    EXPECT_GT(member->TraceCount(), 0);
-    EXPECT_GT(TraceCount(), 0);
-  }
-
-  void Trace(blink::Visitor* visitor) override {
-    visitor->Trace(trace_counter_);
-    TestMixinAllocationB::Trace(visitor);
-  }
-
-  int TraceCount() const { return trace_counter_->TraceCount(); }
-
- private:
-  Member<TraceCounter> trace_counter_;
-};
-
-TEST(HeapTest, MixinConstructionNoGC) {
-  ClearOutOldGarbage();
-  Persistent<ClassWithMember> object = ClassWithMember::Create();
-  EXPECT_EQ(0, object->TraceCount());
-  TestMixinAllocatingObject* mixin =
-      TestMixinAllocatingObject::Create(object.Get());
-  EXPECT_TRUE(mixin);
-  EXPECT_GT(object->TraceCount(), 0);
-  EXPECT_GT(mixin->TraceCount(), 0);
-}
-
 class WeakPersistentHolder final {
  public:
   explicit WeakPersistentHolder(IntWrapper* object) : object_(object) {}
@@ -6420,85 +6316,25 @@
   vector.ShrinkToFit();
 }
 
-namespace {
-
-class MixinCheckingConstructionScope : public GarbageCollectedMixin {
- public:
-  MixinCheckingConstructionScope() {
-    // Oilpan treats mixin construction as forbidden scopes for garbage
-    // collection.
-    CHECK(ThreadState::Current()->IsMixinInConstruction());
-  }
-};
-
-class UsingMixinCheckingConstructionScope
-    : public GarbageCollected<UsingMixinCheckingConstructionScope>,
-      public MixinCheckingConstructionScope {
-  USING_GARBAGE_COLLECTED_MIXIN(UsingMixinCheckingConstructionScope);
-};
-
-}  // namespace
-
-TEST(HeapTest, NoConservativeGCDuringMixinConstruction) {
-  // Regression test: https://crbug.com/904546
-  MakeGarbageCollected<UsingMixinCheckingConstructionScope>();
-}
-
-namespace {
-
-class ObjectCheckingForInConstruction
-    : public GarbageCollected<ObjectCheckingForInConstruction> {
- public:
-  ObjectCheckingForInConstruction() {
-    CHECK(HeapObjectHeader::FromPayload(this)->IsInConstruction());
-  }
-
-  virtual void Trace(Visitor* v) { v->Trace(foo_); }
-
- private:
-  Member<IntWrapper> foo_;
-};
-
-class MixinCheckingInConstruction : public GarbageCollectedMixin {
- public:
-  MixinCheckingInConstruction() {
-    BasePage* const page = PageFromObject(reinterpret_cast<Address>(this));
-    HeapObjectHeader* const header =
-        static_cast<NormalPage*>(page)->FindHeaderFromAddress(
-            reinterpret_cast<Address>(
-                const_cast<MixinCheckingInConstruction*>(this)));
-    CHECK(header->IsInConstruction());
-  }
-
-  void Trace(Visitor* v) override { v->Trace(bar_); }
-
- private:
-  Member<IntWrapper> bar_;
-};
-
-class MixinAppCheckingInConstruction
-    : public GarbageCollected<MixinAppCheckingInConstruction>,
-      public MixinCheckingInConstruction {
-  USING_GARBAGE_COLLECTED_MIXIN(MixinAppCheckingInConstruction)
- public:
-  MixinAppCheckingInConstruction() {
-    CHECK(HeapObjectHeader::FromPayload(this)->IsInConstruction());
-  }
-
-  void Trace(Visitor* v) override { v->Trace(foo_); }
-
- private:
-  Member<IntWrapper> foo_;
-};
-
-}  // namespace
-
 TEST(HeapTest, GarbageCollectedInConstruction) {
-  MakeGarbageCollected<ObjectCheckingForInConstruction>();
+  using O = ObjectWithCallbackBeforeInitializer<IntWrapper>;
+  MakeGarbageCollected<O>(base::BindOnce([](O* thiz) {
+    CHECK(HeapObjectHeader::FromPayload(thiz)->IsInConstruction());
+  }));
 }
 
 TEST(HeapTest, GarbageCollectedMixinInConstruction) {
-  MakeGarbageCollected<MixinAppCheckingInConstruction>();
+  using O = ObjectWithMixinWithCallbackBeforeInitializer<IntWrapper>;
+  MakeGarbageCollected<O>(base::BindOnce([](O::Mixin* thiz) {
+    CHECK(HeapObjectHeader::FromPayload(static_cast<O*>(thiz))
+              ->IsInConstruction());
+  }));
+}
+
+TEST(HeapTest, GarbageCollectedMixinIsAliveDuringConstruction) {
+  using O = ObjectWithMixinWithCallbackBeforeInitializer<IntWrapper>;
+  MakeGarbageCollected<O>(base::BindOnce(
+      [](O::Mixin* thiz) { CHECK(ThreadHeap::IsHeapObjectAlive(thiz)); }));
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/platform/heap/heap_test_utilities.h b/third_party/blink/renderer/platform/heap/heap_test_utilities.h
index 97778731..0e05f426 100644
--- a/third_party/blink/renderer/platform/heap/heap_test_utilities.h
+++ b/third_party/blink/renderer/platform/heap/heap_test_utilities.h
@@ -7,7 +7,12 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_HEAP_TEST_UTILITIES_H_
 #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_HEAP_TEST_UTILITIES_H_
 
+#include "base/callback.h"
 #include "third_party/blink/renderer/platform/heap/blink_gc.h"
+#include "third_party/blink/renderer/platform/heap/garbage_collected.h"
+#include "third_party/blink/renderer/platform/heap/heap.h"
+#include "third_party/blink/renderer/platform/heap/trace_traits.h"
+#include "third_party/blink/renderer/platform/heap/visitor.h"
 
 namespace blink {
 
@@ -16,6 +21,89 @@
     BlinkGC::SweepingType sweeping_type = BlinkGC::kEagerSweeping);
 void ClearOutOldGarbage();
 
+template <typename T>
+class ObjectWithCallbackBeforeInitializer : public GarbageCollected<T> {
+ public:
+  ObjectWithCallbackBeforeInitializer(
+      base::OnceCallback<void(ObjectWithCallbackBeforeInitializer<T>*)>&& cb,
+      T* value)
+      : bool_(ExecuteCallbackReturnTrue(this, std::move(cb))), value_(value) {}
+
+  ObjectWithCallbackBeforeInitializer(
+      base::OnceCallback<void(ObjectWithCallbackBeforeInitializer<T>*)>&& cb)
+      : bool_(ExecuteCallbackReturnTrue(this, std::move(cb))) {}
+
+  virtual void Trace(Visitor* visitor) { visitor->Trace(value_); }
+
+  T* value() const { return value_.Get(); }
+
+ private:
+  static bool ExecuteCallbackReturnTrue(
+      ObjectWithCallbackBeforeInitializer* thiz,
+      base::OnceCallback<void(ObjectWithCallbackBeforeInitializer<T>*)>&& cb) {
+    std::move(cb).Run(thiz);
+    return true;
+  }
+
+  bool bool_;
+  Member<T> value_;
+};
+
+template <typename T>
+class MixinWithCallbackBeforeInitializer : public GarbageCollectedMixin {
+ public:
+  MixinWithCallbackBeforeInitializer(
+      base::OnceCallback<void(MixinWithCallbackBeforeInitializer<T>*)>&& cb,
+      T* value)
+      : bool_(ExecuteCallbackReturnTrue(this, std::move(cb))), value_(value) {}
+
+  MixinWithCallbackBeforeInitializer(
+      base::OnceCallback<void(MixinWithCallbackBeforeInitializer<T>*)>&& cb)
+      : bool_(ExecuteCallbackReturnTrue(this, std::move(cb))) {}
+
+  void Trace(Visitor* visitor) override { visitor->Trace(value_); }
+
+  T* value() const { return value_.Get(); }
+
+ private:
+  static bool ExecuteCallbackReturnTrue(
+      MixinWithCallbackBeforeInitializer* thiz,
+      base::OnceCallback<void(MixinWithCallbackBeforeInitializer<T>*)>&& cb) {
+    std::move(cb).Run(thiz);
+    return true;
+  }
+
+  bool bool_;
+  Member<T> value_;
+};
+
+class BoolMixin {
+ protected:
+  bool bool_ = false;
+};
+
+template <typename T>
+class ObjectWithMixinWithCallbackBeforeInitializer
+    : public GarbageCollected<ObjectWithMixinWithCallbackBeforeInitializer<T>>,
+      public BoolMixin,
+      public MixinWithCallbackBeforeInitializer<T> {
+  USING_GARBAGE_COLLECTED_MIXIN(ObjectWithMixinWithCallbackBeforeInitializer);
+
+ public:
+  using Mixin = MixinWithCallbackBeforeInitializer<T>;
+
+  ObjectWithMixinWithCallbackBeforeInitializer(
+      base::OnceCallback<void(Mixin*)>&& cb,
+      T* value)
+      : Mixin(std::move(cb), value) {}
+
+  ObjectWithMixinWithCallbackBeforeInitializer(
+      base::OnceCallback<void(Mixin*)>&& cb)
+      : Mixin(std::move(cb)) {}
+
+  void Trace(Visitor* visitor) override { Mixin::Trace(visitor); }
+};
+
 }  // namespace blink
 
 #endif  // THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_HEAP_TEST_UTILITIES_H_
diff --git a/third_party/blink/renderer/platform/heap/incremental_marking_test.cc b/third_party/blink/renderer/platform/heap/incremental_marking_test.cc
index 69587d14..b359297 100644
--- a/third_party/blink/renderer/platform/heap/incremental_marking_test.cc
+++ b/third_party/blink/renderer/platform/heap/incremental_marking_test.cc
@@ -1563,28 +1563,30 @@
     thread_state_->IncrementalMarkingStart(BlinkGC::GCReason::kTesting);
   }
 
-  bool SingleStep() {
+  bool SingleStep(BlinkGC::StackState stack_state =
+                      BlinkGC::StackState::kNoHeapPointersOnStack) {
     CHECK(thread_state_->IsIncrementalMarking());
     if (thread_state_->GetGCState() ==
         ThreadState::kIncrementalMarkingStepScheduled) {
-      thread_state_->RunScheduledGC(BlinkGC::kNoHeapPointersOnStack);
+      thread_state_->IncrementalMarkingStep(stack_state);
       return true;
     }
     return false;
   }
 
-  void FinishSteps() {
+  void FinishSteps(BlinkGC::StackState stack_state =
+                       BlinkGC::StackState::kNoHeapPointersOnStack) {
     CHECK(thread_state_->IsIncrementalMarking());
-    while (SingleStep()) {
+    while (SingleStep(stack_state)) {
     }
   }
 
   void FinishGC() {
     CHECK(thread_state_->IsIncrementalMarking());
-    FinishSteps();
+    FinishSteps(BlinkGC::StackState::kNoHeapPointersOnStack);
     CHECK_EQ(ThreadState::kIncrementalMarkingFinalizeScheduled,
              thread_state_->GetGCState());
-    thread_state_->RunScheduledGC(BlinkGC::kNoHeapPointersOnStack);
+    thread_state_->RunScheduledGC(BlinkGC::StackState::kNoHeapPointersOnStack);
     CHECK(!thread_state_->IsIncrementalMarking());
     thread_state_->CompleteSweep();
   }
@@ -1801,6 +1803,80 @@
   ConservativelyCollectGarbage();
 }
 
+namespace {
+
+template <typename T>
+class ObjectHolder : public GarbageCollected<ObjectHolder<T>> {
+ public:
+  ObjectHolder() = default;
+
+  virtual void Trace(Visitor* visitor) { visitor->Trace(holder_); }
+
+  void set_value(T* value) { holder_ = value; }
+  T* value() const { return holder_.Get(); }
+
+ private:
+  Member<T> holder_;
+};
+
+}  // namespace
+
+TEST(IncrementalMarkingTest, StepDuringObjectConstruction) {
+  // Test ensures that objects in construction are delayed for processing to
+  // allow omitting write barriers on initializing stores.
+
+  using O = ObjectWithCallbackBeforeInitializer<Object>;
+  using Holder = ObjectHolder<O>;
+  Persistent<Holder> holder(MakeGarbageCollected<Holder>());
+  IncrementalMarkingTestDriver driver(ThreadState::Current());
+  driver.Start();
+  MakeGarbageCollected<O>(
+      base::BindOnce(
+          [](IncrementalMarkingTestDriver* driver, Holder* holder, O* thiz) {
+            // Publish not-fully-constructed object |thiz| by triggering write
+            // barrier for the object.
+            holder->set_value(thiz);
+            CHECK(HeapObjectHeader::FromPayload(holder->value())->IsValid());
+            // Finish call incremental steps.
+            driver->FinishSteps(BlinkGC::StackState::kHeapPointersOnStack);
+          },
+          &driver, holder.Get()),
+      MakeGarbageCollected<Object>());
+  driver.FinishGC();
+  CHECK(HeapObjectHeader::FromPayload(holder->value())->IsValid());
+  CHECK(HeapObjectHeader::FromPayload(holder->value()->value())->IsValid());
+  PreciselyCollectGarbage();
+}
+
+TEST(IncrementalMarkingTest, StepDuringMixinObjectConstruction) {
+  // Test ensures that mixin objects in construction are delayed for processing
+  // to allow omitting write barriers on initializing stores.
+
+  using Parent = ObjectWithMixinWithCallbackBeforeInitializer<Object>;
+  using Mixin = MixinWithCallbackBeforeInitializer<Object>;
+  using Holder = ObjectHolder<Mixin>;
+  Persistent<Holder> holder(MakeGarbageCollected<Holder>());
+  IncrementalMarkingTestDriver driver(ThreadState::Current());
+  driver.Start();
+  MakeGarbageCollected<Parent>(
+      base::BindOnce(
+          [](IncrementalMarkingTestDriver* driver, Holder* holder,
+             Mixin* thiz) {
+            // Publish not-fully-constructed object
+            // |thiz| by triggering write barrier for
+            // the object.
+            holder->set_value(thiz);
+            // Finish call incremental steps.
+            driver->FinishSteps(BlinkGC::StackState::kHeapPointersOnStack);
+          },
+          &driver, holder.Get()),
+      MakeGarbageCollected<Object>());
+  driver.FinishGC();
+  CHECK(holder->value()->GetHeapObjectHeader()->IsValid());
+  CHECK(HeapObjectHeader::FromPayload(holder->value()->value())->IsValid());
+  PreciselyCollectGarbage();
+}
+
 }  // namespace incremental_marking_test
 }  // namespace blink
 
diff --git a/third_party/blink/renderer/platform/heap/marking_visitor.cc b/third_party/blink/renderer/platform/heap/marking_visitor.cc
index 6efd4e027..b95eb73 100644
--- a/third_party/blink/renderer/platform/heap/marking_visitor.cc
+++ b/third_party/blink/renderer/platform/heap/marking_visitor.cc
@@ -64,11 +64,10 @@
   if (!header || header->IsMarked())
     return;
 
-  // Simple case for fully constructed objects or those that have no vtable
-  // which make dispatching to member fields trivial.
+  // Simple case for fully constructed objects.
   const GCInfo* gc_info =
       GCInfoTable::Get().GCInfoFromIndex(header->GcInfoIndex());
-  if (!gc_info->HasVTable() || !header->IsInConstruction()) {
+  if (!header->IsInConstruction()) {
     MarkHeader(header, gc_info->trace_);
     return;
   }
diff --git a/third_party/blink/renderer/platform/heap/marking_visitor.h b/third_party/blink/renderer/platform/heap/marking_visitor.h
index cbb41e8..a53f0bf 100644
--- a/third_party/blink/renderer/platform/heap/marking_visitor.h
+++ b/third_party/blink/renderer/platform/heap/marking_visitor.h
@@ -51,11 +51,14 @@
 
   // Marking implementation.
 
-  // Conservatively marks an object if pointed to by Address.
+  // Conservatively marks an object if pointed to by Address. The object may
+  // be in construction as the scan is conservative without relying on a
+  // Trace method.
   void ConservativelyMarkAddress(BasePage*, Address);
 
   // Marks an object dynamically using any address within its body and adds a
-  // tracing callback for processing of the object.
+  // tracing callback for processing of the object. The object is not allowed
+  // to be in construction.
   void DynamicallyMarkAddress(Address);
 
   // Marks an object and adds a tracing callback for processing of the object.
@@ -97,8 +100,11 @@
       // that lead to many recursions.
       DCHECK(Heap().GetStackFrameDepth().IsAcceptableStackUse());
       if (LIKELY(Heap().GetStackFrameDepth().IsSafeToRecurse())) {
-        if (MarkHeaderNoTracing(
-                HeapObjectHeader::FromPayload(desc.base_object_payload))) {
+        HeapObjectHeader* header =
+            HeapObjectHeader::FromPayload(desc.base_object_payload);
+        if (header->IsInConstruction()) {
+          not_fully_constructed_worklist_.Push(desc.base_object_payload);
+        } else if (MarkHeaderNoTracing(header)) {
           desc.callback(this, desc.base_object_payload);
         }
         return;
@@ -199,7 +205,9 @@
   DCHECK(header);
   DCHECK(callback);
 
-  if (MarkHeaderNoTracing(header)) {
+  if (header->IsInConstruction()) {
+    not_fully_constructed_worklist_.Push(header->Payload());
+  } else if (MarkHeaderNoTracing(header)) {
     marking_worklist_.Push(
         {reinterpret_cast<void*>(header->Payload()), callback});
   }
diff --git a/third_party/blink/renderer/platform/heap/persistent.h b/third_party/blink/renderer/platform/heap/persistent.h
index 35ed1d2..69db982b 100644
--- a/third_party/blink/renderer/platform/heap/persistent.h
+++ b/third_party/blink/renderer/platform/heap/persistent.h
@@ -269,7 +269,7 @@
                        weaknessConfiguration, crossThreadnessConfiguration>;
     Base* persistent = reinterpret_cast<Base*>(persistent_pointer);
     T* object = persistent->Get();
-    if (object && !ObjectAliveTrait<T>::IsHeapObjectAlive(object))
+    if (object && !ThreadHeap::IsHeapObjectAlive(object))
       ClearWeakPersistent(persistent);
   }
 
diff --git a/third_party/blink/renderer/platform/heap/thread_state.cc b/third_party/blink/renderer/platform/heap/thread_state.cc
index 1e7ffa8..a35433a 100644
--- a/third_party/blink/renderer/platform/heap/thread_state.cc
+++ b/third_party/blink/renderer/platform/heap/thread_state.cc
@@ -177,7 +177,6 @@
       mixins_being_constructed_count_(0),
       object_resurrection_forbidden_(false),
       in_atomic_pause_(false),
-      gc_mixin_marker_(nullptr),
       gc_state_(kNoGCScheduled),
       gc_phase_(GCPhase::kNone),
       reason_for_scheduled_gc_(BlinkGC::GCReason::kMaxValue),
diff --git a/third_party/blink/renderer/platform/heap/thread_state.h b/third_party/blink/renderer/platform/heap/thread_state.h
index 7e7a50e6..00282d6 100644
--- a/third_party/blink/renderer/platform/heap/thread_state.h
+++ b/third_party/blink/renderer/platform/heap/thread_state.h
@@ -62,7 +62,6 @@
 class IncrementalMarkingTestDriver;
 }  // namespace incremental_marking_test
 
-class GarbageCollectedMixinConstructorMarkerBase;
 class MarkingVisitor;
 class PersistentNode;
 class PersistentRegion;
@@ -480,29 +479,6 @@
     perform_cleanup_ = perform_cleanup;
   }
 
-  // By entering a gc-forbidden scope, conservative GCs will not
-  // be allowed while handling an out-of-line allocation request.
-  // Intended used when constructing subclasses of GC mixins, where
-  // the object being constructed cannot be safely traced & marked
-  // fully should a GC be allowed while its subclasses are being
-  // constructed.
-  void EnterGCForbiddenScopeIfNeeded(
-      GarbageCollectedMixinConstructorMarkerBase* gc_mixin_marker) {
-    DCHECK(CheckThread());
-    if (!gc_mixin_marker_) {
-      EnterMixinConstructionScope();
-      gc_mixin_marker_ = gc_mixin_marker;
-    }
-  }
-  void LeaveGCForbiddenScopeIfNeeded(
-      GarbageCollectedMixinConstructorMarkerBase* gc_mixin_marker) {
-    DCHECK(CheckThread());
-    if (gc_mixin_marker_ == gc_mixin_marker) {
-      LeaveMixinConstructionScope();
-      gc_mixin_marker_ = nullptr;
-    }
-  }
-
   void FreePersistentNode(PersistentRegion*, PersistentNode*);
 
   using PersistentClearCallback = void (*)(void*);
@@ -707,8 +683,6 @@
   TimeDelta next_incremental_marking_step_duration_;
   TimeDelta previous_incremental_marking_time_left_;
 
-  GarbageCollectedMixinConstructorMarkerBase* gc_mixin_marker_;
-
   GCState gc_state_;
   GCPhase gc_phase_;
   BlinkGC::GCReason reason_for_scheduled_gc_;