From 7e5b2d4cde87c73a4d20df17de2e770e1ef37089 Mon Sep 17 00:00:00 2001 From: samosvalishe Date: Mon, 21 Sep 2026 13:43:20 +0300 Subject: [PATCH] =?UTF-8?q?refactor(wake):=20=D1=83=D0=B1=D1=80=D0=B0?= =?UTF-8?q?=D1=82=D1=8C=20=D0=BD=D0=B5=D0=B8=D1=81=D0=BF=D0=BE=D0=BB=D1=8C?= =?UTF-8?q?=D0=B7=D1=83=D0=B5=D0=BC=D1=8B=D0=B9=20Notifier?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/session/session.go | 2 +- internal/wake/wake.go | 44 +---------------------------- internal/wake/wake_test.go | 55 ++++++++++++++----------------------- 3 files changed, 23 insertions(+), 78 deletions(-) diff --git a/internal/session/session.go b/internal/session/session.go index aac72c2..54e58e6 100644 --- a/internal/session/session.go +++ b/internal/session/session.go @@ -225,7 +225,7 @@ func (s *Session) Run(ctx context.Context) (err error) { } } bg.Go(guard(func() { - wake.New().Watch(runCtx, wakeTick, wakeThreshold, func(gap time.Duration) { + wake.Watch(runCtx, wakeTick, wakeThreshold, func(gap time.Duration) { log.Warnf("device slept for %s - checking TURN allocations", gap.Truncate(time.Second)) s.Wake() }) diff --git a/internal/wake/wake.go b/internal/wake/wake.go index 6e4734e..18a5154 100644 --- a/internal/wake/wake.go +++ b/internal/wake/wake.go @@ -1,52 +1,11 @@ -// Package wake оповещает компоненты о выходе устройства из режима сна. package wake import ( "context" - "sync" "time" ) -// Notifier рассылает сигнал пробуждения слушателям. -type Notifier struct { - mu sync.Mutex - ch chan struct{} -} - -func New() *Notifier { - return &Notifier{ch: make(chan struct{})} -} - -// Chan возвращает канал, закрывающийся при следующем вызове Fire (nil для nil-ресивера). -func (n *Notifier) Chan() <-chan struct{} { - if n == nil { - return nil - } - n.mu.Lock() - defer n.mu.Unlock() - n.initLocked() - return n.ch -} - -func (n *Notifier) Fire() { - if n == nil { - return - } - n.mu.Lock() - defer n.mu.Unlock() - n.initLocked() - close(n.ch) - n.ch = make(chan struct{}) -} - -func (n *Notifier) initLocked() { - if n.ch == nil { - n.ch = make(chan struct{}) - } -} - -// Watch отслеживает разрывы времени между тиками и вызывает Fire при обнаружении сна. -func (n *Notifier) Watch(ctx context.Context, tick, threshold time.Duration, onGap func(time.Duration)) { +func Watch(ctx context.Context, tick, threshold time.Duration, onGap func(time.Duration)) { t := time.NewTicker(tick) defer t.Stop() @@ -66,7 +25,6 @@ func (n *Notifier) Watch(ctx context.Context, tick, threshold time.Duration, onG if onGap != nil { onGap(gap) } - n.Fire() } } } diff --git a/internal/wake/wake_test.go b/internal/wake/wake_test.go index 2c4385b..5fcdaa0 100644 --- a/internal/wake/wake_test.go +++ b/internal/wake/wake_test.go @@ -1,50 +1,37 @@ package wake import ( + "context" "testing" "time" ) -func TestFireWakesAllListeners(t *testing.T) { - n := New() - a, b := n.Chan(), n.Chan() - n.Fire() +func TestWatchStopsOnContext(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + done := make(chan struct{}) + go func() { + Watch(ctx, time.Millisecond, time.Hour, nil) + close(done) + }() - for i, ch := range []<-chan struct{}{a, b} { - select { - case <-ch: - case <-time.After(time.Second): - t.Fatalf("listener %d did not wake", i) - } + cancel() + select { + case <-done: + case <-time.After(time.Second): + t.Fatal("Watch did not return after context cancel") } } -func TestChanIsRenewedAfterFire(t *testing.T) { - n := New() - n.Fire() +func TestWatchIgnoresGapsBelowThreshold(t *testing.T) { + ctx, cancel := context.WithTimeout(context.Background(), 50*time.Millisecond) + defer cancel() + + fired := make(chan time.Duration, 1) + Watch(ctx, time.Millisecond, time.Hour, func(gap time.Duration) { fired <- gap }) - next := n.Chan() select { - case <-next: - t.Fatal("fresh channel is already closed") + case gap := <-fired: + t.Fatalf("onGap fired without a sleep gap: %s", gap) default: } - - n.Fire() - select { - case <-next: - case <-time.After(time.Second): - t.Fatal("second Fire did not reach the fresh channel") - } -} - -func TestNilNotifierBlocksForever(t *testing.T) { - var n *Notifier - n.Fire() - - select { - case <-n.Chan(): - t.Fatal("nil notifier produced a ready channel") - case <-time.After(10 * time.Millisecond): - } }