(new): expand runtime APIs
Golang lint / lint (push) Successful in 58s
Golang lint / lint (pull_request) Successful in 13m10s

(fix): harden concurrent lifecycle
(tests): add regression coverage
(doc): update v1.2 guidance
This commit is contained in:
2026-08-20 11:08:45 +03:00
parent 29b208eeec
commit 24040fe164
37 changed files with 1129 additions and 157 deletions
+29 -4
View File
@@ -5,6 +5,7 @@ import (
"fmt"
"sync"
"sync/atomic"
"time"
"git.scuroneko.dev/scuroneko/sneklog/v2"
)
@@ -19,6 +20,8 @@ type queuedObserverEvent struct {
type observerDispatcher struct {
observer Observer
logger *sneklog.Logger
ctx context.Context
cancel context.CancelFunc
queue chan queuedObserverEvent
stop chan struct{}
mu sync.RWMutex
@@ -29,9 +32,12 @@ type observerDispatcher struct {
}
func newObserverDispatcher(observer Observer, logger *sneklog.Logger) *observerDispatcher {
ctx, cancel := context.WithCancel(context.Background())
dispatcher := &observerDispatcher{
observer: observer,
logger: logger,
ctx: ctx,
cancel: cancel,
queue: make(chan queuedObserverEvent, observerQueueSize),
stop: make(chan struct{}),
}
@@ -43,9 +49,8 @@ func newObserverDispatcher(observer Observer, logger *sneklog.Logger) *observerD
func (d *observerDispatcher) enqueue(ctx context.Context, event Event) {
if ctx == nil {
ctx = context.Background()
} else {
ctx = context.WithoutCancel(ctx)
}
ctx = observerEventContext{Context: context.WithoutCancel(ctx), lifecycle: d.ctx}
d.mu.RLock()
defer d.mu.RUnlock()
if d.closed {
@@ -89,12 +94,32 @@ func (d *observerDispatcher) dispatch(queued queuedObserverEvent) {
emitObserverEvent(d.observer, queued.ctx, queued.event)
}
func (d *observerDispatcher) close() {
func (d *observerDispatcher) close(ctx context.Context) error {
d.stopOnce.Do(func() {
d.mu.Lock()
d.closed = true
d.cancel()
close(d.stop)
d.mu.Unlock()
})
d.wg.Wait()
done := make(chan struct{})
go func() {
d.wg.Wait()
close(done)
}()
select {
case <-done:
return nil
case <-ctx.Done():
return fmt.Errorf("%w: %v", ErrObserverShutdownTimeout, ctx.Err())
}
}
type observerEventContext struct {
context.Context
lifecycle context.Context
}
func (ctx observerEventContext) Deadline() (time.Time, bool) { return ctx.lifecycle.Deadline() }
func (ctx observerEventContext) Done() <-chan struct{} { return ctx.lifecycle.Done() }
func (ctx observerEventContext) Err() error { return ctx.lifecycle.Err() }