github.com/kaisenlinux/docker.io@v0.0.0-20230510090727-ea55db55fac7/swarmkit/watch/watch.go (about)

     1  package watch
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"sync"
     7  	"time"
     8  
     9  	"github.com/docker/go-events"
    10  	"github.com/docker/swarmkit/watch/queue"
    11  )
    12  
    13  // ChannelSinkGenerator is a constructor of sinks that eventually lead to a
    14  // channel.
    15  type ChannelSinkGenerator interface {
    16  	NewChannelSink() (events.Sink, *events.Channel)
    17  }
    18  
    19  // Queue is the structure used to publish events and watch for them.
    20  type Queue struct {
    21  	sinkGen ChannelSinkGenerator
    22  	// limit is the max number of items to be held in memory for a watcher
    23  	limit       uint64
    24  	mu          sync.Mutex
    25  	broadcast   *events.Broadcaster
    26  	cancelFuncs map[events.Sink]func()
    27  
    28  	// closeOutChan indicates whether the watchers' channels should be closed
    29  	// when a watcher queue reaches its limit or when the Close method of the
    30  	// sink is called.
    31  	closeOutChan bool
    32  }
    33  
    34  // NewQueue creates a new publish/subscribe queue which supports watchers.
    35  // The channels that it will create for subscriptions will have the buffer
    36  // size specified by buffer.
    37  func NewQueue(options ...func(*Queue) error) *Queue {
    38  	// Create a queue with the default values
    39  	q := &Queue{
    40  		sinkGen:      &dropErrClosedChanGen{},
    41  		broadcast:    events.NewBroadcaster(),
    42  		cancelFuncs:  make(map[events.Sink]func()),
    43  		limit:        0,
    44  		closeOutChan: false,
    45  	}
    46  
    47  	for _, option := range options {
    48  		err := option(q)
    49  		if err != nil {
    50  			panic(fmt.Sprintf("Failed to apply options to queue: %s", err))
    51  		}
    52  	}
    53  
    54  	return q
    55  }
    56  
    57  // WithTimeout returns a functional option for a queue that sets a write timeout
    58  func WithTimeout(timeout time.Duration) func(*Queue) error {
    59  	return func(q *Queue) error {
    60  		q.sinkGen = NewTimeoutDropErrSinkGen(timeout)
    61  		return nil
    62  	}
    63  }
    64  
    65  // WithCloseOutChan returns a functional option for a queue whose watcher
    66  // channel is closed when no more events are expected to be sent to the watcher.
    67  func WithCloseOutChan() func(*Queue) error {
    68  	return func(q *Queue) error {
    69  		q.closeOutChan = true
    70  		return nil
    71  	}
    72  }
    73  
    74  // WithLimit returns a functional option for a queue with a max size limit.
    75  func WithLimit(limit uint64) func(*Queue) error {
    76  	return func(q *Queue) error {
    77  		q.limit = limit
    78  		return nil
    79  	}
    80  }
    81  
    82  // Watch returns a channel which will receive all items published to the
    83  // queue from this point, until cancel is called.
    84  func (q *Queue) Watch() (eventq chan events.Event, cancel func()) {
    85  	return q.CallbackWatch(nil)
    86  }
    87  
    88  // WatchContext returns a channel where all items published to the queue will
    89  // be received. The channel will be closed when the provided context is
    90  // cancelled.
    91  func (q *Queue) WatchContext(ctx context.Context) (eventq chan events.Event) {
    92  	return q.CallbackWatchContext(ctx, nil)
    93  }
    94  
    95  // CallbackWatch returns a channel which will receive all events published to
    96  // the queue from this point that pass the check in the provided callback
    97  // function. The returned cancel function will stop the flow of events and
    98  // close the channel.
    99  func (q *Queue) CallbackWatch(matcher events.Matcher) (eventq chan events.Event, cancel func()) {
   100  	chanSink, ch := q.sinkGen.NewChannelSink()
   101  	lq := queue.NewLimitQueue(chanSink, q.limit)
   102  	sink := events.Sink(lq)
   103  
   104  	if matcher != nil {
   105  		sink = events.NewFilter(sink, matcher)
   106  	}
   107  
   108  	q.broadcast.Add(sink)
   109  
   110  	cancelFunc := func() {
   111  		q.broadcast.Remove(sink)
   112  		ch.Close()
   113  		sink.Close()
   114  	}
   115  
   116  	externalCancelFunc := func() {
   117  		q.mu.Lock()
   118  		cancelFunc := q.cancelFuncs[sink]
   119  		delete(q.cancelFuncs, sink)
   120  		q.mu.Unlock()
   121  
   122  		if cancelFunc != nil {
   123  			cancelFunc()
   124  		}
   125  	}
   126  
   127  	q.mu.Lock()
   128  	q.cancelFuncs[sink] = cancelFunc
   129  	q.mu.Unlock()
   130  
   131  	// If the output channel shouldn't be closed and the queue is limitless,
   132  	// there's no need for an additional goroutine.
   133  	if !q.closeOutChan && q.limit == 0 {
   134  		return ch.C, externalCancelFunc
   135  	}
   136  
   137  	outChan := make(chan events.Event)
   138  	go func() {
   139  		for {
   140  			select {
   141  			case <-ch.Done():
   142  				// Close the output channel if the ChannelSink is Done for any
   143  				// reason. This can happen if the cancelFunc is called
   144  				// externally or if it has been closed by a wrapper sink, such
   145  				// as the TimeoutSink.
   146  				if q.closeOutChan {
   147  					close(outChan)
   148  				}
   149  				externalCancelFunc()
   150  				return
   151  			case <-lq.Full():
   152  				// Close the output channel and tear down the Queue if the
   153  				// LimitQueue becomes full.
   154  				if q.closeOutChan {
   155  					close(outChan)
   156  				}
   157  				externalCancelFunc()
   158  				return
   159  			case event := <-ch.C:
   160  				outChan <- event
   161  			}
   162  		}
   163  	}()
   164  
   165  	return outChan, externalCancelFunc
   166  }
   167  
   168  // CallbackWatchContext returns a channel where all items published to the queue will
   169  // be received. The channel will be closed when the provided context is
   170  // cancelled.
   171  func (q *Queue) CallbackWatchContext(ctx context.Context, matcher events.Matcher) (eventq chan events.Event) {
   172  	c, cancel := q.CallbackWatch(matcher)
   173  	go func() {
   174  		<-ctx.Done()
   175  		cancel()
   176  	}()
   177  	return c
   178  }
   179  
   180  // Publish adds an item to the queue.
   181  func (q *Queue) Publish(item events.Event) {
   182  	q.broadcast.Write(item)
   183  }
   184  
   185  // Close closes the queue and frees the associated resources.
   186  func (q *Queue) Close() error {
   187  	// Make sure all watchers have been closed to avoid a deadlock when
   188  	// closing the broadcaster.
   189  	q.mu.Lock()
   190  	for _, cancelFunc := range q.cancelFuncs {
   191  		cancelFunc()
   192  	}
   193  	q.cancelFuncs = make(map[events.Sink]func())
   194  	q.mu.Unlock()
   195  
   196  	return q.broadcast.Close()
   197  }