| | | 1 | | // Licensed to the .NET Foundation under one or more agreements. |
| | | 2 | | // The .NET Foundation licenses this file to you under the MIT license. |
| | | 3 | | |
| | | 4 | | using System; |
| | | 5 | | using System.Threading; |
| | | 6 | | |
| | | 7 | | namespace CoreWCF.Runtime |
| | | 8 | | { |
| | | 9 | | internal sealed class BackoffTimeoutHelper |
| | | 10 | | { |
| | 0 | 11 | | private static readonly int s_maxSkewMilliseconds = 15; |
| | 0 | 12 | | private static readonly long s_maxDriftTicks = s_maxSkewMilliseconds * 2 * TimeSpan.TicksPerMillisecond; |
| | 0 | 13 | | private static readonly TimeSpan s_defaultInitialWaitTime = TimeSpan.FromMilliseconds(1); |
| | 0 | 14 | | private static readonly TimeSpan s_defaultMaxWaitTime = TimeSpan.FromMinutes(1); |
| | | 15 | | private DateTime _deadline; |
| | | 16 | | private TimeSpan _maxWaitTime; |
| | | 17 | | private TimeSpan _waitTime; |
| | | 18 | | private IOThreadTimer _backoffTimer; |
| | | 19 | | private Action<object> _backoffCallback; |
| | | 20 | | private object _backoffState; |
| | | 21 | | private readonly Random _random; |
| | | 22 | | private TimeSpan _originalTimeout; |
| | | 23 | | |
| | | 24 | | internal BackoffTimeoutHelper(TimeSpan timeout) |
| | 0 | 25 | | : this(timeout, s_defaultMaxWaitTime) |
| | | 26 | | { |
| | 0 | 27 | | } |
| | | 28 | | |
| | | 29 | | internal BackoffTimeoutHelper(TimeSpan timeout, TimeSpan maxWaitTime) |
| | 0 | 30 | | : this(timeout, maxWaitTime, s_defaultInitialWaitTime) |
| | | 31 | | { |
| | 0 | 32 | | } |
| | | 33 | | |
| | 0 | 34 | | internal BackoffTimeoutHelper(TimeSpan timeout, TimeSpan maxWaitTime, TimeSpan initialWaitTime) |
| | | 35 | | { |
| | 0 | 36 | | _random = new Random(GetHashCode()); |
| | 0 | 37 | | _maxWaitTime = maxWaitTime; |
| | 0 | 38 | | _originalTimeout = timeout; |
| | 0 | 39 | | Reset(timeout, initialWaitTime); |
| | 0 | 40 | | } |
| | | 41 | | |
| | | 42 | | public TimeSpan OriginalTimeout |
| | | 43 | | { |
| | | 44 | | get |
| | | 45 | | { |
| | 0 | 46 | | return _originalTimeout; |
| | | 47 | | } |
| | | 48 | | } |
| | | 49 | | |
| | | 50 | | private void Reset(TimeSpan timeout, TimeSpan initialWaitTime) |
| | | 51 | | { |
| | 0 | 52 | | if (timeout == TimeSpan.MaxValue) |
| | | 53 | | { |
| | 0 | 54 | | _deadline = DateTime.MaxValue; |
| | | 55 | | } |
| | | 56 | | else |
| | | 57 | | { |
| | 0 | 58 | | _deadline = DateTime.UtcNow + timeout; |
| | | 59 | | } |
| | 0 | 60 | | _waitTime = initialWaitTime; |
| | 0 | 61 | | } |
| | | 62 | | |
| | | 63 | | public bool IsExpired() |
| | | 64 | | { |
| | 0 | 65 | | if (_deadline == DateTime.MaxValue) |
| | | 66 | | { |
| | 0 | 67 | | return false; |
| | | 68 | | } |
| | | 69 | | else |
| | | 70 | | { |
| | 0 | 71 | | return (DateTime.UtcNow >= _deadline); |
| | | 72 | | } |
| | | 73 | | } |
| | | 74 | | |
| | | 75 | | public void WaitAndBackoff(Action<object> callback, object state) |
| | | 76 | | { |
| | 0 | 77 | | if (_backoffCallback != callback || _backoffState != state) |
| | | 78 | | { |
| | 0 | 79 | | if (_backoffTimer != null) |
| | | 80 | | { |
| | 0 | 81 | | _backoffTimer.Cancel(); |
| | | 82 | | } |
| | 0 | 83 | | _backoffCallback = callback; |
| | 0 | 84 | | _backoffState = state; |
| | 0 | 85 | | _backoffTimer = new IOThreadTimer(callback, state, false, s_maxSkewMilliseconds); |
| | | 86 | | } |
| | | 87 | | |
| | 0 | 88 | | TimeSpan backoffTime = WaitTimeWithDrift(); |
| | 0 | 89 | | Backoff(); |
| | 0 | 90 | | _backoffTimer.Set(backoffTime); |
| | 0 | 91 | | } |
| | | 92 | | |
| | | 93 | | // TODO: Consider making Async |
| | | 94 | | public void WaitAndBackoff() |
| | | 95 | | { |
| | 0 | 96 | | Thread.Sleep(WaitTimeWithDrift()); |
| | 0 | 97 | | Backoff(); |
| | 0 | 98 | | } |
| | | 99 | | |
| | | 100 | | private TimeSpan WaitTimeWithDrift() |
| | | 101 | | { |
| | 0 | 102 | | return Ticks.ToTimeSpan(Math.Max( |
| | 0 | 103 | | Ticks.FromTimeSpan(s_defaultInitialWaitTime), |
| | 0 | 104 | | Ticks.Add(Ticks.FromTimeSpan(_waitTime), |
| | 0 | 105 | | (long)(uint)_random.Next() % (2 * s_maxDriftTicks + 1) - s_maxDriftTicks))); |
| | | 106 | | } |
| | | 107 | | |
| | | 108 | | private void Backoff() |
| | | 109 | | { |
| | 0 | 110 | | if (_waitTime.Ticks >= (_maxWaitTime.Ticks / 2)) |
| | | 111 | | { |
| | 0 | 112 | | _waitTime = _maxWaitTime; |
| | | 113 | | } |
| | | 114 | | else |
| | | 115 | | { |
| | 0 | 116 | | _waitTime = TimeSpan.FromTicks(_waitTime.Ticks * 2); |
| | | 117 | | } |
| | | 118 | | |
| | 0 | 119 | | if (_deadline != DateTime.MaxValue) |
| | | 120 | | { |
| | 0 | 121 | | TimeSpan remainingTime = _deadline - DateTime.UtcNow; |
| | 0 | 122 | | if (_waitTime > remainingTime) |
| | | 123 | | { |
| | 0 | 124 | | _waitTime = remainingTime; |
| | 0 | 125 | | if (_waitTime < TimeSpan.Zero) |
| | | 126 | | { |
| | 0 | 127 | | _waitTime = TimeSpan.Zero; |
| | | 128 | | } |
| | | 129 | | } |
| | | 130 | | } |
| | 0 | 131 | | } |
| | | 132 | | } |
| | | 133 | | } |