-
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathclock.go
111 lines (95 loc) · 2.94 KB
/
clock.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
package eviction
// The clock eviction algorithm is a page replacement algorithm that uses a clock-like data structure to keep track of which pages in a computer's memory have been used recently and which have not.
// It works by maintaining a circular buffer of pages, with a "hand" that points to the next page to be replaced.
// When a page needs to be evicted from memory, the hand is advanced to the next page in the buffer, and that page is evicted if it has not been used recently.
// If the page has been used recently, the hand is advanced to the next page, and the process repeats until a page is found that can be evicted.
// The clock eviction algorithm is often used in virtual memory systems to manage the allocation of physical memory.
import (
"sync"
"github.com/hyp3rd/hypercache/errors"
"github.com/hyp3rd/hypercache/types"
)
// ClockAlgorithm is an in-memory cache with the Clock algorithm.
type ClockAlgorithm struct {
items []*types.Item
keys map[string]int
mutex sync.RWMutex
evictMutex sync.Mutex
capacity int
hand int
}
// NewClockAlgorithm creates a new in-memory cache with the given capacity and the Clock algorithm.
func NewClockAlgorithm(capacity int) (*ClockAlgorithm, error) {
if capacity < 0 {
return nil, errors.ErrInvalidCapacity
}
return &ClockAlgorithm{
items: make([]*types.Item, capacity),
keys: make(map[string]int, capacity),
capacity: capacity,
hand: 0,
}, nil
}
// Evict evicts an item from the cache based on the Clock algorithm.
func (c *ClockAlgorithm) Evict() (string, bool) {
c.evictMutex.Lock()
defer c.evictMutex.Unlock()
for i := 0; i < c.capacity; i++ {
item := c.items[c.hand]
if item == nil {
c.hand = (c.hand + 1) % c.capacity
continue
}
if item.AccessCount > 0 {
item.AccessCount--
} else {
delete(c.keys, item.Key)
types.ItemPool.Put(item)
c.items[c.hand] = nil
return item.Key, true
}
c.hand = (c.hand + 1) % c.capacity
}
return "", false
}
// Set sets the item with the given key and value in the cache.
func (c *ClockAlgorithm) Set(key string, value any) {
c.mutex.Lock()
defer c.mutex.Unlock()
evictedKey, ok := c.Evict()
if ok {
c.Delete(evictedKey)
}
item := types.ItemPool.Get().(*types.Item)
item.Key = key
item.Value = value
item.AccessCount = 1
c.keys[key] = c.hand
c.items[c.hand] = item
c.hand = (c.hand + 1) % c.capacity
}
// Get retrieves the item with the given key from the cache.
func (c *ClockAlgorithm) Get(key string) (any, bool) {
c.mutex.Lock()
defer c.mutex.Unlock()
index, ok := c.keys[key]
if !ok {
return nil, false
}
item := c.items[index]
item.AccessCount++
return item.Value, true
}
// Delete deletes the item with the given key from the cache.
func (c *ClockAlgorithm) Delete(key string) {
c.evictMutex.Lock()
defer c.evictMutex.Unlock()
index, ok := c.keys[key]
if !ok {
return
}
item := c.items[index]
delete(c.keys, key)
c.items[index] = nil
types.ItemPool.Put(item)
}