50 lines
1.0 KiB
Go
50 lines
1.0 KiB
Go
package application
|
|||
|
|
|
||
|
|
import (
|
||
|
|
"sync"
|
||
|
|
|
||
|
|
"chub/internal/domain"
|
||
|
|
)
|
||
|
|
|
||
|
|
// EventBus is the in-process local event channel shared by the UI and CLI
|
||
|
|
// adapters. Subscribers own cancellation; publishing never blocks a browser
|
||
|
|
// process monitor on a slow presentation consumer.
|
||
|
|
type EventBus struct {
|
||
|
|
mu sync.RWMutex
|
||
|
|
nextID uint64
|
||
|
|
clients map[uint64]chan domain.BrowserEvent
|
||
|
|
}
|
||
|
|
|
||
|
|
func NewEventBus() *EventBus { return &EventBus{clients: make(map[uint64]chan domain.BrowserEvent)} }
|
||
|
|
|
||
|
|
func (b *EventBus) Subscribe(buffer int) (<-chan domain.BrowserEvent, func()) {
|
||
|
|
if buffer < 1 {
|
||
|
|
buffer = 1
|
||
|
|
}
|
||
|
|
b.mu.Lock()
|
||
|
|
b.nextID++
|
||
|
|
id := b.nextID
|
||
|
|
ch := make(chan domain.BrowserEvent, buffer)
|
||
|
|
b.clients[id] = ch
|
||
|
|
b.mu.Unlock()
|
||
|
|
return ch, func() {
|
||
|
|
b.mu.Lock()
|
||
|
|
if existing, ok := b.clients[id]; ok {
|
||
|
|
delete(b.clients, id)
|
||
|
|
close(existing)
|
||
|
|
}
|
||
|
|
b.mu.Unlock()
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func (b *EventBus) Publish(event domain.BrowserEvent) {
|
||
|
|
b.mu.RLock()
|
||
|
|
defer b.mu.RUnlock()
|
||
|
|
for _, ch := range b.clients {
|
||
|
|
select {
|
||
|
|
case ch <- event:
|
||
|
|
default:
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|