keel/internal/k8s/cache.go

116 lines
2.9 KiB
Go

package k8s
import (
"sort"
"sync"
)
type genericResourceCache struct {
sync.Mutex
values []*GenericResource
}
// GenericResourceCache - storage for generic resources with a rendezvous point for goroutines
// waiting for or announcing the occurence of a cache events.
type GenericResourceCache struct {
genericResourceCache
Cond
}
// Values returns a copy of the contents of the cache.
func (cc *genericResourceCache) Values() []*GenericResource {
cc.Lock()
r := []*GenericResource{}
for _, v := range cc.values {
r = append(r, v.DeepCopy())
}
cc.Unlock()
return r
}
// Add adds an entry to the cache. If a GenericResource with the same
// name exists, it is replaced.
func (cc *genericResourceCache) Add(grs ...*GenericResource) {
if len(grs) == 0 {
return
}
cc.Lock()
sort.Sort(genericResource(cc.values))
for _, gr := range grs {
cc.add(gr)
}
cc.Unlock()
}
// add adds c to the cache. If c is already present, the cached value of c is overwritten.
// invariant: cc.values should be sorted on entry.
func (cc *genericResourceCache) add(c *GenericResource) {
i := sort.Search(len(cc.values), func(i int) bool { return cc.values[i].Identifier >= c.Identifier })
if i < len(cc.values) && cc.values[i].Identifier == c.Identifier {
// c is already present, replace
cc.values[i] = c
} else {
// c is not present, append
cc.values = append(cc.values, c)
// restort to convert append into insert
sort.Sort(genericResource(cc.values))
}
}
// Remove removes the named entry from the cache. If the entry
// is not present in the cache, the operation is a no-op.
func (cc *genericResourceCache) Remove(identifiers ...string) {
if len(identifiers) == 0 {
return
}
cc.Lock()
sort.Sort(genericResource(cc.values))
for _, n := range identifiers {
cc.remove(n)
}
cc.Unlock()
}
// remove removes the named entry from the cache.
// invariant: cc.values should be sorted on entry.
func (cc *genericResourceCache) remove(identifier string) {
i := sort.Search(len(cc.values), func(i int) bool { return cc.values[i].Identifier >= identifier })
if i < len(cc.values) && cc.values[i].Identifier == identifier {
// c is present, remove
cc.values = append(cc.values[:i], cc.values[i+1:]...)
}
}
// Cond implements a condition variable, a rendezvous point for goroutines
// waiting for or announcing the occurence of an event.
type Cond struct {
mu sync.Mutex
waiters []chan int
last int
}
// Register registers ch to receive a value when Notify is called.
func (c *Cond) Register(ch chan int, last int) {
c.mu.Lock()
defer c.mu.Unlock()
if last < c.last {
// notify this channel immediately
ch <- c.last
return
}
c.waiters = append(c.waiters, ch)
}
// Notify notifies all registered waiters that an event has occured.
func (c *Cond) Notify() {
c.mu.Lock()
defer c.mu.Unlock()
c.last++
for _, ch := range c.waiters {
ch <- c.last
}
c.waiters = c.waiters[:0]
}