forked from matrixorigin/mocache
-
Notifications
You must be signed in to change notification settings - Fork 0
/
entry.go
155 lines (138 loc) · 3.32 KB
/
entry.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
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
// Copyright 2020 The LevelDB-Go and Pebble Authors. All rights reserved. Use
// of this source code is governed by a BSD-style license that can be found in
// the LICENSE file.
package mocache
import "sync/atomic"
type entryType int8
const (
etTest entryType = iota
etCold
etHot
)
func (p entryType) String() string {
switch p {
case etTest:
return "test"
case etCold:
return "cold"
case etHot:
return "hot"
}
return "unknown"
}
// entry holds the metadata for a cache entry. The memory for an entry is
// allocated from manually managed memory.
//
// Using manual memory management for entries is technically a volation of the
// Cgo pointer rules:
//
// https://golang.org/cmd/cgo/#hdr-Passing_pointers
//
// Specifically, Go pointers should not be stored in C allocated memory. The
// reason for this rule is that the Go GC will not look at C allocated memory
// to find pointers to Go objects. If the only reference to a Go object is
// stored in C allocated memory, the object will be reclaimed. The shard field
// of the entry struct points to a Go allocated object, thus the
// violation. What makes this "safe" is that the Cache guarantees that there
// are other pointers to the shard which will keep it alive.
type entry struct {
key key
// The value associated with the entry. The entry holds a reference on the
// value which is maintained by entry.setValue().
val *Value
blockLink struct {
next *entry
prev *entry
}
fileLink struct {
next *entry
prev *entry
}
size int64
ptype entryType
// referenced is atomically set to indicate that this entry has been accessed
// since the last time one of the clock hands swept it.
referenced atomic.Bool
shard *shard
// Reference count for the entry. The entry is freed when the reference count
// drops to zero.
ref refcnt
}
func newEntry(s *shard, key key, size int64) *entry {
e := entryAllocNew()
*e = entry{
key: key,
size: size,
ptype: etCold,
shard: s,
}
e.blockLink.next = e
e.blockLink.prev = e
e.fileLink.next = e
e.fileLink.prev = e
e.ref.init(1)
return e
}
func (e *entry) free() {
e.setValue(nil)
*e = entry{}
entryAllocFree(e)
}
func (e *entry) next() *entry {
if e == nil {
return nil
}
return e.blockLink.next
}
func (e *entry) prev() *entry {
if e == nil {
return nil
}
return e.blockLink.prev
}
func (e *entry) link(s *entry) {
s.blockLink.prev = e.blockLink.prev
s.blockLink.prev.blockLink.next = s
s.blockLink.next = e
s.blockLink.next.blockLink.prev = s
}
func (e *entry) unlink() *entry {
next := e.blockLink.next
e.blockLink.prev.blockLink.next = e.blockLink.next
e.blockLink.next.blockLink.prev = e.blockLink.prev
e.blockLink.prev = e
e.blockLink.next = e
return next
}
func (e *entry) linkFile(s *entry) {
s.fileLink.prev = e.fileLink.prev
s.fileLink.prev.fileLink.next = s
s.fileLink.next = e
s.fileLink.next.fileLink.prev = s
}
func (e *entry) unlinkFile() *entry {
next := e.fileLink.next
e.fileLink.prev.fileLink.next = e.fileLink.next
e.fileLink.next.fileLink.prev = e.fileLink.prev
e.fileLink.prev = e
e.fileLink.next = e
return next
}
func (e *entry) setValue(v *Value) {
if v != nil {
v.acquire()
}
old := e.val
e.val = v
old.release()
}
func (e *entry) peekValue() *Value {
return e.val
}
func (e *entry) acquireValue() *Value {
v := e.val
if v != nil {
v.acquire()
}
return v
}