package destination import ( "context" pb "github.com/linkerd/linkerd2-proxy-api/go/destination" logging "github.com/sirupsen/logrus" "google.golang.org/grpc/metadata" ) // synchronizedGetStream is a wrapper around a pb.Destination_GetServer that // makes Send safe to call from multiple goroutines. It does this by using an // unbuffered channel to synchronize calls to Send. Since this channel is // unbuffered, calls to Send may block and callers should do their own queuing. // This type implemenets the pb.Destination_GetServer interface but only the // Send method is supported. Calls to other methods will panic. type synchronizedGetStream struct { done chan struct{} ch chan *pb.Update inner pb.Destination_GetServer log *logging.Entry } func newSyncronizedGetStream(stream pb.Destination_GetServer, log *logging.Entry) *synchronizedGetStream { return &synchronizedGetStream{ done: make(chan struct{}), ch: make(chan *pb.Update), inner: stream, log: log, } } func (s *synchronizedGetStream) SetHeader(metadata.MD) error { panic("SetHeader called on synchronizedGetStream") } func (s *synchronizedGetStream) SendHeader(metadata.MD) error { panic("SendHeader called on synchronizedGetStream") } func (s *synchronizedGetStream) SetTrailer(metadata.MD) { panic("SetTrailer called on synchronizedGetStream") } func (s synchronizedGetStream) Context() context.Context { panic("Conext called on synchronizedGetStream") } func (s *synchronizedGetStream) SendMsg(m any) error { panic("SendMsg called on synchronizedGetStream") } func (s *synchronizedGetStream) RecvMsg(m any) error { panic("RecvMsg called on synchronizedGetStream") } func (s *synchronizedGetStream) Send(update *pb.Update) error { s.ch <- update return nil } func (s *synchronizedGetStream) Start() { go func() { for { select { case <-s.done: return case update := <-s.ch: err := s.inner.Send(update) if err != nil { s.log.Errorf("Error sending update: %v", err) } } } }() } func (s *synchronizedGetStream) Stop() { s.done <- struct{}{} }