wake_detector.go 1.6 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647
  1. package nebula
  2. import "time"
  3. // wakeDetector notices when the machine has returned from system sleep and measures how long it was suspended.
  4. //
  5. // It samples the spread between two kernel clocks: one that pauses across a suspend and one that keeps counting
  6. // (suspendClockDelta, per platform). While the machine is awake the spread is constant no matter how starved,
  7. // stopped, or stepped this process is — SIGSTOP, debugger pauses, scheduler starvation, and NTP adjustments move
  8. // both clocks together or neither, so none of them can fake a wake. A true suspend is the only thing that grows
  9. // the spread, and it grows by exactly the time spent suspended.
  10. //
  11. // Sample is intended to piggyback on a ticker the caller already runs; it costs two clock reads. It is not safe
  12. // for concurrent use.
  13. type wakeDetector struct {
  14. // read returns the current spread between the two clocks, false if this platform can't provide one.
  15. read func() (time.Duration, bool)
  16. last time.Duration
  17. primed bool
  18. }
  19. func newWakeDetector() *wakeDetector {
  20. return &wakeDetector{read: suspendClockDelta}
  21. }
  22. // Sample returns how long the machine was suspended since the previous call, 0 if it wasn't, and false if the
  23. // platform has no way to tell. The first call primes the baseline and always reports 0.
  24. func (w *wakeDetector) Sample() (time.Duration, bool) {
  25. delta, ok := w.read()
  26. if !ok {
  27. return 0, false
  28. }
  29. if !w.primed {
  30. w.primed = true
  31. w.last = delta
  32. return 0, true
  33. }
  34. slept := delta - w.last
  35. w.last = delta
  36. if slept < 0 {
  37. // The clock pair is read non-atomically so tiny negative jitter is possible; it is never a wake.
  38. slept = 0
  39. }
  40. return slept, true
  41. }