Files
2026-07-13 13:00:08 +08:00

42 lines
1014 B
Go

package event
import (
"sync"
"reasonix/internal/evidence"
"reasonix/internal/nilutil"
)
// Sync wraps a Sink so concurrent Emit calls are serialized. The base Sink
// contract assumes serial emission — the agent's run loop emits one event at a
// time. Background jobs (internal/jobs) emit from their own goroutines, which can
// overlap a running turn's emission; wrapping the session sink once in Sync keeps
// the serial-Emit invariant every sink relies on (an SSE writer, a webview
// EventsEmit, a TUI channel) without each having to lock. A nil sink yields
// Discard.
func Sync(s Sink) Sink {
if nilutil.IsNil(s) {
return Discard
}
return &syncSink{inner: s}
}
type syncSink struct {
mu sync.Mutex
inner Sink
}
func (s *syncSink) Emit(e Event) {
s.mu.Lock()
defer s.mu.Unlock()
s.inner.Emit(e)
}
func (s *syncSink) RecordReadinessAudit(a evidence.ReadinessAudit) {
s.mu.Lock()
defer s.mu.Unlock()
if rs, ok := s.inner.(ReadinessAuditSink); ok {
rs.RecordReadinessAudit(a)
}
}