mirror of
https://github.com/1f349/cache.git
synced 2025-01-20 22:16:37 +00:00
Fix cleaner goroutine returning without closing the close channel
This commit is contained in:
parent
1026af286f
commit
76230a5bfc
47
cache.go
47
cache.go
@ -66,66 +66,31 @@ func (c *Cache[K, V]) Close() {
|
|||||||
// cleaner handles removing expired keys. The chainAdd and chainDel channels are
|
// cleaner handles removing expired keys. The chainAdd and chainDel channels are
|
||||||
// handled here to prevent race conditions. This ensures the expiry timer can be
|
// handled here to prevent race conditions. This ensures the expiry timer can be
|
||||||
// stopped before modifying the chain.
|
// stopped before modifying the chain.
|
||||||
//
|
|
||||||
// The cleaner is stopped whenever the chain is empty due to there being no chain
|
|
||||||
// to manage.
|
|
||||||
func (c *Cache[K, V]) cleaner() {
|
func (c *Cache[K, V]) cleaner() {
|
||||||
// cleaner is always called from Set or Delete methods with a value sent on chainAdd or chainDel
|
|
||||||
select {
|
|
||||||
case node := <-c.chainAdd:
|
|
||||||
c.chainInsert(node)
|
|
||||||
case key := <-c.chainDel:
|
|
||||||
c.chainSplice(key)
|
|
||||||
default:
|
|
||||||
// skip if chainAdd or chainDel isn't ready
|
|
||||||
}
|
|
||||||
|
|
||||||
// at this point if the chain is empty then exit
|
|
||||||
if c.chain == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// create a timer for the next expiry
|
|
||||||
t := time.NewTimer(timeUntil(c.chain.expires))
|
|
||||||
|
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case <-c.close:
|
case <-c.close:
|
||||||
// exit the cleaner goroutine
|
// exit the cleaner goroutine
|
||||||
return
|
return
|
||||||
case node := <-c.chainAdd:
|
case node := <-c.chainAdd:
|
||||||
// stop the timer safely
|
|
||||||
if !t.Stop() {
|
|
||||||
<-t.C
|
|
||||||
}
|
|
||||||
// the chain will not be empty after this insert so no check is required
|
|
||||||
c.chainInsert(node)
|
c.chainInsert(node)
|
||||||
case key := <-c.chainDel:
|
case key := <-c.chainDel:
|
||||||
// stop the timer safely
|
|
||||||
if !t.Stop() {
|
|
||||||
<-t.C
|
|
||||||
}
|
|
||||||
c.chainSplice(key)
|
c.chainSplice(key)
|
||||||
case <-t.C:
|
case <-c.nextExpiry():
|
||||||
// if there is no chain then kill the expiry scheduler
|
|
||||||
if c.chain == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// remove all expired entries
|
// remove all expired entries
|
||||||
for c.chain != nil && c.chain.HasExpired() {
|
for c.chain != nil && c.chain.HasExpired() {
|
||||||
c.items.CompareAndDelete(c.chain.data, c.chain.item)
|
c.items.CompareAndDelete(c.chain.data, c.chain.item)
|
||||||
c.chain = c.chain.next
|
c.chain = c.chain.next
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// if there is no chain then kill the expiry scheduler
|
func (c *Cache[K, V]) nextExpiry() <-chan time.Time {
|
||||||
if c.chain == nil {
|
if c.chain == nil {
|
||||||
return
|
return make(chan time.Time)
|
||||||
}
|
|
||||||
|
|
||||||
t.Reset(timeUntil(c.chain.expires))
|
|
||||||
}
|
}
|
||||||
|
return time.After(timeUntil(c.chain.expires))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Cache[K, V]) chainInsert(node keyed[K]) {
|
func (c *Cache[K, V]) chainInsert(node keyed[K]) {
|
||||||
|
@ -152,3 +152,21 @@ func TestCache_UpdateExpiry(t *testing.T) {
|
|||||||
assert.True(t, b)
|
assert.True(t, b)
|
||||||
assert.Equal(t, "b", get)
|
assert.Equal(t, "b", get)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestCache_ClearerDeath(t *testing.T) {
|
||||||
|
timeNow = func() time.Time { return time.Now() }
|
||||||
|
|
||||||
|
c := New[string, string]()
|
||||||
|
|
||||||
|
time.Sleep(10 * time.Millisecond)
|
||||||
|
|
||||||
|
var added bool
|
||||||
|
go func() {
|
||||||
|
c.chainAdd <- keyed[string]{item: item[string]{data: "a"}}
|
||||||
|
c.chainAdd <- keyed[string]{item: item[string]{data: "b"}}
|
||||||
|
added = true
|
||||||
|
}()
|
||||||
|
|
||||||
|
time.Sleep(10 * time.Millisecond)
|
||||||
|
assert.True(t, added)
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user