Files
nebula/wake_detector.go
2026-07-22 18:49:19 -05:00

48 lines
1.6 KiB
Go

package nebula
import "time"
// wakeDetector notices when the machine has returned from system sleep and measures how long it was suspended.
//
// It samples the spread between two kernel clocks: one that pauses across a suspend and one that keeps counting
// (suspendClockDelta, per platform). While the machine is awake the spread is constant no matter how starved,
// stopped, or stepped this process is — SIGSTOP, debugger pauses, scheduler starvation, and NTP adjustments move
// both clocks together or neither, so none of them can fake a wake. A true suspend is the only thing that grows
// the spread, and it grows by exactly the time spent suspended.
//
// Sample is intended to piggyback on a ticker the caller already runs; it costs two clock reads. It is not safe
// for concurrent use.
type wakeDetector struct {
// read returns the current spread between the two clocks, false if this platform can't provide one.
read func() (time.Duration, bool)
last time.Duration
primed bool
}
func newWakeDetector() *wakeDetector {
return &wakeDetector{read: suspendClockDelta}
}
// Sample returns how long the machine was suspended since the previous call, 0 if it wasn't, and false if the
// platform has no way to tell. The first call primes the baseline and always reports 0.
func (w *wakeDetector) Sample() (time.Duration, bool) {
delta, ok := w.read()
if !ok {
return 0, false
}
if !w.primed {
w.primed = true
w.last = delta
return 0, true
}
slept := delta - w.last
w.last = delta
if slept < 0 {
// The clock pair is read non-atomically so tiny negative jitter is possible; it is never a wake.
slept = 0
}
return slept, true
}