| 
					
				 | 
			
			
				@@ -0,0 +1,64 @@ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+package broadcaster 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import ( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	"context" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	"sync" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	"sync/atomic" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+type Broadcaster[T any] interface { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	Publish(ctx context.Context, data T) error 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	Subscribe() chan T 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+type broadcaster[T any] struct { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	mu       sync.RWMutex 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	channels []*chanMeta[T] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	bufSize  int 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+type chanMeta[T any] struct { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	ch     chan T 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	closed atomic.Bool 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+func NewBroadcaster[T any](bufSize int) Broadcaster[T] { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	return &broadcaster[T]{ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		mu:       sync.RWMutex{}, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		channels: make([]*chanMeta[T], 0), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		bufSize:  bufSize, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+// Publish implements Broadcaster. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+func (b *broadcaster[T]) Publish(ctx context.Context, data T) error { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	b.mu.RLock() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	defer b.mu.RUnlock() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	for _, meta := range b.channels { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		if meta.closed.Load() { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			continue 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		select { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		case meta.ch <- data: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		default: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			meta.closed.Store(true) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			close(meta.ch) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	return nil 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+// Subscribe implements Broadcaster. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+func (b *broadcaster[T]) Subscribe() chan T { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	b.mu.Lock() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	defer b.mu.Unlock() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	ch := make(chan T, b.bufSize) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	b.channels = append(b.channels, &chanMeta[T]{ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		ch:     ch, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		closed: atomic.Bool{}, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	}) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	return ch 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 |