123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285 |
- package cache
- import (
- "context"
- "sync"
- "time"
- )
- const (
- LiveInfinite = time.Duration(-1)
- )
- type CallCreate func(key any) (value any, created bool)
- type CallCreateNew func(key any) (rKey, value any, created bool)
- type CallCheck func(key, value any, exists bool) (rKey, rValue any, created bool)
- type item struct {
- value any
- expire int64
- }
- func NewCacheMap(ctx context.Context, maxSize int, live time.Duration) *CacheMap {
- cctx, cancel := context.WithCancel(ctx)
- res := &CacheMap{
- ctx: cctx,
- cancel: cancel,
- items: make(map[any]*item),
- mi: new(sync.RWMutex),
- live: live,
- maxSize: maxSize,
- mc: new(sync.RWMutex),
- }
- // todo - clean worker
- return res
- }
- type CacheMap struct {
- finished bool
- ctx context.Context
- cancel context.CancelFunc
- items map[any]*item
- mi *sync.RWMutex
- live time.Duration
- maxSize int
- cleanChans []chan map[any]any
- mc *sync.RWMutex
- }
- func (s *CacheMap) IsFinished() bool {
- return s.finished
- }
- func (s *CacheMap) close() {
- s.finished = true
- s.mc.Lock()
- for _, ch := range s.cleanChans {
- close(ch)
- }
- s.cleanChans = nil
- s.mc.Unlock()
- }
- func (s *CacheMap) eventClean(m map[any]any) {
- s.mc.RLock()
- for _, ch := range s.cleanChans {
- select {
- case ch <- m:
- default:
- }
- }
- s.mc.RUnlock()
- }
- func (s *CacheMap) EventCleaner() <-chan map[any]any {
- s.mc.Lock()
- ech := make(chan map[any]any, 1)
- s.cleanChans = append(s.cleanChans, ech)
- s.mc.Unlock()
- return ech
- }
- func (s *CacheMap) cleanWorker() {
- defer func() {
- for _, v := range s.cleanChans {
- close(v)
- }
- }()
- t := time.NewTicker(s.live)
- loop:
- for {
- select {
- case <-t.C:
- // clean items
- nowTS := time.Now().Unix()
- m := make(map[any]any)
- s.mi.Lock()
- for key, item := range s.items {
- if item.expire < nowTS {
- delete(s.items, key)
- m[key] = item.value
- }
- }
- s.mi.Unlock()
- if len(m) > 0 {
- s.eventClean(m)
- }
- case <-s.ctx.Done():
- break loop
- }
- }
- }
- func (s *CacheMap) delete(key any) {
- delete(s.items, key)
- }
- // Delete removes cached object
- func (s *CacheMap) Delete(key any) {
- s.mi.Lock()
- s.delete(key)
- s.mi.Unlock()
- }
- func (s *CacheMap) set(key, value any) {
- s.items[key] = &item{
- value: value,
- expire: time.Now().Add(s.live).UnixNano(),
- }
- }
- func (s *CacheMap) keysOlder(count int) []any {
- if count > len(s.items) {
- count = len(s.items)
- }
- last, llast := time.Now().Unix(), int64(-1)
- res := make([]any, 0, count)
- for len(res) < count {
- var rKey any
- ltmp := last
- for key, val := range s.items {
- if val.expire < ltmp && val.expire > llast {
- rKey = key
- ltmp = val.expire
- }
- }
- res = append(res, rKey)
- llast = ltmp
- }
- return res
- }
- func (s *CacheMap) Set(key, value any) {
- s.mi.Lock()
- if s.maxSize > 0 && len(s.items) == s.maxSize {
- // remove first
- for key, val := range s.items {
- delete(s.items, key)
- s.eventClean(map[any]any{
- key: val.value,
- })
- break
- }
- }
- s.set(key, value)
- s.mi.Unlock()
- }
- func (s *CacheMap) SetMulti(m map[any]any) {
- s.mi.Lock()
- /*
- if s.maxSize > 0 && len(s.items)+len(m) > s.maxSize {
- // remove first
- for key, val := range s.items {
- delete(s.items, key)
- s.eventClean(map[any]any{
- key: val.value,
- })
- break
- }
- }
- */
- for key, val := range m {
- s.set(key, val)
- }
- s.mi.Unlock()
- }
- func (s *CacheMap) DeleteMulti(keys []any) {
- s.mi.Lock()
- for _, key := range keys {
- delete(s.items, key)
- }
- s.mi.Unlock()
- }
- func (s *CacheMap) get(key any) (res any, check bool) {
- var it *item
- if it, check = s.items[key]; check {
- res = it.value
- }
- return
- }
- func (s *CacheMap) Get(key any) (res any, check bool) {
- s.mi.RLock()
- res, check = s.get(key)
- s.mi.RUnlock()
- return
- }
- func (s *CacheMap) GetCreate(key any, mCreate CallCreate) (val any, check bool) {
- if val, check = s.Get(key); !check {
- s.mi.Lock()
- defer s.mi.Unlock()
- if val, check = s.get(key); check {
- return
- }
- if val, check = mCreate(key); check {
- s.set(key, val)
- }
- }
- return
- }
- func (s *CacheMap) GetCreateNew(key any, mCreateNew CallCreateNew) (rKey, val any, check bool) {
- rKey = key
- if val, check = s.Get(key); !check {
- s.mi.Lock()
- defer s.mi.Unlock()
- if val, check = s.get(key); check {
- return
- }
- if rKey, val, check = mCreateNew(key); check {
- s.set(rKey, val)
- }
- }
- return
- }
- func (s *CacheMap) GetCheck(key any, mCheck CallCheck) (res any, check bool) {
- s.mi.Lock()
- res, check = s.get(key)
- if rKey, rVal, rCheck := mCheck(key, res, check); rCheck {
- s.set(rKey, rVal)
- key, res, check = rKey, rVal, true
- }
- s.mi.Unlock()
- return
- }
- // Each implements a map bypass for each key using the callback function. If the callback function returns false, then the cycle stops
- func (s *CacheMap) Each(callback func(any, any) bool) {
- s.mi.RLock()
- defer s.mi.RUnlock()
- for key, val := range s.items {
- if !callback(key, val.value) {
- return
- }
- }
- }
- func (s CacheMap) Len() (res int) {
- s.mi.RLock()
- res = len(s.items)
- s.mi.RUnlock()
- return
- }
- func (s *CacheMap) Keys() (res []any) {
- s.mi.RLock()
- res = make([]any, 0, len(s.items))
- for key := range s.items {
- res = append(res, key)
- }
- s.mi.RUnlock()
- return
- }
- func (s *CacheMap) Clear() {
- s.mi.Lock()
- s.items = make(map[any]*item)
- s.mi.Unlock()
- }
|