65 lines
1.3 KiB
Go
65 lines
1.3 KiB
Go
|
package limiter
|
||
|
|
||
|
import "sync"
|
||
|
|
||
|
type Key = interface{}
|
||
|
|
||
|
// Manages resources with a limited number of concurrent slots for use for each key.
|
||
|
type Instance struct {
|
||
|
SlotsPerKey int
|
||
|
|
||
|
mu sync.Mutex
|
||
|
// Limits concurrent use of a resource. Push into the channel to use a slot, and receive to free
|
||
|
// up a slot.
|
||
|
active map[Key]*activeValueType
|
||
|
}
|
||
|
|
||
|
type activeValueType struct {
|
||
|
ch chan struct{}
|
||
|
refs int
|
||
|
}
|
||
|
|
||
|
type ActiveValueRef struct {
|
||
|
v *activeValueType
|
||
|
k Key
|
||
|
i *Instance
|
||
|
}
|
||
|
|
||
|
// Returns the limiting channel. Send to it to obtain a slot, and receive to release the slot.
|
||
|
func (me ActiveValueRef) C() chan struct{} {
|
||
|
return me.v.ch
|
||
|
}
|
||
|
|
||
|
// Drop the reference to a key, this allows keys to be reclaimed when they're no longer in use.
|
||
|
func (me ActiveValueRef) Drop() {
|
||
|
me.i.mu.Lock()
|
||
|
defer me.i.mu.Unlock()
|
||
|
me.v.refs--
|
||
|
if me.v.refs == 0 {
|
||
|
delete(me.i.active, me.k)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Get a reference to the values for a key. You should make sure to call Drop exactly once on the
|
||
|
// returned value when done.
|
||
|
func (i *Instance) GetRef(key Key) ActiveValueRef {
|
||
|
i.mu.Lock()
|
||
|
defer i.mu.Unlock()
|
||
|
if i.active == nil {
|
||
|
i.active = make(map[Key]*activeValueType)
|
||
|
}
|
||
|
v, ok := i.active[key]
|
||
|
if !ok {
|
||
|
v = &activeValueType{
|
||
|
ch: make(chan struct{}, i.SlotsPerKey),
|
||
|
}
|
||
|
i.active[key] = v
|
||
|
}
|
||
|
v.refs++
|
||
|
return ActiveValueRef{
|
||
|
v: v,
|
||
|
k: key,
|
||
|
i: i,
|
||
|
}
|
||
|
}
|