wake_clock_windows.go 1.4 KB

1234567891011121314151617181920212223242526272829303132333435363738394041
  1. //go:build windows
  2. package nebula
  3. import (
  4. "sync"
  5. "time"
  6. "unsafe"
  7. "golang.org/x/sys/windows"
  8. )
  9. var (
  10. procQueryInterruptTime = windows.NewLazySystemDLL("kernelbase.dll").NewProc("QueryInterruptTime")
  11. procQueryUnbiasedInterruptTime = windows.NewLazySystemDLL("kernel32.dll").NewProc("QueryUnbiasedInterruptTime")
  12. // QueryInterruptTime needs Windows 10; probe once and stay dormant on anything older.
  13. wakeClockAvailable = sync.OnceValue(func() bool {
  14. return procQueryInterruptTime.Find() == nil && procQueryUnbiasedInterruptTime.Find() == nil
  15. })
  16. )
  17. // suspendClockDelta returns interrupt time minus unbiased interrupt time, both in 100ns units. The unbiased count
  18. // excludes time the system spends suspended while the biased one includes it, so the difference grows by exactly
  19. // the time spent asleep.
  20. //
  21. // The pausing (unbiased) clock is read first so scheduling jitter between the two reads biases the delta positive;
  22. // the wakeDetector clamps out the noise.
  23. func suspendClockDelta() (time.Duration, bool) {
  24. if !wakeClockAvailable() {
  25. return 0, false
  26. }
  27. var unbiased, biased uint64
  28. if r1, _, _ := procQueryUnbiasedInterruptTime.Call(uintptr(unsafe.Pointer(&unbiased))); r1 == 0 {
  29. return 0, false
  30. }
  31. // Returns void, cannot fail once resolved.
  32. _, _, _ = procQueryInterruptTime.Call(uintptr(unsafe.Pointer(&biased)))
  33. return time.Duration(int64(biased-unbiased)) * 100, true
  34. }