1package syncutil 2 3import ( 4 "fmt" 5 "log" 6 "sync" 7) 8 9type debugT bool 10 11var debug = debugT(false) 12 13func (d debugT) Printf(format string, args ...interface{}) { 14 if bool(d) { 15 log.Printf(format, args...) 16 } 17} 18 19// Sem implements a semaphore that can have multiple units acquired/released 20// at a time. 21type Sem struct { 22 c *sync.Cond // Protects size 23 max, free int64 24} 25 26// NewSem creates a semaphore with max units available for acquisition. 27func NewSem(max int64) *Sem { 28 return &Sem{ 29 c: sync.NewCond(new(sync.Mutex)), 30 free: max, 31 max: max, 32 } 33} 34 35// Acquire will deduct n units from the semaphore. If the deduction would 36// result in the available units falling below zero, the call will block until 37// another go routine returns units via a call to Release. If more units are 38// requested than the semaphore is configured to hold, error will be non-nil. 39func (s *Sem) Acquire(n int64) error { 40 if n > s.max { 41 return fmt.Errorf("sem: attempt to acquire more units than semaphore size %d > %d", n, s.max) 42 } 43 s.c.L.Lock() 44 defer s.c.L.Unlock() 45 for { 46 debug.Printf("Acquire check max %d free %d, n %d", s.max, s.free, n) 47 if s.free >= n { 48 s.free -= n 49 return nil 50 } 51 debug.Printf("Acquire Wait max %d free %d, n %d", s.max, s.free, n) 52 s.c.Wait() 53 } 54} 55 56// Release will return n units to the semaphore and notify any currently 57// blocking Acquire calls. 58func (s *Sem) Release(n int64) { 59 s.c.L.Lock() 60 defer s.c.L.Unlock() 61 debug.Printf("Release max %d free %d, n %d", s.max, s.free, n) 62 s.free += n 63 s.c.Broadcast() 64} 65