github.com/google/syzkaller@v0.0.0-20240517125934-c0f1611a36d6/pkg/ipc/gate.go (about) 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 4 package ipc 5 6 import ( 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. 13 type 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. 23 func 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 31 func (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 50 func (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 }