浏览代码

LibJS: Add a finalization pass to the garbage collector

Doing things in the destructor of a GC-allocated object isn't always
safe, in case it involves accessing other GC-allocated objects.
If they were already swept by GC, we'd be poking into freed memory.

This patch adds a separate finalization pass where GC calls finalize()
on every unmarked cell that's about to be deleted.

It's safe to access other GC objects in finalize(), even if they're
also unmarked.
Andreas Kling 2 年之前
父节点
当前提交
07a36c8f80
共有 3 个文件被更改,包括 16 次插入0 次删除
  1. 3 0
      Userland/Libraries/LibJS/Heap/Cell.h
  2. 12 0
      Userland/Libraries/LibJS/Heap/Heap.cpp
  3. 1 0
      Userland/Libraries/LibJS/Heap/Heap.h

+ 3 - 0
Userland/Libraries/LibJS/Heap/Cell.h

@@ -77,6 +77,9 @@ public:
     virtual bool is_environment() const { return false; }
     virtual void visit_edges(Visitor&) { }
 
+    // This will be called on unmarked objects by the garbage collector in a separate pass before destruction.
+    virtual void finalize() { }
+
     Heap& heap() const;
     VM& vm() const;
 

+ 12 - 0
Userland/Libraries/LibJS/Heap/Heap.cpp

@@ -106,6 +106,7 @@ void Heap::collect_garbage(CollectionType collection_type, bool print_report)
         gather_roots(roots);
         mark_live_cells(roots);
     }
+    finalize_unmarked_cells();
     sweep_dead_cells(print_report, collection_measurement_timer);
 }
 
@@ -231,6 +232,17 @@ void Heap::mark_live_cells(HashTable<Cell*> const& roots)
     m_uprooted_cells.clear();
 }
 
+void Heap::finalize_unmarked_cells()
+{
+    for_each_block([&](auto& block) {
+        block.template for_each_cell_in_state<Cell::State::Live>([](Cell* cell) {
+            if (!cell->is_marked())
+                cell->finalize();
+        });
+        return IterationDecision::Continue;
+    });
+}
+
 void Heap::sweep_dead_cells(bool print_report, Core::ElapsedTimer const& measurement_timer)
 {
     dbgln_if(HEAP_DEBUG, "sweep_dead_cells:");

+ 1 - 0
Userland/Libraries/LibJS/Heap/Heap.h

@@ -84,6 +84,7 @@ private:
     void gather_roots(HashTable<Cell*>&);
     void gather_conservative_roots(HashTable<Cell*>&);
     void mark_live_cells(HashTable<Cell*> const& live_cells);
+    void finalize_unmarked_cells();
     void sweep_dead_cells(bool print_report, Core::ElapsedTimer const&);
 
     CellAllocator& allocator_for_size(size_t);