You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

88 lines
2.4 KiB

  1. // Copyright 2011 The Go Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style
  3. // license that can be found in the LICENSE file.
  4. package util
  5. import "time"
  6. // A Throttle permits throttling of a goroutine by
  7. // calling the Throttle method repeatedly.
  8. //
  9. type Throttle struct {
  10. f float64 // f = (1-r)/r for 0 < r < 1
  11. dt time.Duration // minimum run time slice; >= 0
  12. tr time.Duration // accumulated time running
  13. ts time.Duration // accumulated time stopped
  14. tt time.Time // earliest throttle time (= time Throttle returned + tm)
  15. }
  16. // NewThrottle creates a new Throttle with a throttle value r and
  17. // a minimum allocated run time slice of dt:
  18. //
  19. // r == 0: "empty" throttle; the goroutine is always sleeping
  20. // r == 1: full throttle; the goroutine is never sleeping
  21. //
  22. // A value of r == 0.6 throttles a goroutine such that it runs
  23. // approx. 60% of the time, and sleeps approx. 40% of the time.
  24. // Values of r < 0 or r > 1 are clamped down to values between 0 and 1.
  25. // Values of dt < 0 are set to 0.
  26. //
  27. func NewThrottle(r float64, dt time.Duration) *Throttle {
  28. var f float64
  29. switch {
  30. case r <= 0:
  31. f = -1 // indicates always sleep
  32. case r >= 1:
  33. f = 0 // assume r == 1 (never sleep)
  34. default:
  35. // 0 < r < 1
  36. f = (1 - r) / r
  37. }
  38. if dt < 0 {
  39. dt = 0
  40. }
  41. return &Throttle{f: f, dt: dt, tt: time.Now().Add(dt)}
  42. }
  43. // Throttle calls time.Sleep such that over time the ratio tr/ts between
  44. // accumulated run (tr) and sleep times (ts) approximates the value 1/(1-r)
  45. // where r is the throttle value. Throttle returns immediately (w/o sleeping)
  46. // if less than tm ns have passed since the last call to Throttle.
  47. //
  48. func (p *Throttle) Throttle() {
  49. if p.f < 0 {
  50. select {} // always sleep
  51. }
  52. t0 := time.Now()
  53. if t0.Before(p.tt) {
  54. return // keep running (minimum time slice not exhausted yet)
  55. }
  56. // accumulate running time
  57. p.tr += t0.Sub(p.tt) + p.dt
  58. // compute sleep time
  59. // Over time we want:
  60. //
  61. // tr/ts = r/(1-r)
  62. //
  63. // Thus:
  64. //
  65. // ts = tr*f with f = (1-r)/r
  66. //
  67. // After some incremental run time δr added to the total run time
  68. // tr, the incremental sleep-time δs to get to the same ratio again
  69. // after waking up from time.Sleep is:
  70. if δs := time.Duration(float64(p.tr)*p.f) - p.ts; δs > 0 {
  71. time.Sleep(δs)
  72. }
  73. // accumulate (actual) sleep time
  74. t1 := time.Now()
  75. p.ts += t1.Sub(t0)
  76. // set earliest next throttle time
  77. p.tt = t1.Add(p.dt)
  78. }