예제 #1
0
        public async ValueTask <IDisposable?> TryAcquireAsync(TimeoutValue timeout, CancellationToken cancellationToken)
        {
            var acquired = SyncViaAsync.IsSynchronous
                ? this._semaphore.Wait(timeout.InMilliseconds, cancellationToken)
                : await this._semaphore.WaitAsync(timeout.InMilliseconds, cancellationToken).ConfigureAwait(false);

            return(acquired ? new Handle(this._semaphore) : null);
        }
예제 #2
0
        public static async ValueTask <TResult?> WaitAsync <TState, TResult>(
            TState state,
            Func <TState, CancellationToken, ValueTask <TResult?> > tryGetValue,
            TimeoutValue timeout,
            TimeoutValue minSleepTime,
            TimeoutValue maxSleepTime,
            CancellationToken cancellationToken)
            where TResult : class
        {
            Invariant.Require(minSleepTime.CompareTo(maxSleepTime) <= 0);
            Invariant.Require(!maxSleepTime.IsInfinite);

            var initialResult = await tryGetValue(state, cancellationToken).ConfigureAwait(false);

            if (initialResult != null || timeout.IsZero)
            {
                return(initialResult);
            }

            using var _ = CreateMergedCancellationTokenSourceSource(timeout, cancellationToken, out var mergedCancellationToken);

            var random           = new Random(Guid.NewGuid().GetHashCode());
            var sleepRangeMillis = maxSleepTime.InMilliseconds - minSleepTime.InMilliseconds;

            while (true)
            {
                var sleepTime = minSleepTime.TimeSpan + TimeSpan.FromMilliseconds(random.NextDouble() * sleepRangeMillis);
                try
                {
                    await SyncViaAsync.Delay(sleepTime, mergedCancellationToken).ConfigureAwait(false);
                }
                catch (OperationCanceledException) when(IsTimedOut())
                {
                    // if we time out while sleeping, always try one more time with just the regular token
                    return(await tryGetValue(state, cancellationToken).ConfigureAwait(false));
                }

                try
                {
                    var result = await tryGetValue(state, mergedCancellationToken).ConfigureAwait(false);

                    if (result != null)
                    {
                        return(result);
                    }
                }
                catch (OperationCanceledException) when(IsTimedOut())
                {
                    return(null);
                }
            }

            bool IsTimedOut() =>
            mergedCancellationToken.IsCancellationRequested && !cancellationToken.IsCancellationRequested;
        }
예제 #3
0
        /// <summary>
        /// A <see cref="SyncViaAsync"/>-compatible implementation of <see cref="Task.Delay(TimeSpan, CancellationToken)"/>.
        /// </summary>
        public static ValueTask Delay(TimeoutValue timeout, CancellationToken cancellationToken)
        {
            if (!IsSynchronous)
            {
                return(Task.Delay(timeout.InMilliseconds, cancellationToken).AsValueTask());
            }

            if (cancellationToken.CanBeCanceled)
            {
                if (cancellationToken.WaitHandle.WaitOne(timeout.InMilliseconds))
                {
                    throw new OperationCanceledException("delay was canceled", cancellationToken);
                }
            }
            else
            {
                Thread.Sleep(timeout.InMilliseconds);
            }

            return(default);
예제 #4
0
        private static IDisposable?CreateMergedCancellationTokenSourceSource(TimeoutValue timeout, CancellationToken cancellationToken, out CancellationToken mergedCancellationToken)
        {
            if (timeout.IsInfinite)
            {
                mergedCancellationToken = cancellationToken;
                return(null);
            }

            if (!cancellationToken.CanBeCanceled)
            {
                var timeoutSource = new CancellationTokenSource(millisecondsDelay: timeout.InMilliseconds);
                mergedCancellationToken = timeoutSource.Token;
                return(timeoutSource);
            }

            var mergedSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);

            mergedSource.CancelAfter(timeout.InMilliseconds);
            mergedCancellationToken = mergedSource.Token;
            return(mergedSource);
        }
예제 #5
0
        private static Task CreateMonitoringLoopTask(WeakReference <LeaseMonitor> weakMonitor, TimeoutValue monitoringCadence, CancellationToken disposalToken)
        {
            return(Task.Run(() => MonitoringLoop()));

            async Task MonitoringLoop()
            {
                var leaseLifetime = Stopwatch.StartNew();

                do
                {
                    // wait until the next monitoring check
                    await Task.Delay(monitoringCadence.InMilliseconds, disposalToken).TryAwait();
                }while (!disposalToken.IsCancellationRequested && await RunMonitoringLoopIterationAsync(weakMonitor, leaseLifetime).ConfigureAwait(false));
            }
        }