From 10c6e01e1f6eb5087f031a4ae7691b8dc52d34d5 Mon Sep 17 00:00:00 2001 From: Josh Bleecher Snyder Date: Thu, 22 Dec 2016 17:37:32 -0800 Subject: [PATCH] Allow GC to reclaim completed transactions The existing append-based implementation left a hanging reference to the last tx. For example, if db.txs was: []*Tx{0x1, 0x2, 0x3, 0x4, 0x5} and we removed the second element, db.txs would now be: []*Tx{0x1, 0x3, 0x4, 0x5, 0x5}[:4] The garbage collector cannot reclaim anything anywhere in a slice, even pointers between its len and cap, because the len can always be extended up to the cap. This hanging reference to the Tx could last indefinitely, and since the Tx has a reference to user-provided functions, which could be closures, this bug could prevent arbitrary amounts of user garbage from being collected. Since db.txs is unordered anyway, switch to a simpler--and O(1) instead of O(n)--implementation. Swap the last element into the spot to be deleted, nil out the original last element, and shrink the slice. --- db.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/db.go b/db.go index 48da059..f352ff1 100644 --- a/db.go +++ b/db.go @@ -552,7 +552,10 @@ func (db *DB) removeTx(tx *Tx) { // Remove the transaction. for i, t := range db.txs { if t == tx { - db.txs = append(db.txs[:i], db.txs[i+1:]...) + last := len(db.txs) - 1 + db.txs[i] = db.txs[last] + db.txs[last] = nil + db.txs = db.txs[:last] break } }