github.com/rudderlabs/rudder-go-kit@v0.30.0/async/single_sender.go (about) 1 package async 2 3 import ( 4 "context" 5 ) 6 7 // SingleSender is a helper for sending and receiving values to and from a channel between 2 separate goroutines, a sending and a receiving goroutine, while at the same time supporting the following scenarios: 8 // 1. The sending goroutine in case the parent context is canceled should be able to notify the receiver goroutine about the error through the channel. 9 // 2. The receiving goroutine should be able to stop listening from the channel (a.k.a. leave) at any point. 10 // 3. The sending goroutine shouldn't be blocked trying to send to the channel when the receiver has left it. 11 // 4. Receiver's departure should act as a context cancellation signal to the sending goroutine, i.e. it should stop working. 12 type SingleSender[T any] struct { 13 ctx context.Context 14 ctxCancel context.CancelFunc 15 sendCtx context.Context 16 sendCtxCancel context.CancelFunc 17 ch chan T 18 closed bool 19 } 20 21 // Begin creates a new channel and returns it along with a context for the sending goroutine to use and a function for the receiving goroutine to be able to leave the "conversation" if needed. 22 func (s *SingleSender[T]) Begin(parentCtx context.Context) (ctx context.Context, ch <-chan T, leave func()) { 23 s.ctx, s.ctxCancel = context.WithCancel(parentCtx) 24 s.ch = make(chan T) 25 s.sendCtx, s.sendCtxCancel = context.WithCancel(context.Background()) 26 return s.ctx, s.ch, s.sendCtxCancel 27 } 28 29 // Send tries to send a value to the channel. If the channel is closed, or the receiving goroutine has left it does nothing. 30 func (s *SingleSender[T]) Send(value T) { 31 closed := s.closed 32 if closed { // don't send to a closed channel 33 return 34 } 35 select { 36 case <-s.sendCtx.Done(): 37 s.ctxCancel() 38 return 39 case s.ch <- value: 40 } 41 } 42 43 // Close the channel and cancel all related contexts. 44 func (s *SingleSender[T]) Close() { 45 if s.closed { 46 return 47 } 48 s.closed = true 49 s.ctxCancel() 50 s.sendCtxCancel() 51 close(s.ch) 52 }