This follows up from abb5cbc96e301a4ca1f5df698b105ae8553ce1e9. We currently limit how many requests peers can send us, but didn't really check that peers didn't make us allocate huge amounts of space to buffer their requests. I'm sure there's some rough edges here.
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
|
|
}
|