1
0
mirror of https://github.com/stryan/mumble-discord-bridge.git synced 2024-11-26 22:45:43 -05:00
mumble-discord-bridge/pkg/sleepct/sleepct.go

93 lines
2.1 KiB
Go
Raw Normal View History

package sleepct
2021-04-18 00:30:27 -04:00
import (
2021-09-13 00:50:23 -04:00
"context"
2021-04-18 00:30:27 -04:00
"fmt"
"time"
)
// SleepCT - Sleep constant time step crates a sleep based ticker.
2021-09-13 00:50:23 -04:00
// designed to maintain a consistent sleep/tick interval.
// The sleeper can be paused waiting to be signaled from another go routine.
// This allows for the pausing of loops that do not have work to complete
2021-04-18 00:30:27 -04:00
type SleepCT struct {
d time.Duration // desired duration between targets
t time.Time // last time target
resume chan bool
2021-09-13 00:50:23 -04:00
wake time.Time // last wake time
drift int64 // last wake drift microseconds
2021-04-18 00:30:27 -04:00
}
func (s *SleepCT) Start(d time.Duration) {
2021-09-13 00:50:23 -04:00
s.resume = make(chan bool, 2)
if s.t.IsZero() {
s.d = d
s.t = time.Now()
} else {
panic("SleepCT already started")
}
}
// Sleep to the next target duration.
// If pause it set to true will sleep the duration and wait to be notified.
// The notification channel will be cleared when the thread wakes.
// SleepNextTarget should not be call more than once concurrently.
2021-09-13 00:50:23 -04:00
func (s *SleepCT) SleepNextTarget(ctx context.Context, pause bool) int64 {
2021-04-18 00:30:27 -04:00
now := time.Now()
2021-09-13 00:50:23 -04:00
// if target is zero safety net
2021-04-18 00:30:27 -04:00
if s.t.IsZero() {
fmt.Println("SleepCT reset")
2021-09-13 00:50:23 -04:00
s.t = now.Add(-s.d)
2021-04-18 00:30:27 -04:00
}
// Sleep to Next Target
2021-09-13 00:50:23 -04:00
s.t = s.t.Add(s.d)
2021-04-18 00:30:27 -04:00
2021-09-13 00:50:23 -04:00
// Compute the desired sleep time to reach the target
d := time.Until(s.t)
2021-04-18 00:30:27 -04:00
2021-09-13 00:50:23 -04:00
// Sleep
2021-04-18 00:30:27 -04:00
time.Sleep(d)
2021-09-13 00:50:23 -04:00
// record the wake time
s.wake = time.Now()
s.drift = s.wake.Sub(s.t).Microseconds()
// fmt.Println(s.t.UnixMilli(), d.Milliseconds(), wake.UnixMilli(), drift, pause, len(s.resume))
// external pause control
if pause {
2021-09-13 00:50:23 -04:00
// don't pause if the notification channel has something
if len(s.resume) == 0 {
2021-09-13 00:50:23 -04:00
// fmt.Println("pause")
select {
case <-s.resume:
case <-ctx.Done():
// fmt.Println("sleepct ctx exit")
}
// if we did pause set the last sleep target to now
2021-09-13 00:50:23 -04:00
s.t = time.Now()
}
}
2021-04-18 00:30:27 -04:00
// Drain the resume channel
select {
case <-s.resume:
default:
}
2021-09-13 00:50:23 -04:00
// return the drift for monitoring purposes
return s.drift
2021-04-18 00:30:27 -04:00
}
// Notify attempts to resume a paused sleeper.
// It is safe to call notify from other processes and as often as desired.
func (s *SleepCT) Notify() {
2021-09-13 00:50:23 -04:00
select {
case s.resume <- true:
default:
}
2021-04-18 00:30:27 -04:00
}