1// Copyright 2015 syzkaller project authors. All rights reserved. 2// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file. 3 4package ipc 5 6import ( 7 "sync" 8) 9 10// Gate limits concurrency level and window to the given value. 11// Limitation of concurrency window means that if a very old activity is still 12// running it will not let new activities to start even if concurrency level is low. 13type Gate struct { 14 cv *sync.Cond 15 busy []bool 16 pos int 17 running int 18 stop bool 19 f func() 20} 21 22// If f is not nil, it will be called after each batch of c activities. 23func NewGate(c int, f func()) *Gate { 24 return &Gate{ 25 cv: sync.NewCond(new(sync.Mutex)), 26 busy: make([]bool, c), 27 f: f, 28 } 29} 30 31func (g *Gate) Enter() int { 32 g.cv.L.Lock() 33 for g.busy[g.pos] || g.stop { 34 g.cv.Wait() 35 } 36 idx := g.pos 37 g.pos++ 38 if g.pos >= len(g.busy) { 39 g.pos = 0 40 } 41 g.busy[idx] = true 42 g.running++ 43 if g.running > len(g.busy) { 44 panic("broken gate") 45 } 46 g.cv.L.Unlock() 47 return idx 48} 49 50func (g *Gate) Leave(idx int) { 51 g.cv.L.Lock() 52 if !g.busy[idx] { 53 panic("broken gate") 54 } 55 g.busy[idx] = false 56 g.running-- 57 if g.running < 0 { 58 panic("broken gate") 59 } 60 if idx == 0 && g.f != nil { 61 if g.stop { 62 panic("broken gate") 63 } 64 g.stop = true 65 for g.running != 0 { 66 g.cv.Wait() 67 } 68 g.stop = false 69 g.f() 70 g.cv.Broadcast() 71 } 72 if idx == g.pos && !g.stop || g.running == 0 && g.stop { 73 g.cv.Broadcast() 74 } 75 g.cv.L.Unlock() 76} 77