-
-
Save hxzhouh/1945d4a1e5a6567f084628d60b63f125 to your computer and use it in GitHub Desktop.
weak cache
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import ( | |
"container/list" | |
"fmt" | |
"runtime" | |
"sync" | |
"time" | |
"weak" | |
) | |
type CacheItem struct { | |
key string | |
value any | |
} | |
type WeakCache struct { | |
cache map[string]weak.Pointer[list.Element] // Use weak references to store values | |
mu sync.Mutex | |
storage Storage | |
} | |
// Storage is a fixed-length cache based on doubly linked tables and weak | |
type Storage struct { | |
capacity int // Maximum size of the cache | |
list *list.List | |
} | |
// NewWeakCache creates a fixed-length weak reference cache. | |
func NewWeakCache(capacity int) *WeakCache { | |
return &WeakCache{ | |
cache: make(map[string]weak.Pointer[list.Element]), | |
storage: Storage{capacity: capacity, list: list.New()}, | |
} | |
} | |
// Set adds or updates cache entries | |
func (c *WeakCache) Set(key string, value any) { | |
// If the element already exists, update the value and move it to the head of the chain table | |
if elem, exists := c.cache[key]; exists { | |
if elemValue := elem.Value(); elemValue != nil { | |
elemValue.Value = &CacheItem{key: key, value: value} | |
c.storage.list.MoveToFront(elemValue) | |
elemWeak := weak.Make(elemValue) | |
c.cache[key] = elemWeak | |
return | |
} else { | |
c.removeElement(key) | |
} | |
} | |
// remove the oldest unused element if capacity is full | |
if c.storage.list.Len() >= c.storage.capacity { | |
c.evict() | |
} | |
// Add new element | |
elem := c.storage.list.PushFront(&CacheItem{key: key, value: value}) | |
elemWeak := weak.Make(elem) | |
c.cache[key] = elemWeak | |
} | |
// Get gets the value of the cached item | |
func (c *WeakCache) Get(key string) (any, bool) { | |
if elem, exists := c.cache[key]; exists { | |
// Check if the weak reference is still valid | |
if elemValue := elem.Value(); elemValue != nil { | |
// Moving to the head of the chain indicates the most recent visit | |
c.storage.list.MoveToFront(elemValue) | |
return elemValue.Value.(*CacheItem).value, true | |
} else { | |
c.removeElement(key) | |
} | |
} | |
return nil, false | |
} | |
// evict removes the cache item that has not been used for the longest time | |
func (c *WeakCache) evict() { | |
if elem := c.storage.list.Back(); elem != nil { | |
item := elem.Value.(*CacheItem) | |
c.removeElement(item.key) | |
} | |
} | |
// removeElement removes elements from chains and dictionaries. | |
func (c *WeakCache) removeElement(key string) { | |
c.mu.Lock() | |
defer c.mu.Unlock() | |
if elem, exists := c.cache[key]; exists { | |
// Check if the weak reference is still valid | |
if elemValue := elem.Value(); elemValue != nil { | |
c.storage.list.Remove(elemValue) | |
} | |
delete(c.cache, key) | |
} | |
} | |
// Debug prints the contents of the cache | |
func (c *WeakCache) Debug() { | |
fmt.Println("Cache content:") | |
for k, v := range c.cache { | |
if v.Value() != nil { | |
fmt.Printf("Key: %s, Value: %v\n", k, v.Value().Value.(*CacheItem).value) | |
} | |
} | |
} | |
func (c *WeakCache) CleanCache() { | |
c.storage.list.Init() | |
c.storage.capacity = 0 | |
} | |
func main() { | |
cache := NewWeakCache(3) | |
cache.Set("a", "value1") | |
cache.Set("b", "value2") | |
cache.Set("c", "value3") | |
runtime.GC() | |
time.Sleep(1 * time.Millisecond) | |
cache.Debug() | |
// Access "a" to make it the most recently used | |
_, _ = cache.Get("a") | |
runtime.GC() | |
time.Sleep(1 * time.Millisecond) | |
cache.Debug() | |
// Add new element "d", triggering the elimination of the oldest unused "b". | |
cache.Set("d", "value4") | |
runtime.GC() | |
time.Sleep(1 * time.Millisecond) | |
cache.Debug() | |
cache.CleanCache() | |
runtime.GC() | |
time.Sleep(1 * time.Millisecond) | |
cache.Debug() | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment