github.com/kaisenlinux/docker.io@v0.0.0-20230510090727-ea55db55fac7/swarmkit/watch/sinks.go (about) 1 package watch 2 3 import ( 4 "fmt" 5 "time" 6 7 events "github.com/docker/go-events" 8 ) 9 10 // ErrSinkTimeout is returned from the Write method when a sink times out. 11 var ErrSinkTimeout = fmt.Errorf("timeout exceeded, tearing down sink") 12 13 // timeoutSink is a sink that wraps another sink with a timeout. If the 14 // embedded sink fails to complete a Write operation within the specified 15 // timeout, the Write operation of the timeoutSink fails. 16 type timeoutSink struct { 17 timeout time.Duration 18 sink events.Sink 19 } 20 21 func (s timeoutSink) Write(event events.Event) error { 22 errChan := make(chan error) 23 go func(c chan<- error) { 24 c <- s.sink.Write(event) 25 }(errChan) 26 27 timer := time.NewTimer(s.timeout) 28 select { 29 case err := <-errChan: 30 timer.Stop() 31 return err 32 case <-timer.C: 33 s.sink.Close() 34 return ErrSinkTimeout 35 } 36 } 37 38 func (s timeoutSink) Close() error { 39 return s.sink.Close() 40 } 41 42 // dropErrClosed is a sink that suppresses ErrSinkClosed from Write, to avoid 43 // debug log messages that may be confusing. It is possible that the queue 44 // will try to write an event to its destination channel while the queue is 45 // being removed from the broadcaster. Since the channel is closed before the 46 // queue, there is a narrow window when this is possible. In some event-based 47 // dropping events when a sink is removed from a broadcaster is a problem, but 48 // for the usage in this watch package that's the expected behavior. 49 type dropErrClosed struct { 50 sink events.Sink 51 } 52 53 func (s dropErrClosed) Write(event events.Event) error { 54 err := s.sink.Write(event) 55 if err == events.ErrSinkClosed { 56 return nil 57 } 58 return err 59 } 60 61 func (s dropErrClosed) Close() error { 62 return s.sink.Close() 63 } 64 65 // dropErrClosedChanGen is a ChannelSinkGenerator for dropErrClosed sinks wrapping 66 // unbuffered channels. 67 type dropErrClosedChanGen struct{} 68 69 func (s *dropErrClosedChanGen) NewChannelSink() (events.Sink, *events.Channel) { 70 ch := events.NewChannel(0) 71 return dropErrClosed{sink: ch}, ch 72 } 73 74 // TimeoutDropErrChanGen is a ChannelSinkGenerator that creates a channel, 75 // wrapped by the dropErrClosed sink and a timeout. 76 type TimeoutDropErrChanGen struct { 77 timeout time.Duration 78 } 79 80 // NewChannelSink creates a new sink chain of timeoutSink->dropErrClosed->Channel 81 func (s *TimeoutDropErrChanGen) NewChannelSink() (events.Sink, *events.Channel) { 82 ch := events.NewChannel(0) 83 return timeoutSink{ 84 timeout: s.timeout, 85 sink: dropErrClosed{ 86 sink: ch, 87 }, 88 }, ch 89 } 90 91 // NewTimeoutDropErrSinkGen returns a generator of timeoutSinks wrapping dropErrClosed 92 // sinks, wrapping unbuffered channel sinks. 93 func NewTimeoutDropErrSinkGen(timeout time.Duration) ChannelSinkGenerator { 94 return &TimeoutDropErrChanGen{timeout: timeout} 95 }