mirror of
https://github.com/idanoo/autobrr
synced 2025-07-22 16:29:12 +00:00
79 lines
1.6 KiB
Go
79 lines
1.6 KiB
Go
// Copyright (c) 2021-2025, Ludvig Lundgren and the autobrr contributors.
|
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
|
|
package ttlcache
|
|
|
|
import (
|
|
"time"
|
|
)
|
|
|
|
func (c *Cache[K, V]) startExpirations() {
|
|
timer := time.NewTimer(1 * time.Second)
|
|
stopTimer(timer) // wasteful, but makes the loop cleaner because this is initialized.
|
|
defer stopTimer(timer)
|
|
|
|
var timeSleep time.Time
|
|
for {
|
|
select {
|
|
case t, ok := <-c.ch:
|
|
if !ok {
|
|
return
|
|
} else if t.IsZero() {
|
|
continue
|
|
}
|
|
|
|
if timeSleep.IsZero() || timeSleep.After(t) {
|
|
timeSleep = t
|
|
restartTimer(timer, timeSleep.Sub(c.tc.Now()))
|
|
}
|
|
|
|
case <-timer.C:
|
|
stopTimer(timer)
|
|
c.expire()
|
|
timeSleep = time.Time{}
|
|
}
|
|
}
|
|
}
|
|
|
|
func restartTimer(t *time.Timer, d time.Duration) {
|
|
stopTimer(t)
|
|
t.Reset(d)
|
|
}
|
|
|
|
func stopTimer(t *time.Timer) {
|
|
t.Stop()
|
|
|
|
// go < 1.23 returns stale values on expired timers.
|
|
if len(t.C) != 0 {
|
|
<-t.C
|
|
}
|
|
}
|
|
|
|
func (c *Cache[K, V]) expire() {
|
|
t := c.tc.Now()
|
|
var soon time.Time
|
|
|
|
c.l.Lock()
|
|
defer c.l.Unlock()
|
|
for k, v := range c.m {
|
|
if v.t.IsZero() {
|
|
continue
|
|
} else if v.t.After(t) {
|
|
if soon.IsZero() || soon.After(v.t) {
|
|
soon = v.t
|
|
}
|
|
continue
|
|
}
|
|
|
|
c.deleteUnsafe(k, v, ReasonTimedOut)
|
|
}
|
|
|
|
if !soon.IsZero() { // wake-up feedback loop
|
|
go func(s time.Time) { // we need to release the lock, if the input pipeline has exceeded the wakeup budget.
|
|
defer func() {
|
|
_ = recover() // if the channel is closed, this doesn't matter on shutdown because this is expected.
|
|
}()
|
|
c.ch <- s
|
|
}(soon)
|
|
}
|
|
}
|