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