@@ -3,6 +3,7 @@ package bdcache
33import (
44 "container/list"
55 "sync"
6+ "sync/atomic"
67 "time"
78)
89
@@ -44,8 +45,8 @@ type entry[K comparable, V any] struct {
4445 key K
4546 value V
4647 element * list.Element
47- freq int // Access frequency counter
48- inSmall bool // True if in Small queue, false if in Main
48+ freq atomic. Int32 // Access frequency counter (atomic for lock-free reads)
49+ inSmall bool // True if in Small queue, false if in Main
4950}
5051
5152// newS3FIFO creates a new S3-FIFO cache with the given capacity.
@@ -77,26 +78,31 @@ func newS3FIFO[K comparable, V any](capacity int) *s3fifo[K, V] {
7778// get retrieves a value from the cache.
7879// On hit, increments frequency counter (used during eviction).
7980func (c * s3fifo [K , V ]) get (key K ) (V , bool ) {
80- c .mu .Lock ()
81- defer c .mu .Unlock ()
82-
81+ c .mu .RLock ()
8382 ent , ok := c .items [key ]
8483 if ! ok {
84+ c .mu .RUnlock ()
8585 var zero V
8686 return zero , false
8787 }
8888
89- // Check expiration
89+ // Fast path: check expiration while holding read lock
90+ // This is safe because we're only reading ent.expiry
9091 if ! ent .expiry .IsZero () && time .Now ().After (ent .expiry ) {
92+ c .mu .RUnlock ()
9193 var zero V
9294 return zero , false
9395 }
9496
97+ val := ent .value
98+ c .mu .RUnlock ()
99+
95100 // S3-FIFO: Increment frequency on access (lazy promotion)
96101 // Items are promoted during eviction, not on access
97- ent .freq ++
102+ // Use atomic increment to avoid lock contention on hot path
103+ ent .freq .Add (1 )
98104
99- return ent . value , true
105+ return val , true
100106}
101107
102108// set adds or updates a value in the cache.
@@ -108,7 +114,7 @@ func (c *s3fifo[K, V]) set(key K, value V, expiry time.Time) {
108114 if ent , ok := c .items [key ]; ok {
109115 ent .value = value
110116 ent .expiry = expiry
111- ent .freq ++
117+ ent .freq . Add ( 1 )
112118 return
113119 }
114120
@@ -126,9 +132,9 @@ func (c *s3fifo[K, V]) set(key K, value V, expiry time.Time) {
126132 key : key ,
127133 value : value ,
128134 expiry : expiry ,
129- freq : 0 ,
130135 inSmall : ! inGhost ,
131136 }
137+ // freq starts at 0 (atomic.Int32 zero value)
132138
133139 // S3-FIFO: Make room if at total capacity
134140 for len (c .items ) >= c .capacity {
@@ -193,15 +199,15 @@ func (c *s3fifo[K, V]) evictFromSmall() {
193199 c .small .Remove (elem )
194200
195201 // One-hit wonder: never accessed since insertion
196- if ent .freq == 0 {
202+ if ent .freq . Load () == 0 {
197203 // Evict and track in ghost queue
198204 delete (c .items , ent .key )
199205 c .addToGhost (ent .key )
200206 return
201207 }
202208
203209 // Hot item: promote to Main queue
204- ent .freq = 0 // Reset frequency
210+ ent .freq . Store ( 0 ) // Reset frequency
205211 ent .inSmall = false
206212 ent .element = c .main .PushBack (ent )
207213 }
@@ -223,7 +229,7 @@ func (c *s3fifo[K, V]) evictFromMain() {
223229 c .main .Remove (elem )
224230
225231 // Not accessed recently: evict
226- if ent .freq == 0 {
232+ if ent .freq . Load () == 0 {
227233 delete (c .items , ent .key )
228234 // Note: Don't add to ghost - item was already added when evicted from Small
229235 // Only Small queue evictions go to ghost
@@ -232,7 +238,7 @@ func (c *s3fifo[K, V]) evictFromMain() {
232238
233239 // Accessed recently: lazy promotion (FIFO-Reinsertion / Second Chance)
234240 // Move to back of Main queue and decrement frequency
235- ent .freq --
241+ ent .freq . Add ( - 1 )
236242 ent .element = c .main .PushBack (ent )
237243 }
238244}
0 commit comments