A simple TicTacToe app with Golang backend and WebSockets gluing it all together.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

144 lines
2.9 KiB

package cache
import (
"runtime"
"time"
)
// NewCache returns a simple in-memory cache
func NewCache(defaultExp, cleanupInterval time.Duration) *Cache {
items := make(map[string]Item)
return newCacheWithJanitor(defaultExp, cleanupInterval, items)
}
func newCacheWithJanitor(defaultExp, cleanupInterval time.Duration, items map[string]Item) *Cache {
cache := newCache(defaultExp, items)
Cache := &Cache{cache}
if cleanupInterval > 0 {
runJanitor(cache, cleanupInterval)
runtime.SetFinalizer(Cache, stopJanitor)
}
return Cache
}
func newCache(de time.Duration, m map[string]Item) *cache {
if de == 0 {
de = -1
}
c := &cache{
defaultExpiration: de,
items: m,
}
return c
}
// Add an item to the cache, replacing any existing item. If the duration is 0
// (DefaultExpiration), the cache's default expiration time is used. If it is -1
// (NoExpiration), the item never expires.
func (c *cache) Set(k string, x Series, d time.Duration) {
// "Inlining" of set
var e int64
if d == DefaultExpiration {
d = c.defaultExpiration
}
if d > 0 {
e = time.Now().Add(d).UnixNano()
}
c.mu.Lock()
c.items[k] = Item{
Series: x,
Expiration: e,
}
// TODO: Calls to mu.Unlock are currently not deferred because defer
// adds ~200 ns (as of go1.)
c.mu.Unlock()
}
func (c *cache) set(k string, x Series, d time.Duration) {
var e int64
if d == DefaultExpiration {
d = c.defaultExpiration
}
if d > 0 {
e = time.Now().Add(d).UnixNano()
}
c.items[k] = Item{
Series: x,
Expiration: e,
}
}
// Add an item to the cache, replacing any existing item, using the default
// expiration.
func (c *cache) SetDefault(k string, x Series) {
c.Set(k, x, DefaultExpiration)
}
// Get an item from the cache. Returns the item or nil, and a bool indicating
// whether the key was found.
func (c *cache) Get(k string) (Series, bool) {
c.mu.RLock()
// "Inlining" of get and Expired
item, found := c.items[k]
if !found {
c.mu.RUnlock()
return item.Series, false
}
if item.Expiration > 0 {
if time.Now().UnixNano() > item.Expiration {
c.mu.RUnlock()
return item.Series, false
}
}
c.mu.RUnlock()
return item.Series, true
}
type keyAndValue struct {
key string
value interface{}
}
// Delete all expired items from the cache.
func (c *cache) DeleteExpired() {
now := time.Now().UnixNano()
c.mu.Lock()
for k, v := range c.items {
// "Inlining" of expired
if v.Expiration > 0 && now > v.Expiration {
delete(c.items, k)
}
}
c.mu.Unlock()
}
type janitor struct {
Interval time.Duration
stop chan bool
}
func (j *janitor) Run(c *cache) {
ticker := time.NewTicker(j.Interval)
for {
select {
case <-ticker.C:
c.DeleteExpired()
case <-j.stop:
ticker.Stop()
return
}
}
}
func stopJanitor(c *Cache) {
c.janitor.stop <- true
}
func runJanitor(c *cache, ci time.Duration) {
j := &janitor{
Interval: ci,
stop: make(chan bool),
}
c.janitor = j
go j.Run(c)
}