Improve code quality and thread safety (#844)

- Format Go files (gofmt) for backend/windows/notify*.go
- Add thread safety to Stopwatch with sync.Mutex
- Add comprehensive unit tests for Stopwatch with race detection
- Add godoc comments to ImageCache public APIs
- Improve IPC documentation with platform-specific socket path details
- Document portable mode TODO for future enhancement
This commit is contained in:
Gianluca Boiano
2026-02-03 11:39:47 -08:00
committed by GitHub
parent 5e5210adb3
commit 2e33b81914
6 changed files with 324 additions and 90 deletions
+31 -3
View File
@@ -18,7 +18,9 @@ type CacheItem struct {
lastAccessed int64
}
// A custom in-memory cache for images with the following eviction strategy:
// ImageCache is a thread-safe in-memory cache for images with LRU eviction.
//
// Eviction strategy:
// 1. If there are fewer than MinSize items in the cache, none will be evicted
// 2. If a new addition would make the cache exceed MaxSize, an item will be immediately evicted
// 2a. in this case, evict the LRU expired item or if none expired, the LRU item
@@ -39,14 +41,19 @@ type ImageCache struct {
cache map[string]CacheItem
}
// ErrNotFound is returned when a requested cache item does not exist.
var ErrNotFound = errors.New("item not found")
// Init initializes the cache and starts a background goroutine for periodic eviction.
// The goroutine stops when the provided context is cancelled.
func (i *ImageCache) Init(ctx context.Context, evictionInterval time.Duration) {
i.cache = make(map[string]CacheItem)
go i.periodicallyEvict(ctx, evictionInterval)
}
// holds writer lock for O(i.MaxSize) worst case
// SetWithTTL stores an image in the cache with a custom time-to-live duration.
// If the cache is at MaxSize, an item will be evicted using LRU strategy.
// Thread-safe. Holds writer lock for O(MaxSize) worst case.
func (i *ImageCache) SetWithTTL(key string, val image.Image, ttl time.Duration) {
i.mu.Lock()
defer i.mu.Unlock()
@@ -71,10 +78,14 @@ func (i *ImageCache) SetWithTTL(key string, val image.Image, ttl time.Duration)
}
}
// Set stores an image in the cache with the default TTL.
// See SetWithTTL for more details.
func (i *ImageCache) Set(key string, val image.Image) {
i.SetWithTTL(key, val, i.DefaultTTL)
}
// Has returns true if the key exists in the cache, expired or not.
// Thread-safe.
func (i *ImageCache) Has(key string) bool {
i.mu.RLock()
defer i.mu.RUnlock()
@@ -83,10 +94,17 @@ func (i *ImageCache) Has(key string) bool {
return ok
}
// Get retrieves an image from the cache and updates its last accessed time.
// Returns ErrNotFound if the key doesn't exist.
// Thread-safe.
func (i *ImageCache) Get(key string) (image.Image, error) {
return i.GetResetTTL(key, false)
}
// GetResetTTL retrieves an image and optionally resets its expiration time.
// If resetTTL is true, the expiration is reset to now + original TTL.
// Returns ErrNotFound if the key doesn't exist.
// Thread-safe.
func (i *ImageCache) GetResetTTL(key string, resetTTL bool) (image.Image, error) {
i.mu.Lock()
defer i.mu.Unlock()
@@ -102,7 +120,11 @@ func (i *ImageCache) GetResetTTL(key string, resetTTL bool) (image.Image, error)
return nil, ErrNotFound
}
// Gets the image if it exists and extends TTL to time.Now + ttl iff the image would expire before then
// GetExtendTTL retrieves an image and extends its TTL if it would expire sooner.
// The expiration time is extended to now + ttl only if the current expiration
// is earlier than that time.
// Returns ErrNotFound if the key doesn't exist.
// Thread-safe.
func (i *ImageCache) GetExtendTTL(key string, ttl time.Duration) (image.Image, error) {
i.mu.Lock()
defer i.mu.Unlock()
@@ -118,6 +140,10 @@ func (i *ImageCache) GetExtendTTL(key string, ttl time.Duration) (image.Image, e
return nil, ErrNotFound
}
// GetWithNewTTL retrieves an image and replaces its TTL with a new value.
// The expiration time is set to now + newTtl.
// Returns ErrNotFound if the key doesn't exist.
// Thread-safe.
func (i *ImageCache) GetWithNewTTL(key string, newTtl time.Duration) (image.Image, error) {
i.mu.Lock()
defer i.mu.Unlock()
@@ -132,6 +158,8 @@ func (i *ImageCache) GetWithNewTTL(key string, newTtl time.Duration) (image.Imag
return nil, ErrNotFound
}
// Clear removes all items from the cache.
// Thread-safe.
func (i *ImageCache) Clear() {
i.mu.Lock()
defer i.mu.Unlock()