Skip to content
This repository was archived by the owner on Feb 20, 2021. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 13 additions & 5 deletions cache.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,10 @@ import (

// Cache is a synchronised map of items that auto-expire once stale
type Cache struct {
mutex sync.RWMutex
ttl time.Duration
items map[string]*Item
mutex sync.RWMutex
ttl time.Duration
items map[string]*Item
shutdown chan bool
}

// Set is a thread-safe way to add new items to the map
Expand Down Expand Up @@ -47,6 +48,10 @@ func (cache *Cache) Count() int {
return count
}

func (cache *Cache) Close() {
cache.shutdown <- true
}

func (cache *Cache) cleanup() {
cache.mutex.Lock()
for key, item := range cache.items {
Expand All @@ -66,6 +71,8 @@ func (cache *Cache) startCleanupTimer() {
go (func() {
for {
select {
case <-cache.shutdown:
return
case <-ticker:
cache.cleanup()
}
Expand All @@ -76,8 +83,9 @@ func (cache *Cache) startCleanupTimer() {
// NewCache is a helper to create instance of the Cache struct
func NewCache(duration time.Duration) *Cache {
cache := &Cache{
ttl: duration,
items: map[string]*Item{},
ttl: duration,
items: map[string]*Item{},
shutdown: make(chan bool, 1),
}
cache.startCleanupTimer()
return cache
Expand Down
29 changes: 25 additions & 4 deletions cache_test.go
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
package ttlcache

import (
"runtime"
"testing"
"time"
)

func TestGet(t *testing.T) {
cache := &Cache{
ttl: time.Second,
items: map[string]*Item{},
ttl: time.Second,
items: map[string]*Item{},
shutdown: make(chan bool, 1),
}

data, exists := cache.Get("hello")
Expand All @@ -24,12 +26,15 @@ func TestGet(t *testing.T) {
if data != "world" {
t.Errorf("Expected cache to return `world` for `hello`")
}

cache.Close()
}

func TestExpiration(t *testing.T) {
cache := &Cache{
ttl: time.Second,
items: map[string]*Item{},
ttl: time.Second,
items: map[string]*Item{},
shutdown: make(chan bool, 1),
}

cache.Set("x", "1")
Expand Down Expand Up @@ -84,4 +89,20 @@ func TestExpiration(t *testing.T) {
if count != 0 {
t.Errorf("Expected cache to be empty")
}

cache.Close()
}

func TestClose(t *testing.T) {
initial := runtime.NumGoroutine()
cache := NewCache(time.Second)
if runtime.NumGoroutine() != 1+initial {
t.Errorf("Expected the number of go routines to increase by one")
}

cache.Close()
time.Sleep(time.Second)
if runtime.NumGoroutine() != initial {
t.Errorf("Expected the number of go routines to decrease by one")
}
}