Пример #1
0
        public static CancellationToken FromTimeout(int millisecondsTimeout)
        {
            // Note that CancellationTokenSource constructor requires input to be >= -1,
            // restricting millisecondsTimeout to be >= -1 would enforce that
            if (millisecondsTimeout < -1)
            {
                throw new ArgumentOutOfRangeException("Invalid millisecondsTimeout value " + millisecondsTimeout);
            }

            // To prevent s_tokenCache growing too large, we have to adjust the granularity of the our coalesce depending
            // on the value of millisecondsTimeout. The coalescing span scales proportionally with millisecondsTimeout which
            // would guarantee constant s_tokenCache size in the case where similar millisecondsTimeout values are accepted.
            // If the method is given a wildly different millisecondsTimeout values all the time, the dictionary would still
            // only grow logarithmically with respect to the range of the input values

            uint currentTime = (uint)Environment.TickCount;
            long targetTime  = millisecondsTimeout + currentTime;

            // Formula for our coalescing span:
            // Divide millisecondsTimeout by SegmentationFactor and take the highest bit and then multiply CoalescingFactor back
            int segmentValue     = millisecondsTimeout / SegmentationFactor;
            int coalescingSpanMs = CoalescingFactor;

            while (segmentValue > 0)
            {
                segmentValue     >>= 1;
                coalescingSpanMs <<= 1;
            }
            targetTime = ((targetTime + (coalescingSpanMs - 1)) / coalescingSpanMs) * coalescingSpanMs;

            if (!s_timerCache.TryGetValue(targetTime, out CancellationTokenSourceIOThreadTimer ctsTimer))
            {
                ctsTimer = new CancellationTokenSourceIOThreadTimer();

                // only a single thread may succeed adding its timer into the cache
                if (s_timerCache.TryAdd(targetTime, ctsTimer))
                {
                    // Clean up cache when timer fires
                    ctsTimer.SetCompletionCallback(s_deregisterTimer, targetTime);
                    ctsTimer.Set((int)(targetTime - currentTime));
                }
                else
                {
                    // for threads that failed when calling TryAdd, there should be one already in the cache
                    if (!s_timerCache.TryGetValue(targetTime, out ctsTimer))
                    {
                        // In unlikely scenario the timer has already fired, we would not find it in cache.
                        // In this case we would simply create a CTS which doesn't use the coalesced timer.
                        var cts = new RecoverableTimeoutCancellationTokenSource(millisecondsTimeout);
                        cts.CancelAfter(millisecondsTimeout);
                        return(cts.Token);
                    }
                }
            }

            var tokenSource = new RecoverableTimeoutCancellationTokenSource(millisecondsTimeout);

            ctsTimer.RegisterTokenSourceForCancellation(tokenSource);
            return(tokenSource.Token);
        }
Пример #2
0
 public static TimeSpan GetOriginalTimeout(CancellationToken token)
 {
     return(RecoverableTimeoutCancellationTokenSource.GetOriginalTimeout(token));
 }