81 lines
1.2 KiB
Go
81 lines
1.2 KiB
Go
package alloclim
|
|
|
|
import "sync"
|
|
|
|
// Manages reservations sharing a common allocation limit.
|
|
type Limiter struct {
|
|
// Maximum outstanding allocation space.
|
|
Max int64
|
|
initOnce sync.Once
|
|
mu sync.Mutex
|
|
// Current unallocated space.
|
|
value int64
|
|
// Reservations waiting to in the order they arrived.
|
|
waiting []*Reservation
|
|
}
|
|
|
|
func (me *Limiter) initValue() {
|
|
me.value = me.Max
|
|
}
|
|
|
|
func (me *Limiter) init() {
|
|
me.initOnce.Do(func() {
|
|
me.initValue()
|
|
})
|
|
}
|
|
|
|
func (me *Limiter) Reserve(n int64) *Reservation {
|
|
r := &Reservation{
|
|
l: me,
|
|
n: n,
|
|
}
|
|
me.init()
|
|
me.mu.Lock()
|
|
if n <= me.value {
|
|
me.value -= n
|
|
r.granted.Set()
|
|
} else {
|
|
me.waiting = append(me.waiting, r)
|
|
}
|
|
me.mu.Unlock()
|
|
return r
|
|
}
|
|
|
|
func (me *Limiter) doWakesLocked() {
|
|
for {
|
|
if len(me.waiting) == 0 {
|
|
break
|
|
}
|
|
r := me.waiting[0]
|
|
switch {
|
|
case r.cancelled.IsSet():
|
|
case r.n <= me.value:
|
|
if r.wake() {
|
|
me.value -= r.n
|
|
}
|
|
default:
|
|
return
|
|
}
|
|
me.waiting = me.waiting[1:]
|
|
}
|
|
}
|
|
|
|
func (me *Limiter) doWakes() {
|
|
me.mu.Lock()
|
|
me.doWakesLocked()
|
|
me.mu.Unlock()
|
|
}
|
|
|
|
func (me *Limiter) addValue(n int64) {
|
|
me.mu.Lock()
|
|
me.value += n
|
|
me.doWakesLocked()
|
|
me.mu.Unlock()
|
|
}
|
|
|
|
func (me *Limiter) Value() int64 {
|
|
me.mu.Lock()
|
|
defer me.mu.Unlock()
|
|
return me.value
|
|
}
|