(new): expand runtime APIs
(fix): harden concurrent lifecycle (tests): add regression coverage (doc): update v1.2 guidance
This commit is contained in:
+29
-4
@@ -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() }
|
||||
|
||||
Reference in New Issue
Block a user