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  }