github.com/docker/compose-on-kubernetes@v0.5.0/internal/registry/signaler.go (about)

     1  package registry
     2  
     3  import (
     4  	"sync"
     5  )
     6  
     7  // signaler is a single-shot notification system through channel and callbacks
     8  type signaler struct {
     9  	callbacks map[int]func()
    10  	signals   []chan struct{}
    11  	mutex     sync.Mutex
    12  	triggered bool
    13  	nextIndex int
    14  }
    15  
    16  // newSignaler returns a new signaler
    17  func newSignaler() *signaler {
    18  	return &signaler{
    19  		callbacks: make(map[int]func()),
    20  	}
    21  }
    22  
    23  // Register registers a callback function on trigger and returns an uid
    24  func (s *signaler) Register(f func()) int {
    25  	s.mutex.Lock()
    26  	if s.triggered {
    27  		s.mutex.Unlock()
    28  		f()
    29  		return -1
    30  	}
    31  	idx := s.nextIndex
    32  	s.nextIndex++
    33  	s.callbacks[idx] = f
    34  	s.mutex.Unlock()
    35  	return idx
    36  }
    37  
    38  // Unregister unregisters a callback function by uid
    39  func (s *signaler) Unregister(id int) {
    40  	s.mutex.Lock()
    41  	defer s.mutex.Unlock()
    42  	delete(s.callbacks, id)
    43  }
    44  
    45  // Channel registers a channel reader.
    46  func (s *signaler) Channel() chan struct{} {
    47  	res := make(chan struct{}, 1)
    48  	s.mutex.Lock()
    49  	if s.triggered {
    50  		res <- struct{}{}
    51  	} else {
    52  		s.signals = append(s.signals, res)
    53  	}
    54  	s.mutex.Unlock()
    55  	return res
    56  }
    57  
    58  // Signal triggers the signaler
    59  func (s *signaler) Signal() {
    60  	s.mutex.Lock()
    61  	if s.triggered {
    62  		s.mutex.Unlock()
    63  		return
    64  	}
    65  	cb := s.callbacks
    66  	s.triggered = true
    67  	for _, s := range s.signals {
    68  		s <- struct{}{}
    69  	}
    70  	s.mutex.Unlock()
    71  	// callbacks must be invoked without holding the lock since they might Unregister()
    72  	for _, f := range cb {
    73  		f()
    74  	}
    75  }
    76  
    77  // Triggered check if the signaler was triggered
    78  func (s *signaler) Triggered() bool {
    79  	return s.triggered
    80  }