BackoffTimeoutHelper.cs 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134
  1. //-----------------------------------------------------------------------------
  2. // Copyright (c) Microsoft Corporation. All rights reserved.
  3. //-----------------------------------------------------------------------------
  4. namespace System.Runtime
  5. {
  6. using System;
  7. using System.Threading;
  8. sealed class BackoffTimeoutHelper
  9. {
  10. readonly static int maxSkewMilliseconds = (int)(IOThreadTimer.SystemTimeResolutionTicks / TimeSpan.TicksPerMillisecond);
  11. readonly static long maxDriftTicks = IOThreadTimer.SystemTimeResolutionTicks * 2;
  12. readonly static TimeSpan defaultInitialWaitTime = TimeSpan.FromMilliseconds(1);
  13. readonly static TimeSpan defaultMaxWaitTime = TimeSpan.FromMinutes(1);
  14. DateTime deadline;
  15. TimeSpan maxWaitTime;
  16. TimeSpan waitTime;
  17. IOThreadTimer backoffTimer;
  18. Action<object> backoffCallback;
  19. object backoffState;
  20. Random random;
  21. TimeSpan originalTimeout;
  22. internal BackoffTimeoutHelper(TimeSpan timeout)
  23. : this(timeout, BackoffTimeoutHelper.defaultMaxWaitTime)
  24. {
  25. }
  26. internal BackoffTimeoutHelper(TimeSpan timeout, TimeSpan maxWaitTime)
  27. : this(timeout, maxWaitTime, BackoffTimeoutHelper.defaultInitialWaitTime)
  28. {
  29. }
  30. internal BackoffTimeoutHelper(TimeSpan timeout, TimeSpan maxWaitTime, TimeSpan initialWaitTime)
  31. {
  32. this.random = new Random(GetHashCode());
  33. this.maxWaitTime = maxWaitTime;
  34. this.originalTimeout = timeout;
  35. Reset(timeout, initialWaitTime);
  36. }
  37. public TimeSpan OriginalTimeout
  38. {
  39. get
  40. {
  41. return this.originalTimeout;
  42. }
  43. }
  44. void Reset(TimeSpan timeout, TimeSpan initialWaitTime)
  45. {
  46. if (timeout == TimeSpan.MaxValue)
  47. {
  48. this.deadline = DateTime.MaxValue;
  49. }
  50. else
  51. {
  52. this.deadline = DateTime.UtcNow + timeout;
  53. }
  54. this.waitTime = initialWaitTime;
  55. }
  56. public bool IsExpired()
  57. {
  58. if (this.deadline == DateTime.MaxValue)
  59. {
  60. return false;
  61. }
  62. else
  63. {
  64. return (DateTime.UtcNow >= this.deadline);
  65. }
  66. }
  67. public void WaitAndBackoff(Action<object> callback, object state)
  68. {
  69. if (this.backoffCallback != callback || this.backoffState != state)
  70. {
  71. if (this.backoffTimer != null)
  72. {
  73. this.backoffTimer.Cancel();
  74. }
  75. this.backoffCallback = callback;
  76. this.backoffState = state;
  77. this.backoffTimer = new IOThreadTimer(callback, state, false, BackoffTimeoutHelper.maxSkewMilliseconds);
  78. }
  79. TimeSpan backoffTime = WaitTimeWithDrift();
  80. Backoff();
  81. this.backoffTimer.Set(backoffTime);
  82. }
  83. public void WaitAndBackoff()
  84. {
  85. Thread.Sleep(WaitTimeWithDrift());
  86. Backoff();
  87. }
  88. TimeSpan WaitTimeWithDrift()
  89. {
  90. return Ticks.ToTimeSpan(Math.Max(
  91. Ticks.FromTimeSpan(BackoffTimeoutHelper.defaultInitialWaitTime),
  92. Ticks.Add(Ticks.FromTimeSpan(this.waitTime),
  93. (long)(uint)this.random.Next() % (2 * BackoffTimeoutHelper.maxDriftTicks + 1) - BackoffTimeoutHelper.maxDriftTicks)));
  94. }
  95. void Backoff()
  96. {
  97. if (waitTime.Ticks >= (maxWaitTime.Ticks / 2))
  98. {
  99. waitTime = maxWaitTime;
  100. }
  101. else
  102. {
  103. waitTime = TimeSpan.FromTicks(waitTime.Ticks * 2);
  104. }
  105. if (this.deadline != DateTime.MaxValue)
  106. {
  107. TimeSpan remainingTime = this.deadline - DateTime.UtcNow;
  108. if (this.waitTime > remainingTime)
  109. {
  110. this.waitTime = remainingTime;
  111. if (this.waitTime < TimeSpan.Zero)
  112. {
  113. this.waitTime = TimeSpan.Zero;
  114. }
  115. }
  116. }
  117. }
  118. }
  119. }