From e02ac9918b39281f0bcd5e0338b6bd4a92831fc3 Mon Sep 17 00:00:00 2001 From: Prashant Date: Thu, 12 Feb 2026 22:04:23 +0530 Subject: [PATCH 1/3] break gc reachability of removed element in bianryheap --- std/container/binaryheap.d | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/std/container/binaryheap.d b/std/container/binaryheap.d index 0fd34526ea7..95cf1ef62e2 100644 --- a/std/container/binaryheap.d +++ b/std/container/binaryheap.d @@ -312,8 +312,8 @@ and $(D length == capacity), throws an exception. return 1; } -/** -Removes the largest element from the heap. + /** + Removes the largest element from the heap. */ void removeFront() { @@ -326,6 +326,8 @@ Removes the largest element from the heap. _store[_length - 1] = move(t1); } --_length; + // Break GC reachability of the removed element + _store[length] = typeof(_store[_length]).init; percolate(_store[], 0, _length); } From 1d14d21baf41420ac0b173a041a10ff12f54b412 Mon Sep 17 00:00:00 2001 From: Prashant Date: Thu, 5 Mar 2026 15:46:51 +0530 Subject: [PATCH 2/3] added removeTail function to clear inactive slots instead of clearing on popFront call --- std/container/binaryheap.d | 45 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 43 insertions(+), 2 deletions(-) diff --git a/std/container/binaryheap.d b/std/container/binaryheap.d index 95cf1ef62e2..dade39df1f8 100644 --- a/std/container/binaryheap.d +++ b/std/container/binaryheap.d @@ -261,6 +261,27 @@ Clears the heap by detaching it from the underlying store. _payload = _payload.init; } +/** +Clears the inactive portion of the heap (from `length` to `capacity`) +to break GC reachability. This is useful for heaps with large capacities +where the unused portion might hold references to objects. + */ + void removeTail() + { + if (!_payload.refCountedStore.isInitialized) return; + if (_length >= _store.length) return; + + alias T = typeof(_store[0]); + static if (hasIndirections!T) + { + // Clear the inactive portion of the store + foreach (i; _length .. _store.length) + { + _store[i] = T.init; + } + } + } + /** Inserts `value` into the store. If the underlying store is a range and $(D length == capacity), throws an exception. @@ -326,8 +347,6 @@ and $(D length == capacity), throws an exception. _store[_length - 1] = move(t1); } --_length; - // Break GC reachability of the removed element - _store[length] = typeof(_store[_length]).init; percolate(_store[], 0, _length); } @@ -345,6 +364,7 @@ that are expensive to copy. removeFront(); return _store[_length]; } + /** Replaces the largest element in the store with `value`. @@ -639,3 +659,24 @@ original Dyamic Array, but point to a new Dynamic Array. a[0] = 60; assert(h.front() == 40); } + +/// Test removeTail() clears inactive portion of heap with references +@system unittest +{ + class Node { int value; this(int v) { value = v; } } + + Node[] a = new Node[10]; + foreach (i; 0..5) + a[i] = new Node(i); + + auto h = heapify!"a.value > b.value"(a[0..5]); + + h.insert(new Node(6)); + Node saved = a[5]; + assert(a[6] is null); + + // After removeTail(), inactive slots should be cleared + h.removeTail(); + assert(a[5] is saved); // Active element unchanged + assert(a[6] is null); // Inactive slots cleared +} From df2985e2e88605618a4fadcc44db0b9820517b51 Mon Sep 17 00:00:00 2001 From: Prashant Date: Thu, 5 Mar 2026 16:04:05 +0530 Subject: [PATCH 3/3] style fixed --- std/container/binaryheap.d | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/std/container/binaryheap.d b/std/container/binaryheap.d index dade39df1f8..ea3fcf71d2f 100644 --- a/std/container/binaryheap.d +++ b/std/container/binaryheap.d @@ -270,7 +270,6 @@ where the unused portion might hold references to objects. { if (!_payload.refCountedStore.isInitialized) return; if (_length >= _store.length) return; - alias T = typeof(_store[0]); static if (hasIndirections!T) { @@ -364,7 +363,6 @@ that are expensive to copy. removeFront(); return _store[_length]; } - /** Replaces the largest element in the store with `value`. @@ -664,17 +662,17 @@ original Dyamic Array, but point to a new Dynamic Array. @system unittest { class Node { int value; this(int v) { value = v; } } - + Node[] a = new Node[10]; - foreach (i; 0..5) + foreach (i; 0 .. 5) a[i] = new Node(i); - - auto h = heapify!"a.value > b.value"(a[0..5]); - + + auto h = heapify!"a.value > b.value"(a[0 .. 5]); + h.insert(new Node(6)); Node saved = a[5]; assert(a[6] is null); - + // After removeTail(), inactive slots should be cleared h.removeTail(); assert(a[5] is saved); // Active element unchanged