Skip to content

Commit 56f903d

Browse files
authored
Use go 1.24 (#82)
1. New Cache and CacheWithRenew 2. Move some packages location
1 parent 4a5fe14 commit 56f903d

21 files changed

+319
-228
lines changed

cache/cache.go

+23-113
Original file line numberDiff line numberDiff line change
@@ -1,138 +1,48 @@
11
package cache
22

33
import (
4-
"context"
5-
"log"
6-
"sync"
7-
"time"
8-
)
9-
10-
var valueKey int
11-
12-
type item[T any] struct {
13-
sync.Mutex
14-
ctx context.Context
15-
cancel context.CancelFunc
16-
lifecycle time.Duration
17-
fn func() (T, error)
18-
}
19-
20-
func (i *item[T]) set(value *T) {
21-
if i.ctx = context.WithValue(context.Background(), &valueKey, value); i.lifecycle > 0 {
22-
i.ctx, i.cancel = context.WithTimeout(i.ctx, i.lifecycle)
23-
}
24-
}
4+
"runtime"
5+
"weak"
256

26-
func (i *item[T]) value() T {
27-
return *i.ctx.Value(&valueKey).(*T)
28-
}
29-
30-
func (i *item[T]) renew() T {
31-
v, err := i.fn()
32-
i.Lock()
33-
defer i.Unlock()
34-
if err != nil {
35-
log.Print(err)
36-
v = i.value()
37-
}
38-
i.set(&v)
39-
return v
40-
}
7+
"github.com/sunshineplan/utils/container"
8+
)
419

4210
// Cache is cache struct.
43-
type Cache[Key, Value any] struct {
44-
m Map[Key, *item[Value]]
45-
autoRenew bool
11+
type Cache[Key any, Value any] struct {
12+
m container.Map[weak.Pointer[Key], Value]
4613
}
4714

4815
// New creates a new cache with auto clean or not.
49-
func New[Key, Value any](autoRenew bool) *Cache[Key, Value] {
50-
return &Cache[Key, Value]{autoRenew: autoRenew}
51-
}
52-
53-
// Set sets cache value for a key, if fn is presented, this value will regenerate when expired.
54-
func (c *Cache[Key, Value]) Set(key Key, value Value, lifecycle time.Duration, fn func() (Value, error)) {
55-
i := &item[Value]{lifecycle: lifecycle, fn: fn}
56-
i.Lock()
57-
defer i.Unlock()
58-
i.set(&value)
59-
if c.autoRenew && lifecycle > 0 {
60-
go func() {
61-
for {
62-
<-i.ctx.Done()
63-
if i.ctx.Err() == context.DeadlineExceeded {
64-
if i.fn != nil {
65-
i.renew()
66-
continue
67-
} else {
68-
c.Delete(key)
69-
}
70-
}
71-
return
72-
}
73-
}()
74-
}
75-
c.m.Store(key, i)
16+
func New[Key comparable, Value any]() *Cache[Key, Value] {
17+
return &Cache[Key, Value]{}
7618
}
7719

78-
func (c *Cache[Key, Value]) get(key Key) (i *item[Value], ok bool) {
79-
if i, ok = c.m.Load(key); !ok {
80-
return
81-
}
82-
if !c.autoRenew && i.ctx.Err() == context.DeadlineExceeded {
83-
if i.fn == nil {
84-
c.Delete(key)
85-
return nil, false
86-
}
87-
i.renew()
88-
}
89-
return
20+
// Set sets cache value for a key.
21+
func (c *Cache[Key, Value]) Set(key *Key, value Value) {
22+
p := weak.Make(key)
23+
c.m.Store(p, value)
24+
runtime.AddCleanup(key, func(p weak.Pointer[Key]) { c.m.Delete(p) }, p)
9025
}
9126

9227
// Get gets cache value by key and whether value was found.
93-
func (c *Cache[Key, Value]) Get(key Key) (value Value, ok bool) {
94-
var i *item[Value]
95-
if i, ok = c.get(key); !ok {
96-
return
97-
}
98-
i.Lock()
99-
defer i.Unlock()
100-
value = i.value()
101-
return
28+
func (c *Cache[Key, Value]) Get(key *Key) (value Value, ok bool) {
29+
p := weak.Make(key)
30+
return c.m.Load(p)
10231
}
10332

10433
// Delete deletes the value for a key.
105-
func (c *Cache[Key, Value]) Delete(key Key) {
106-
if i, ok := c.m.LoadAndDelete(key); ok {
107-
i.Lock()
108-
defer i.Unlock()
109-
if i.cancel != nil {
110-
i.cancel()
111-
}
112-
}
34+
func (c *Cache[Key, Value]) Delete(key *Key) {
35+
p := weak.Make(key)
36+
c.m.Delete(p)
11337
}
11438

11539
// Swap swaps the value for a key and returns the previous value if any. The loaded result reports whether the key was present.
116-
func (c *Cache[Key, Value]) Swap(key Key, value Value) (previous Value, loaded bool) {
117-
var i *item[Value]
118-
if i, loaded = c.get(key); loaded {
119-
i.Lock()
120-
defer i.Unlock()
121-
previous = i.value()
122-
i.set(&value)
123-
}
124-
return
40+
func (c *Cache[Key, Value]) Swap(key *Key, value Value) (previous Value, loaded bool) {
41+
p := weak.Make(key)
42+
return c.m.Swap(p, value)
12543
}
12644

12745
// Clear deletes all values in cache.
12846
func (c *Cache[Key, Value]) Clear() {
129-
c.m.Range(func(k Key, i *item[Value]) bool {
130-
c.m.Delete(k)
131-
i.Lock()
132-
defer i.Unlock()
133-
if i.cancel != nil {
134-
i.cancel()
135-
}
136-
return true
137-
})
47+
c.m.Clear()
13848
}

cache/cache_test.go

+31-77
Original file line numberDiff line numberDiff line change
@@ -1,86 +1,40 @@
11
package cache
22

33
import (
4+
"runtime"
45
"testing"
5-
"time"
6+
"weak"
67
)
78

89
func TestCache(t *testing.T) {
9-
cache := New[string, string](false)
10-
cache.Set("key", "value", 0, nil)
11-
if value, ok := cache.Get("key"); !ok {
12-
t.Fatal("expected ok; got not")
13-
} else if value != "value" {
14-
t.Errorf("expected value; got %q", value)
15-
}
16-
if value, ok := cache.Swap("key", "swap"); !ok {
17-
t.Fatal("expected ok; got not")
18-
} else if value != "value" {
19-
t.Errorf("expected value; got %q", value)
20-
}
21-
if value, ok := cache.Get("key"); !ok {
22-
t.Fatal("expected ok; got not")
23-
} else if value != "swap" {
24-
t.Errorf("expected swap; got %q", value)
25-
}
26-
cache.Delete("key")
27-
if _, ok := cache.Get("key"); ok {
28-
t.Error("expected not ok; got ok")
29-
}
30-
}
31-
32-
func TestEmpty(t *testing.T) {
33-
cache := New[string, int](false)
34-
cache.Set("a", 1, 0, nil)
35-
cache.Set("b", 2, 0, nil)
36-
cache.Set("c", 3, 0, nil)
37-
for _, i := range []string{"a", "b", "c"} {
38-
if _, ok := cache.Get(i); !ok {
39-
t.Error("expected ok; got not")
40-
}
41-
}
42-
cache.Clear()
43-
for _, i := range []string{"a", "b", "c"} {
44-
if _, ok := cache.Get(i); ok {
45-
t.Error("expected not ok; got ok")
46-
}
47-
}
48-
}
49-
50-
func TestRenew(t *testing.T) {
51-
cache := New[string, string](true)
52-
defer cache.Clear()
53-
expire := make(chan struct{})
54-
cache.Set("renew", "old", 2*time.Second, func() (string, error) {
55-
defer func() { close(expire) }()
56-
return "new", nil
57-
})
58-
cache.Set("expire", "value", 1*time.Second, nil)
59-
if value, ok := cache.Get("expire"); !ok {
60-
t.Fatal("expected ok; got not")
61-
} else if expect := "value"; value != expect {
62-
t.Errorf("expected %q; got %q", expect, value)
63-
}
64-
if value, ok := cache.Get("renew"); !ok {
65-
t.Fatal("expected ok; got not")
66-
} else if expect := "old"; value != expect {
67-
t.Errorf("expected %q; got %q", expect, value)
68-
}
69-
ticker := time.NewTicker(4 * time.Second)
70-
defer ticker.Stop()
71-
select {
72-
case <-expire:
73-
time.Sleep(100 * time.Millisecond)
74-
if _, ok := cache.Get("expire"); ok {
75-
t.Error("expected not ok; got ok")
76-
}
77-
value, ok := cache.Get("renew")
78-
if !ok {
79-
t.Fatal("expected ok; got not")
80-
} else if expect := "new"; value != expect {
81-
t.Errorf("expected %q; got %q", expect, value)
82-
}
83-
case <-ticker.C:
84-
t.Fatal("time out")
10+
cache := New[string, string]()
11+
key := "key"
12+
p := weak.Make(&key)
13+
value := "value"
14+
cache.Set(&key, value)
15+
if v, ok := cache.Get(&key); !ok {
16+
t.Fatal("expected cached, got not")
17+
} else if v != value {
18+
t.Fatalf("expected %q, got %q", value, v)
19+
}
20+
if v, ok := cache.m.Load(p); !ok {
21+
t.Fatal("expected cached, got not")
22+
} else if v != value {
23+
t.Fatalf("expected %q, got %q", value, v)
24+
}
25+
runtime.GC()
26+
if v, ok := cache.Get(&key); !ok {
27+
t.Fatal("expected cached, got not")
28+
} else if v != value {
29+
t.Fatalf("expected %q, got %q", value, v)
30+
}
31+
if v, ok := cache.m.Load(p); !ok {
32+
t.Fatal("expected cached, got not")
33+
} else if v != value {
34+
t.Fatalf("expected %q, got %q", value, v)
35+
}
36+
runtime.GC()
37+
if _, ok := cache.m.Load(p); ok {
38+
t.Fatal("expected not cached, got cached")
8539
}
8640
}

0 commit comments

Comments
 (0)