Skip to content

Commit

Permalink
[#59] Add node states
Browse files Browse the repository at this point in the history
  • Loading branch information
maypok86 committed Mar 3, 2024
1 parent 758caa3 commit 4ac2bcf
Show file tree
Hide file tree
Showing 10 changed files with 106 additions and 19 deletions.
25 changes: 23 additions & 2 deletions cmd/generator/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -134,8 +134,10 @@ func newGenerator(nodeType string) *generator {
func (g *generator) printImports() {
g.p("import (")
g.in()
g.p("\"sync/atomic\"")
g.p("\"unsafe\"")
if g.features[expiration] {
g.p("")
g.p("\"github.com/maypok86/otter/internal/unixtime\"")
}
g.out()
Expand Down Expand Up @@ -178,6 +180,7 @@ func (g *generator) printStruct() {
g.p("cost uint32")
}

g.p("state uint32")
g.p("frequency uint8")
g.p("queueType uint8")
g.out()
Expand All @@ -199,6 +202,7 @@ func (g *generator) printConstructors() {
if g.features[cost] {
g.p("cost: cost,")
}
g.p("state: aliveState,")
g.out()
g.p("}")
g.out()
Expand Down Expand Up @@ -365,6 +369,14 @@ func (g *generator) printFunctions() {
g.p("}")

const otherFunctions = `
func (n *%s[K, V]) IsAlive() bool {
return atomic.LoadUint32(&n.state) == aliveState
}
func (n *%s[K, V]) Die() {
atomic.StoreUint32(&n.state, deadState)
}
func (n *%s[K, V]) Frequency() uint8 {
return n.frequency
}
Expand Down Expand Up @@ -460,6 +472,11 @@ const (
maxFrequency uint8 = 3
)
const (
aliveState uint32 = iota
deadState
)
// Node is a cache entry.
type Node[K comparable, V any] interface {
// Key returns the key.
Expand Down Expand Up @@ -490,6 +507,10 @@ type Node[K comparable, V any] interface {
Expiration() uint32
// Cost returns the cost of the node.
Cost() uint32
// IsAlive returns true if the entry is available in the hash-table.
IsAlive() bool
// Die sets the node to the dead state.
Die()
// Frequency returns the frequency of the node.
Frequency() uint8
// IncrementFrequency increments the frequency of the node.
Expand Down Expand Up @@ -522,11 +543,11 @@ func Equals[K comparable, V any](a, b Node[K, V]) bool {
type Config struct {
WithExpiration bool
WithCost bool
WithCost bool
}
type Manager[K comparable, V any] struct {
create func(key K, value V, expiration, cost uint32) Node[K, V]
create func(key K, value V, expiration, cost uint32) Node[K, V]
fromPointer func(ptr unsafe.Pointer) Node[K, V]
}
Expand Down
31 changes: 20 additions & 11 deletions internal/core/cache.go
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ func (c *Cache[K, V]) Has(key K) bool {
// Get returns the value associated with the key in this cache.
func (c *Cache[K, V]) Get(key K) (V, bool) {
got, ok := c.hashmap.Get(key)
if !ok {
if !ok || !got.IsAlive() {
c.stats.IncMisses()
return zeroValue[V](), false
}
Expand Down Expand Up @@ -249,6 +249,7 @@ func (c *Cache[K, V]) set(key K, value V, expiration uint32, onlyIfAbsent bool)
evicted := c.hashmap.Set(n)
if evicted != nil {
// update
evicted.Die()
c.writeBuffer.Insert(task.NewUpdateTask(n, evicted))
} else {
// insert
Expand All @@ -260,23 +261,24 @@ func (c *Cache[K, V]) set(key K, value V, expiration uint32, onlyIfAbsent bool)

// Delete removes the association for this key from the cache.
func (c *Cache[K, V]) Delete(key K) {
deleted := c.hashmap.Delete(key)
if deleted != nil {
c.writeBuffer.Insert(task.NewDeleteTask(deleted))
}
c.afterDelete(c.hashmap.Delete(key))
}

func (c *Cache[K, V]) deleteNode(n node.Node[K, V]) {
deleted := c.hashmap.DeleteNode(n)
c.afterDelete(c.hashmap.DeleteNode(n))
}

func (c *Cache[K, V]) afterDelete(deleted node.Node[K, V]) {
if deleted != nil {
deleted.Die()
c.writeBuffer.Insert(task.NewDeleteTask(deleted))
}
}

// DeleteByFunc removes the association for this key from the cache when the given function returns true.
func (c *Cache[K, V]) DeleteByFunc(f func(key K, value V) bool) {
c.hashmap.Range(func(n node.Node[K, V]) bool {
if n.IsExpired() {
if !n.IsAlive() || n.IsExpired() {
return true
}

Expand Down Expand Up @@ -305,6 +307,7 @@ func (c *Cache[K, V]) cleanup() {

for _, n := range e {
c.hashmap.DeleteNode(n)
n.Die()
}

expired = clearBuffer(expired)
Expand Down Expand Up @@ -346,14 +349,19 @@ func (c *Cache[K, V]) process() {
c.evictionMutex.Lock()

for _, t := range buffer {
n := t.Node()
switch {
case t.IsDelete():
c.expirePolicy.Delete(t.Node())
c.expirePolicy.Delete(n)
case t.IsAdd():
c.expirePolicy.Add(t.Node())
if n.IsAlive() {
c.expirePolicy.Add(n)
}
case t.IsUpdate():
c.expirePolicy.Delete(t.OldNode())
c.expirePolicy.Add(t.Node())
if n.IsAlive() {
c.expirePolicy.Add(n)
}
}
}

Expand All @@ -366,6 +374,7 @@ func (c *Cache[K, V]) process() {

for _, n := range d {
c.hashmap.DeleteNode(n)
n.Die()
}

buffer = clearBuffer(buffer)
Expand All @@ -379,7 +388,7 @@ func (c *Cache[K, V]) process() {
// Iteration stops early when the given function returns false.
func (c *Cache[K, V]) Range(f func(key K, value V) bool) {
c.hashmap.Range(func(n node.Node[K, V]) bool {
if n.IsExpired() {
if !n.IsAlive() || n.IsExpired() {
return true
}

Expand Down
11 changes: 11 additions & 0 deletions internal/generated/node/b.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

11 changes: 11 additions & 0 deletions internal/generated/node/bc.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

14 changes: 13 additions & 1 deletion internal/generated/node/be.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

14 changes: 13 additions & 1 deletion internal/generated/node/bec.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 9 additions & 0 deletions internal/generated/node/manager.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion internal/s3fifo/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ func (m *main[K, V]) evict(deleted []node.Node[K, V]) []node.Node[K, V] {
for m.cost > 0 {
n := m.q.pop()

if n.IsExpired() || n.Frequency() == 0 {
if !n.IsAlive() || n.IsExpired() || n.Frequency() == 0 {
n.Unmark()
m.cost -= n.Cost()
return append(deleted, n)
Expand Down
6 changes: 4 additions & 2 deletions internal/s3fifo/policy.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ func (p *Policy[K, V]) Write(

// already deleted in map
if t.IsDelete() {
p.delete(t.Node())
p.delete(n)
continue
}

Expand All @@ -103,7 +103,9 @@ func (p *Policy[K, V]) Write(
}

// add
deleted = p.insert(deleted, n)
if n.IsAlive() {
deleted = p.insert(deleted, n)
}
}
return deleted
}
Expand Down
2 changes: 1 addition & 1 deletion internal/s3fifo/small.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ func (s *small[K, V]) evict(deleted []node.Node[K, V]) []node.Node[K, V] {
n := s.q.pop()
s.cost -= n.Cost()
n.Unmark()
if n.IsExpired() {
if !n.IsAlive() || n.IsExpired() {
return append(deleted, n)
}

Expand Down

0 comments on commit 4ac2bcf

Please sign in to comment.