clash/common/cache/cache.go

107 lines
2 KiB
Go
Raw Normal View History

2018-12-05 13:13:29 +00:00
package cache
import (
"runtime"
"sync"
"time"
)
// Cache store element with a expired time
2022-04-05 15:29:52 +00:00
type Cache[K comparable, V any] struct {
*cache[K, V]
2018-12-05 13:13:29 +00:00
}
2022-04-05 15:29:52 +00:00
type cache[K comparable, V any] struct {
2018-12-05 13:13:29 +00:00
mapping sync.Map
2022-04-05 15:29:52 +00:00
janitor *janitor[K, V]
2018-12-05 13:13:29 +00:00
}
2022-04-05 15:29:52 +00:00
type element[V any] struct {
2018-12-05 13:13:29 +00:00
Expired time.Time
2022-04-05 15:29:52 +00:00
Payload V
2018-12-05 13:13:29 +00:00
}
// Put element in Cache with its ttl
2022-04-05 15:29:52 +00:00
func (c *cache[K, V]) Put(key K, payload V, ttl time.Duration) {
c.mapping.Store(key, &element[V]{
2018-12-05 13:13:29 +00:00
Payload: payload,
Expired: time.Now().Add(ttl),
})
}
// Get element in Cache, and drop when it expired
2022-04-05 15:29:52 +00:00
func (c *cache[K, V]) Get(key K) V {
2018-12-05 13:13:29 +00:00
item, exist := c.mapping.Load(key)
if !exist {
2022-04-05 15:29:52 +00:00
return getZero[V]()
2018-12-05 13:13:29 +00:00
}
2022-04-05 15:29:52 +00:00
elm := item.(*element[V])
2018-12-05 13:13:29 +00:00
// expired
if time.Since(elm.Expired) > 0 {
c.mapping.Delete(key)
2022-04-05 15:29:52 +00:00
return getZero[V]()
2018-12-05 13:13:29 +00:00
}
return elm.Payload
}
// GetWithExpire element in Cache with Expire Time
2022-04-05 15:29:52 +00:00
func (c *cache[K, V]) GetWithExpire(key K) (payload V, expired time.Time) {
item, exist := c.mapping.Load(key)
if !exist {
return
}
2022-04-05 15:29:52 +00:00
elm := item.(*element[V])
// expired
if time.Since(elm.Expired) > 0 {
c.mapping.Delete(key)
return
}
return elm.Payload, elm.Expired
}
2022-04-05 15:29:52 +00:00
func (c *cache[K, V]) cleanup() {
2022-03-16 04:10:13 +00:00
c.mapping.Range(func(k, v any) bool {
2018-12-05 13:13:29 +00:00
key := k.(string)
2022-04-05 15:29:52 +00:00
elm := v.(*element[V])
2018-12-05 13:13:29 +00:00
if time.Since(elm.Expired) > 0 {
c.mapping.Delete(key)
}
return true
})
}
2022-04-05 15:29:52 +00:00
type janitor[K comparable, V any] struct {
2018-12-05 13:13:29 +00:00
interval time.Duration
stop chan struct{}
}
2022-04-05 15:29:52 +00:00
func (j *janitor[K, V]) process(c *cache[K, V]) {
2018-12-05 13:13:29 +00:00
ticker := time.NewTicker(j.interval)
for {
select {
case <-ticker.C:
c.cleanup()
case <-j.stop:
ticker.Stop()
return
}
}
}
2022-04-05 15:29:52 +00:00
func stopJanitor[K comparable, V any](c *Cache[K, V]) {
2018-12-05 13:13:29 +00:00
c.janitor.stop <- struct{}{}
}
// New return *Cache
2022-04-05 15:29:52 +00:00
func New[K comparable, V any](interval time.Duration) *Cache[K, V] {
j := &janitor[K, V]{
2018-12-05 13:13:29 +00:00
interval: interval,
stop: make(chan struct{}),
}
2022-04-05 15:29:52 +00:00
c := &cache[K, V]{janitor: j}
2018-12-05 13:13:29 +00:00
go j.process(c)
2022-04-05 15:29:52 +00:00
C := &Cache[K, V]{c}
runtime.SetFinalizer(C, stopJanitor[K, V])
2018-12-05 13:13:29 +00:00
return C
}