7582279077
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/claude-agent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
117 lines
2.0 KiB
Go
117 lines
2.0 KiB
Go
package eventbus
|
|
|
|
import "sync"
|
|
|
|
type Event struct {
|
|
Seq int64
|
|
WorkflowRunID string
|
|
AlbumID string
|
|
Quality string
|
|
EventType string
|
|
Step string
|
|
Message string
|
|
Data interface{}
|
|
}
|
|
|
|
type Subscription struct {
|
|
Ring *RingBuffer[*Event]
|
|
C chan struct{}
|
|
done chan struct{}
|
|
once sync.Once
|
|
}
|
|
|
|
type EventBus struct {
|
|
mu sync.RWMutex
|
|
topics map[string]map[*Subscription]struct{}
|
|
global map[*Subscription]struct{}
|
|
}
|
|
|
|
func New() *EventBus {
|
|
return &EventBus{
|
|
topics: make(map[string]map[*Subscription]struct{}),
|
|
global: make(map[*Subscription]struct{}),
|
|
}
|
|
}
|
|
|
|
func (b *EventBus) Publish(topic string, event *Event) {
|
|
b.mu.RLock()
|
|
defer b.mu.RUnlock()
|
|
|
|
if subs, ok := b.topics[topic]; ok {
|
|
for sub := range subs {
|
|
sub.Ring.Push(event)
|
|
select {
|
|
case sub.C <- struct{}{}:
|
|
default:
|
|
}
|
|
}
|
|
}
|
|
|
|
for sub := range b.global {
|
|
sub.Ring.Push(event)
|
|
select {
|
|
case sub.C <- struct{}{}:
|
|
default:
|
|
}
|
|
}
|
|
}
|
|
|
|
func (b *EventBus) Subscribe(topic string) (*Subscription, func()) {
|
|
sub := &Subscription{
|
|
Ring: NewRingBuffer[*Event](256),
|
|
C: make(chan struct{}, 1),
|
|
done: make(chan struct{}),
|
|
}
|
|
|
|
b.mu.Lock()
|
|
if b.topics[topic] == nil {
|
|
b.topics[topic] = make(map[*Subscription]struct{})
|
|
}
|
|
b.topics[topic][sub] = struct{}{}
|
|
b.mu.Unlock()
|
|
|
|
cleanup := func() {
|
|
sub.once.Do(func() {
|
|
b.mu.Lock()
|
|
delete(b.topics[topic], sub)
|
|
if len(b.topics[topic]) == 0 {
|
|
delete(b.topics, topic)
|
|
}
|
|
b.mu.Unlock()
|
|
close(sub.done)
|
|
})
|
|
}
|
|
|
|
return sub, cleanup
|
|
}
|
|
|
|
func (b *EventBus) SubscribeGlobal() (*Subscription, func()) {
|
|
sub := &Subscription{
|
|
Ring: NewRingBuffer[*Event](256),
|
|
C: make(chan struct{}, 1),
|
|
done: make(chan struct{}),
|
|
}
|
|
|
|
b.mu.Lock()
|
|
b.global[sub] = struct{}{}
|
|
b.mu.Unlock()
|
|
|
|
cleanup := func() {
|
|
sub.once.Do(func() {
|
|
b.mu.Lock()
|
|
delete(b.global, sub)
|
|
b.mu.Unlock()
|
|
close(sub.done)
|
|
})
|
|
}
|
|
|
|
return sub, cleanup
|
|
}
|
|
|
|
func (b *EventBus) HasTopic(topic string) bool {
|
|
b.mu.RLock()
|
|
defer b.mu.RUnlock()
|
|
_, ok := b.topics[topic]
|
|
return ok
|
|
}
|