Home | History | Annotate | Download | only in ipc
      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 }
     77