diff --git a/priority.go b/priority.go index 67aee4c..481c12d 100644 --- a/priority.go +++ b/priority.go @@ -48,6 +48,10 @@ func (h *priorityHeap[T]) Pop() any { elem := h.elems[n-1] + var zero T + + h.elems[n-1] = zero + h.elems = h.elems[0 : n-1] return elem diff --git a/priority_test.go b/priority_test.go index 05277ee..b223e31 100644 --- a/priority_test.go +++ b/priority_test.go @@ -24,6 +24,7 @@ func TestPriority(t *testing.T) { t.Run("CapacityLesserThanLenElems", testPriorityCapacityLesserThanLenElems) t.Run("Offer", testPriorityOffer) t.Run("Get", testPriorityGet) + t.Run("GetReleasesReference", testPriorityGetReleasesReference) t.Run("Clear", testPriorityClear) t.Run("Contains", testPriorityContains) t.Run("Iterator", testPriorityIterator) @@ -35,6 +36,51 @@ func TestPriority(t *testing.T) { t.Run("ResetReleasesExtras", testPriorityResetReleasesExtras) } +func testPriorityGetReleasesReference(t *testing.T) { + t.Parallel() + + type payload struct{ id int } + + var pq *queue.Priority[*payload] + + finalized := make(chan struct{}, 1) + + func() { + p := &payload{id: 42} + + runtime.SetFinalizer(p, func(*payload) { finalized <- struct{}{} }) + + pq = queue.NewPriority[*payload](nil, func(a, b *payload) bool { return a.id < b.id }) + + if err := pq.Offer(p); err != nil { + t.Fatalf("offer: %v", err) + } + + got, err := pq.Get() + if err != nil || got != p { + t.Fatalf("get: %v, %v", got, err) + } + }() + + deadline := time.After(time.Second) + + for { + runtime.GC() //nolint:revive // explicit GC needed to drive finalizer + + select { + case <-finalized: + runtime.KeepAlive(pq) + + return + case <-deadline: + runtime.KeepAlive(pq) + t.Fatal("popped element not finalized; backing array still holds it") + default: + time.Sleep(10 * time.Millisecond) + } + } +} + func testPriorityResetReleasesExtras(t *testing.T) { t.Parallel()