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
|
||
|
}
|