LibGC: Avoid decommit worker during full sweep teardown
Heap destruction uses CollectEverything to synchronously free every live cell. Empty blocks from that pass do not need deferred madvise work. Registering them with the global decommit worker can start a detached thread during process teardown. LeakSanitizer can then hang while doing its final thread scan with log_threads enabled. Let full sweep deallocation opt out of deferred decommit registration. Normal incremental sweeping stays on the background worker path. The sanitizer LibGC container and visitor tests now exit with verbose LSan thread logging enabled.
This commit is contained in:
parent
af7b2b5e8a
commit
42da44868b
5 changed files with 16 additions and 11 deletions
|
|
@ -372,7 +372,7 @@ void* BlockAllocator::allocate_block([[maybe_unused]] char const* name)
|
|||
return block;
|
||||
}
|
||||
|
||||
void BlockAllocator::deallocate_block(void* block)
|
||||
void BlockAllocator::deallocate_block(void* block, DeferDecommit defer_decommit)
|
||||
{
|
||||
VERIFY(block);
|
||||
|
||||
|
|
@ -385,12 +385,12 @@ void BlockAllocator::deallocate_block(void* block)
|
|||
{
|
||||
Sync::MutexLocker locker(m_mutex);
|
||||
m_freshly_freed.append(block);
|
||||
if (!m_in_decommit_registry) {
|
||||
if (defer_decommit == DeferDecommit::Yes && !m_in_decommit_registry) {
|
||||
m_in_decommit_registry = true;
|
||||
need_to_register = true;
|
||||
}
|
||||
}
|
||||
if (need_to_register)
|
||||
if (need_to_register && defer_decommit == DeferDecommit::Yes)
|
||||
DecommitWorker::the().register_pending(*this);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -14,6 +14,11 @@
|
|||
|
||||
namespace GC {
|
||||
|
||||
enum class DeferDecommit {
|
||||
No,
|
||||
Yes,
|
||||
};
|
||||
|
||||
class DecommitWorker;
|
||||
|
||||
class GC_API BlockAllocator {
|
||||
|
|
@ -22,7 +27,7 @@ public:
|
|||
~BlockAllocator();
|
||||
|
||||
void* allocate_block(char const* name);
|
||||
void deallocate_block(void*);
|
||||
void deallocate_block(void*, DeferDecommit = DeferDecommit::Yes);
|
||||
|
||||
size_t block_count();
|
||||
|
||||
|
|
|
|||
|
|
@ -53,13 +53,13 @@ Cell* CellAllocator::allocate_cell(Heap& heap)
|
|||
return cell;
|
||||
}
|
||||
|
||||
void CellAllocator::block_did_become_empty(Badge<Heap>, HeapBlock& block)
|
||||
void CellAllocator::block_did_become_empty(Badge<Heap>, HeapBlock& block, DeferDecommit defer_decommit)
|
||||
{
|
||||
block.m_list_node.remove();
|
||||
block.heap().m_live_heap_blocks.remove(&block);
|
||||
// NOTE: HeapBlocks are managed by the BlockAllocator, so we don't want to `delete` the block here.
|
||||
block.~HeapBlock();
|
||||
m_block_allocator.deallocate_block(&block);
|
||||
m_block_allocator.deallocate_block(&block, defer_decommit);
|
||||
}
|
||||
|
||||
void CellAllocator::block_did_become_usable(Badge<Heap>, HeapBlock& block)
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@ public:
|
|||
return IterationDecision::Continue;
|
||||
}
|
||||
|
||||
void block_did_become_empty(Badge<Heap>, HeapBlock&);
|
||||
void block_did_become_empty(Badge<Heap>, HeapBlock&, DeferDecommit = DeferDecommit::Yes);
|
||||
void block_did_become_usable(Badge<Heap>, HeapBlock&);
|
||||
|
||||
bool has_blocks_pending_sweep() const { return !m_blocks_pending_sweep.is_empty(); }
|
||||
|
|
|
|||
|
|
@ -1125,7 +1125,7 @@ void Heap::sweep_dead_cells(bool print_report, Core::ElapsedTimer const& measure
|
|||
ScopedPhaseTimer timer { g_recording_phase_timings, g_phase_timings.sweep_block_reclassify_us };
|
||||
for (auto* block : empty_blocks) {
|
||||
dbgln_if(HEAP_DEBUG, " - HeapBlock empty @ {}: cell_size={}", block, block->cell_size());
|
||||
block->cell_allocator().block_did_become_empty({}, *block);
|
||||
block->cell_allocator().block_did_become_empty({}, *block, DeferDecommit::No);
|
||||
}
|
||||
|
||||
for (auto* block : full_blocks_that_became_usable) {
|
||||
|
|
@ -1158,9 +1158,9 @@ void Heap::sweep_dead_cells(bool print_report, Core::ElapsedTimer const& measure
|
|||
}
|
||||
(void)measurement_timer;
|
||||
|
||||
// Sweep is done; kick the global decommit worker so the slots we just
|
||||
// freed get madvise()'d off the GC pause path.
|
||||
BlockAllocator::wake_decommit_worker_async();
|
||||
// CollectEverything finishes synchronously, and is currently only used
|
||||
// when the heap is going away. Do not spin up background decommit work
|
||||
// from process teardown.
|
||||
}
|
||||
|
||||
void Heap::sweep_block(HeapBlock& block)
|
||||
|
|
|
|||
Loading…
Reference in a new issue