internal AsyncProducerConsumerQueue <T> TryEnqueueInternal(T item, CancellationToken ct) { try { using (var source = CancellationTokenHelper.Aggregate(completed.Token, ct)) { using (mutex.Lock()) { while (IsFull) { notFull.Wait(source.Token); } if (completed.IsCancellationRequested) { return(null); } queue.Enqueue(item); completedOrNotEmpty.Notify(); return(this); } } } catch (OperationCanceledException) { return(null); } }
public async Task CreateCrisis_RequestOk_ReturnViewModel() { using var context = new AppDbContext(DbHelper.GetDbContextOptions()); context.Teams.Add(new Team(name: "Team 1", description: "Description 1")); context.Teams.Add(new Team(name: "Team 2", description: "Description 2")); context.SaveChanges(); var request = new CreateCrisisRequest() { Name = "test", Description = "test", TeamIds = new List <int>() { 1, 2 } }; var handler = new CreateCrisisHandler(context); var result = await handler.Handle(request, CancellationTokenHelper.GetCancellationToken()); result.IsSuccess.Should().BeTrue(); result.Value.Should().BeOfType <BaseCrisisViewModel>(); result.Value.Name.Should().Be("test"); result.Value.Id.Should().NotBe(0); result.Value.Teams.Count.Should().Be(2); }
internal async Task <AsyncProducerConsumerQueue <T> > TryEnqueueAsync(T item, CancellationToken ct, TaskCompletionSource abort) { try { using (var source = CancellationTokenHelper.Aggregate(completed.Token, ct)) { using (await mutex.LockAsync().ConfigureAwait(false)) { while (IsFull) { await notFull.WaitAsync(source.Token).ConfigureAwait(false); } if (completed.IsCancellationRequested) { return(null); } if (null != abort && false == abort.TrySetCanceled()) { return(null); } queue.Enqueue(item); completedOrNotEmpty.Notify(); return(this); } } } catch (OperationCanceledException) { return(null); } }
/// <summary> /// /// </summary> /// <typeparam name="T"></typeparam> /// <param name="queues"></param> /// <param name="ct"></param> /// <returns></returns> public static async Task <AsyncProducerConsumerQueue <T> .DequeueResult> TryDequeueFromAnyAsync <T>( this IEnumerable <AsyncProducerConsumerQueue <T> > queues, CancellationToken ct) { var abort = new TaskCompletionSource(); using (var cancellation = CancellationTokenHelper.FromTask(abort.Task)) { using (var aggregation = CancellationTokenHelper.Aggregate(cancellation.Token, ct)) { var token = aggregation.Token; var tasks = queues.Select(queue => queue.TryDequeueAsync(token, abort)); var results = await Task.WhenAll(tasks).ConfigureAwait(false); var result = results.FirstOrDefault(value => value.Success); if (null != result) { return(result); } ct.ThrowIfCancellationRequested(); return(AsyncProducerConsumerQueue <T> .FalseResult); } } }
//[MethodImpl(MethodImplOptions.AggressiveInlining)] private async ValueTask RunWithTimeout(Func <CancellationToken, ValueTask> action, CancellationToken originalCancellationToken) { originalCancellationToken.ThrowIfCancellationRequested(); var cancellationTokenWithTimeout = CancellationTokenHelper.CancellationTokenWithTimeout(ClientConfig.Timeout, originalCancellationToken); try { await action.Invoke(cancellationTokenWithTimeout.Token); } catch (OperationCanceledException operationCanceledException) { throw TimeoutOrCancelledException(operationCanceledException, originalCancellationToken); } catch (SocketException socketException) { if (socketException.InnerException?.GetType().IsAssignableFrom(typeof(OperationCanceledException)) == true) { throw TimeoutOrCancelledException(socketException.InnerException, originalCancellationToken); } throw; } finally { cancellationTokenWithTimeout.Dispose(); } }
public async Task <HttpResponseMessage> Delete(string endpoint, NameValuePair deleteParam, CancellationToken?token = null) { var url = GetUrl(endpoint, out var _); try { var forms = deleteParam != null ? new[] { deleteParam }.AsKeyValuePairs() : new KeyValuePair <string, string> [0]; using (var content = new FormUrlEncodedContent(forms)) using (var request = new HttpRequestMessage(new HttpMethod("DELETE"), url) { Content = content }) using (var tokenHelper = new CancellationTokenHelper(token, Timeout)) { return(await HttpClient.SendAsync(request, tokenHelper.Token)); } } catch (BacklogException) { throw; } catch (Exception ex) { throw new BacklogApiException("backlog api request failed.", ex); } }
/// <summary> /// Blocks the current thread until the next notification /// </summary> /// <param name="timeout">Tiemout in milliseconds</param> /// <param name="token">Cancellation token</param> /// <returns>True if the current thread successfully received a notification</returns> /// <exception cref="SynchronizationLockException">Lock is not acquired</exception> /// <exception cref="ObjectDisposedException">Waiter was disposed</exception> /// <exception cref="OperationCanceledException">Cancellation happened</exception> /// <exception cref="OperationInterruptedException">Waiting was interrupted by Dispose</exception> public bool Wait(int timeout, CancellationToken token) { if (!Monitor.IsEntered(this)) { throw new SynchronizationLockException("Lock on the current SignalWaiter should be acquired"); } if (_isDisposed) { throw new ObjectDisposedException(this.GetType().Name); } if (token.IsCancellationRequested) { throw new OperationCanceledException(token); } if (timeout < 0) { timeout = Timeout.Infinite; } CancellationTokenRegistration cancellationTokenRegistration = default(CancellationTokenRegistration); try { if (token.CanBeCanceled) { cancellationTokenRegistration = CancellationTokenHelper.RegisterWithoutECIfPossible(token, _cancellationTokenCanceledEventHandler, this); } // Waiting for signal if (!Monitor.Wait(this, timeout)) { return(false); } // Check if cancellation or dispose was the reasons of the signal if (token.IsCancellationRequested) { throw new OperationCanceledException(token); } if (_isDisposed) { throw new OperationInterruptedException("Wait was interrupted by Dispose", new ObjectDisposedException(this.GetType().Name)); } } finally { cancellationTokenRegistration.Dispose(); } return(true); }
/// <summary> /// Enter the lock on the current <see cref="ConditionVariable"/> object /// </summary> /// <param name="timeout">Total operation timeout</param> /// <param name="token">Cancellation token</param> /// <returns>Lock guard to work with 'using' statement</returns> /// <exception cref="ObjectDisposedException">ConditionVariable disposed</exception> /// <exception cref="OperationCanceledException">Cancellation requested</exception> /// <exception cref="SynchronizationLockException">externalLock is already acquired</exception> public ConditionVariableWaiter Enter(int timeout, CancellationToken token) { if (_isDisposed) { throw new ObjectDisposedException(nameof(ConditionVariable), $"ConditionVariable '{Name}' was disposed"); } if (token.IsCancellationRequested) { throw new OperationCanceledException(token); } if (Monitor.IsEntered(_externalLock)) { throw new SynchronizationLockException("Recursive lock is not supported"); } uint startTime = 0; if (timeout > 0) { startTime = TimeoutHelper.GetTimestamp(); } else if (timeout < -1) { timeout = Timeout.Infinite; } CancellationTokenRegistration cancellationTokenRegistration = default(CancellationTokenRegistration); if (token.CanBeCanceled) { try { cancellationTokenRegistration = CancellationTokenHelper.RegisterWithoutECIfPossible(token, _cancellationTokenCanceledEventHandler, this); Monitor.Enter(_externalLock); // Can be interrupted } catch { cancellationTokenRegistration.Dispose(); throw; } } else { Monitor.Enter(_externalLock); } Interlocked.Increment(ref _waiterCount); return(new ConditionVariableWaiter(this, timeout, startTime, token, cancellationTokenRegistration)); }
private static void TestGeneratedRegister(CancellationToken token, int iter) { Action <object> act = new Action <object>(Callback); var sw = Stopwatch.StartNew(); for (int i = 0; i < iter; i++) { using (var reg = CancellationTokenHelper.RegisterWithoutECIfPossible(token, act, null)) { } } sw.Stop(); Console.WriteLine($"Generated register: {sw.ElapsedMilliseconds}ms"); }
public async Task CreateCrisis_RequestTeamIdsEmpty_ReturnFail() { var request = new CreateCrisisRequest() { Name = "test", TeamIds = new List <int>() }; using var context = new AppDbContext(DbHelper.GetDbContextOptions()); var handler = new CreateCrisisHandler(context); var result = await handler.Handle(request, CancellationTokenHelper.GetCancellationToken()); result.IsFailed.Should().BeTrue(); result.WithError("Request team empty"); }
/// <summary> /// Enter the lock on the current <see cref="MonitorObject"/> object /// </summary> /// <param name="timeout">Total operation timeout</param> /// <param name="token">Cancellation token</param> /// <returns>Lock guard to work with 'using' statement</returns> /// <exception cref="ObjectDisposedException">MonitorObject disposed</exception> /// <exception cref="OperationCanceledException">Cancellation requested</exception> public MonitorWaiter Enter(int timeout, CancellationToken token) { if (_isDisposed) { throw new ObjectDisposedException(nameof(MonitorObject), $"MonitorObject '{Name}' was disposed"); } if (token.IsCancellationRequested) { throw new OperationCanceledException(token); } uint startTime = 0; if (timeout > 0) { startTime = TimeoutHelper.GetTimestamp(); } else if (timeout < -1) { timeout = Timeout.Infinite; } CancellationTokenRegistration cancellationTokenRegistration = default(CancellationTokenRegistration); if (token.CanBeCanceled) { try { cancellationTokenRegistration = CancellationTokenHelper.RegisterWithoutECIfPossible(token, _cancellationTokenCanceledEventHandler, this); Monitor.Enter(this); // Can be interrupted } catch { cancellationTokenRegistration.Dispose(); throw; } } else { Monitor.Enter(this); } Interlocked.Increment(ref _waiterCount); return(new MonitorWaiter(this, timeout, startTime, token, cancellationTokenRegistration)); }
public async Task <HttpResponseMessage> Post(string endpoint, ICollection <NameValuePair> postParams, CancellationToken?token = null) { var url = GetUrl(endpoint, out var _); try { using (var content = new FormUrlEncodedContent(postParams.AsKeyValuePairs())) using (var request = CreateRequestMessage(HttpMethod.Post, url, content)) using (var tokenHelper = new CancellationTokenHelper(token, Timeout)) { return(await HttpClient.SendAsync(request, HttpCompletionOption.ResponseContentRead, tokenHelper.Token)); } } catch (BacklogException) { throw; } catch (Exception ex) { throw new BacklogApiException("backlog api request failed.", ex); } }
public override async void OnNavigatedTo(INavigationParameters parameters) { try { _cts = CancellationTokenHelper.SetupCancellationToken(_cts); switch (parameters.GetNavigationMode()) { case NavigationMode.Back: // TODO: Handle any tasks that should occur only when navigated back to break; case NavigationMode.New: IsBusy = true; break; } } catch (Exception ex) { LogError(ex); } }
public void RegisterWithoutECTest() { var originalContext = SynchronizationContext.Current; var syncContext = new CustomSyncContext(); try { CancellationTokenSource tokenSource = new CancellationTokenSource(); CancellationToken token = tokenSource.Token; AtomicBool isNoExecutionContext = new AtomicBool(false); using (CancellationTokenHelper.RegisterWithoutECIfPossible(token, (st) => { isNoExecutionContext.Value = SynchronizationContext.Current == null; }, null)) { Barrier barrier = new Barrier(2); Barrier barrier2 = new Barrier(2); Task.Run(() => { barrier.SignalAndWait(); barrier2.SignalAndWait(); tokenSource.Cancel(); }); barrier.SignalAndWait(); SynchronizationContext.SetSynchronizationContext(syncContext); barrier2.SignalAndWait(); TimingAssert.IsTrue(10000, isNoExecutionContext, "isNoExecutionContext"); } } finally { SynchronizationContext.SetSynchronizationContext(originalContext); } }
public async Task <HttpResponseMessage> Get(string endpoint, GetParams getParams, QueryParams queryParams, CancellationToken?token = null) { try { bool paramExists; var url = new StringBuilder(GetUrl(endpoint, out paramExists)); SetParamString(url, paramExists, (getParams?.Parameters ?? new NameValuePair[0]).Concat(queryParams?.Parameters ?? new NameValuePair[0])); using (var request = CreateRequestMessage(HttpMethod.Get, url.ToString())) using (var tokenHelper = new CancellationTokenHelper(token, Timeout)) { return(await HttpClient.SendAsync(request, HttpCompletionOption.ResponseContentRead, tokenHelper.Token)); } } catch (BacklogException) { throw; } catch (Exception ex) { throw new BacklogApiException("backlog api request failed.", ex); } }
public async Task <HttpResponseMessage> PostMultiPart(string endpoint, ICollection <KeyValuePair <string, object> > postParams, CancellationToken?token = null) { var url = GetUrl(endpoint, out var _); try { using (var content = new MultipartFormDataContent()) using (var tokenHelper = new CancellationTokenHelper(token, Timeout)) { foreach (var param in postParams) { if (param.Value is string strValue) { content.Add(new StringContent(strValue), param.Key); } else if (param.Value is AttachmentData attach) { content.Add(new StreamContent(attach.Content), param.Key, attach.FileName); } else { throw new BacklogApiException($"Illegal parameter type name={param.Key},value={param.Value}"); } } return(await HttpClient.PostAsync(url, content, tokenHelper.Token)); } } catch (BacklogException) { throw; } catch (Exception ex) { throw new BacklogApiException("backlog api request failed.", ex); } }
public async Task CreateCrisis_NotAllTeamsFound_ReturnFail() { var context = new AppDbContext(DbHelper.GetDbContextOptions()); context.Teams.Add(new Team(name: "Team 1", description: "Description 1")); context.Teams.Add(new Team(name: "Team 2", description: "Description 2")); context.SaveChanges(); var request = new CreateCrisisRequest() { Name = "test", Description = "test", TeamIds = new List <int>() { 1, 2, 3 } }; var handler = new CreateCrisisHandler(context); var result = await handler.Handle(request, CancellationTokenHelper.GetCancellationToken()); result.IsFailed.Should().BeTrue(); result.WithError("Not all teams found"); }
/// <summary> /// /// </summary> /// <typeparam name="T"></typeparam> /// <param name="queues"></param> /// <param name="item"></param> /// <param name="ct"></param> /// <returns></returns> public static async Task <AsyncProducerConsumerQueue <T> > TryEnqueueToAnyAsync <T>( this IEnumerable <AsyncProducerConsumerQueue <T> > queues, T item, CancellationToken ct) { var abort = new TaskCompletionSource(); using (var cancellation = CancellationTokenHelper.FromTask(abort.Task)) { using (var aggregation = CancellationTokenHelper.Aggregate(cancellation.Token, ct)) { var token = aggregation.Token; var tasks = queues.Select(queue => queue.TryEnqueueAsync(item, token, abort)); var results = await Task.WhenAll(tasks).ConfigureAwait(false); var candidate = results.FirstOrDefault(value => null != value); if (null == candidate) { ct.ThrowIfCancellationRequested(); } return(candidate); } } }
/// <summary> /// Slow path /// </summary> private bool WaitSlowPath <TState>(WaitPredicate <TState> predicate, TState state, uint startTime, int timeout, CancellationToken token) { if (token.IsCancellationRequested) { throw new OperationCanceledException(token); } if (timeout < 0) { timeout = Timeout.Infinite; } if (timeout > 0 && TimeoutHelper.UpdateTimeout(startTime, timeout) <= 0) // Predicate estimation took too much time { return(false); } CancellationTokenRegistration cancellationTokenRegistration = default(CancellationTokenRegistration); bool internalLockTaken = false; bool externalLockTaken = true; try { if (token.CanBeCanceled) { cancellationTokenRegistration = CancellationTokenHelper.RegisterWithoutECIfPossible(token, _cancellationTokenCanceledEventHandler, this); } try { } finally { Monitor.Enter(_internalLock, ref internalLockTaken); TurboContract.Assert(internalLockTaken, conditionString: "internalLockTaken"); _waitCount++; } if (WaitUntilPredicate(ref internalLockTaken, ref externalLockTaken, predicate, state, startTime, timeout, token)) { return(true); } if (token.IsCancellationRequested) { throw new OperationCanceledException(token); } if (_isDisposed) { throw new OperationInterruptedException("Wait was interrupted by Dispose", new ObjectDisposedException(this.GetType().Name)); } } finally { if (internalLockTaken) { _waitCount--; TurboContract.Assert(_waitCount >= 0, conditionString: "_waitCount >= 0"); Monitor.Exit(_internalLock); } EnterLock(_externalLock, ref externalLockTaken); cancellationTokenRegistration.Dispose(); } // Final check for predicate return(predicate(state)); }
/// <summary> /// Blocks the current thread until the next notification /// </summary> /// <param name="timeout">Tiemout in milliseconds</param> /// <param name="token">Cancellation token</param> /// <returns>True if the current thread successfully received a notification</returns> /// <exception cref="SynchronizationLockException">externalLock is not acquired or acquired recursively</exception> /// <exception cref="ObjectDisposedException">ConditionVariable was disposed</exception> /// <exception cref="OperationCanceledException">Cancellation happened</exception> /// <exception cref="OperationInterruptedException">Waiting was interrupted by Dispose</exception> public bool Wait(int timeout, CancellationToken token) { if (!Monitor.IsEntered(_externalLock)) { throw new SynchronizationLockException("External lock should be acquired"); } if (_isDisposed) { throw new ObjectDisposedException(this.GetType().Name); } if (token.IsCancellationRequested) { throw new OperationCanceledException(token); } uint startTime = 0; if (timeout > 0) { startTime = TimeoutHelper.GetTimestamp(); } else if (timeout < 0) { timeout = Timeout.Infinite; } CancellationTokenRegistration cancellationTokenRegistration = default(CancellationTokenRegistration); bool internalLockTaken = false; bool externalLockTaken = true; try { if (token.CanBeCanceled) { cancellationTokenRegistration = CancellationTokenHelper.RegisterWithoutECIfPossible(token, _cancellationTokenCanceledEventHandler, this); } try { } finally { Monitor.Enter(_internalLock, ref internalLockTaken); TurboContract.Assert(internalLockTaken, conditionString: "internalLockTaken"); _waitCount++; } // Check if cancelled or disposed after entering the lock if (token.IsCancellationRequested) { throw new OperationCanceledException(token); } if (_isDisposed) { throw new OperationInterruptedException("Wait was interrupted by Dispose", new ObjectDisposedException(this.GetType().Name)); } // Calculate remaining timeout int remainingWaitMilliseconds = Timeout.Infinite; if (timeout != Timeout.Infinite) { remainingWaitMilliseconds = TimeoutHelper.UpdateTimeout(startTime, timeout); if (remainingWaitMilliseconds <= 0) { return(false); } } // Exit external lock right before Wait ExitLock(_externalLock, ref externalLockTaken); if (Monitor.IsEntered(_externalLock)) // Sanity check { throw new SynchronizationLockException("Recursive lock is not supported"); } // Waiting for signal if (!Monitor.Wait(_internalLock, remainingWaitMilliseconds)) { return(false); } // Check if cancellation or dispose was the reasons of the signal if (token.IsCancellationRequested) { throw new OperationCanceledException(token); } if (_isDisposed) { throw new OperationInterruptedException("Wait was interrupted by Dispose", new ObjectDisposedException(this.GetType().Name)); } } finally { if (internalLockTaken) { _waitCount--; TurboContract.Assert(_waitCount >= 0, conditionString: "_waitCount >= 0"); Monitor.Exit(_internalLock); } EnterLock(_externalLock, ref externalLockTaken); cancellationTokenRegistration.Dispose(); } return(true); }
/// <summary> /// Blocks the current thread until predicate estimates as True /// </summary> /// <typeparam name="TState">Type of the state object</typeparam> /// <param name="predicate">Predicate that should return True to complete waiting</param> /// <param name="state">State object for the predicate</param> /// <param name="timeout">Tiemout in milliseconds</param> /// <param name="token">Cancellation token</param> /// <returns>True if predicate evaluates to True</returns> /// <exception cref="ArgumentNullException">predicate is null</exception> /// <exception cref="SynchronizationLockException">Lock is not acquired</exception> /// <exception cref="ObjectDisposedException">Waiter was disposed</exception> /// <exception cref="OperationCanceledException">Cancellation happened</exception> /// <exception cref="OperationInterruptedException">Waiting was interrupted by Dispose</exception> public bool Wait <TState>(WaitPredicate <TState> predicate, TState state, int timeout, CancellationToken token) { if (predicate == null) { throw new ArgumentNullException(nameof(predicate)); } if (!Monitor.IsEntered(this)) { throw new SynchronizationLockException("External lock should be acquired"); } if (_isDisposed) { throw new ObjectDisposedException(this.GetType().Name); } if (token.IsCancellationRequested) { throw new OperationCanceledException(token); } uint startTime = 0; if (timeout > 0) { startTime = TimeoutHelper.GetTimestamp(); } else if (timeout < -1) { timeout = Timeout.Infinite; } if (predicate(state)) { return(true); } if (timeout == 0) { return(false); } if (timeout > 0 && TimeoutHelper.UpdateTimeout(startTime, timeout) <= 0) // Predicate estimation took too much time { return(false); } CancellationTokenRegistration cancellationTokenRegistration = default(CancellationTokenRegistration); try { if (token.CanBeCanceled) { cancellationTokenRegistration = CancellationTokenHelper.RegisterWithoutECIfPossible(token, _cancellationTokenCanceledEventHandler, this); } if (WaitUntilPredicate(predicate, state, startTime, timeout, token)) { return(true); } if (token.IsCancellationRequested) { throw new OperationCanceledException(token); } if (_isDisposed) { throw new OperationInterruptedException("Wait was interrupted by Dispose", new ObjectDisposedException(this.GetType().Name)); } } finally { cancellationTokenRegistration.Dispose(); } // Final check for predicate return(predicate(state)); }
/// <summary> /// Helper method to register notification for token cancellation /// </summary> /// <param name="token">Cancellation token</param> /// <returns>Registration info</returns> internal CancellationTokenRegistration RegisterNotificationOnCancellation(CancellationToken token) { return(CancellationTokenHelper.RegisterWithoutECIfPossible(token, _cancellationTokenCanceledEventHandler, this)); }
/// <summary> /// Blocks the current thread until it can enter the semaphore /// </summary> /// <param name="timeout">Tiemout in milliseconds</param> /// <param name="token">Cancellation token</param> /// <param name="throwOnCancellation">Whether the OperationCanceledException should be thrown if cancellation happened</param> /// <returns>True if the current thread successfully entered the semaphore</returns> internal bool Wait(int timeout, CancellationToken token, bool throwOnCancellation) { if (_isDisposed) { throw new ObjectDisposedException(this.GetType().Name); } if (token.IsCancellationRequested) { if (throwOnCancellation) { throw new OperationCanceledException(token); } return(false); } // Делаем захват if (TryTakeLockFree()) { return(true); } // Early exit: nothing to wait if (timeout == 0 && _waitCount >= _currentCountForWait) { return(false); } uint startTime = 0; if (timeout > 0) { startTime = TimeoutHelper.GetTimestamp(); } else if (timeout < -1) { timeout = Timeout.Infinite; } // Ждём появления (лучше активно подождать, чем входить в lock) if (_waitCount >= _currentCountForWait) { if (timeout == 0) // Редкая ситуация. При нулевом таймауте нам нечего ловить { return(false); } int spinNumber = _processorCount > 1 ? 0 : SPIN_YIELD_THRESHOLD; // Пропускаем активное ожидание, если только одно ядро доступно int currentCountLocFree = _currentCountLockFree; while (spinNumber < SPIN_YIELD_THRESHOLD + 8) { if (currentCountLocFree > 0 && Interlocked.CompareExchange(ref _currentCountLockFree, currentCountLocFree - 1, currentCountLocFree) == currentCountLocFree) { return(true); } SpinOnce(spinNumber); if (spinNumber < SPIN_YIELD_THRESHOLD && _waitCount > _currentCountForWait + 2) // Жгём CPU только если немного потоков в ожидании. Иначе лучше на Thread.Yield переходить { spinNumber = SPIN_YIELD_THRESHOLD; } else { spinNumber++; } currentCountLocFree = _currentCountLockFree; } // Пробуем захватить ещё раз if (currentCountLocFree > 0 && Interlocked.CompareExchange(ref _currentCountLockFree, currentCountLocFree - 1, currentCountLocFree) == currentCountLocFree) { return(true); } } // Вынуждены уходить в lock CancellationTokenRegistration cancellationTokenRegistration = default(CancellationTokenRegistration); bool lockTaken = false; try { if (token.CanBeCanceled) { cancellationTokenRegistration = CancellationTokenHelper.RegisterWithoutECIfPossible(token, _cancellationTokenCanceledEventHandler, this); } try { } finally { Monitor.Enter(_lockObj, ref lockTaken); TurboContract.Assert(lockTaken, conditionString: "lockTaken"); Interlocked.Increment(ref _waitCount); // Release должен увидеть наше появление } // Пробуем забрать из _currentCountForWait if (_currentCountForWait > 0) { _currentCountForWait--; return(true); } // Пока входили в lock могли добавится значения в _currentCountLocFree if (TryTakeLockFree()) { return(true); } if (timeout == 0) { return(false); } // Ожидаем появления элементов и забираем сразу if (WaitUntilCountOrTimeoutAndTake(timeout, startTime, token)) { return(true); } if (token.IsCancellationRequested) { if (throwOnCancellation) { throw new OperationCanceledException(token); } return(false); } if (_isDisposed) { throw new OperationInterruptedException("Semaphore wait was interrupted by Dispose", new ObjectDisposedException(this.GetType().Name)); } } finally { if (lockTaken) { _waitCount--; TurboContract.Assert(_waitCount >= 0, conditionString: "_waitCount >= 0"); Monitor.Exit(_lockObj); } cancellationTokenRegistration.Dispose(); } return(false); }
/// <summary> /// Blocks the current thread if it is required /// </summary> /// <param name="timeout">Waiting timeout in milliseconds</param> /// <param name="token">Cancellation token</param> /// <returns>True if the current thread successfully passed the <see cref="PartialThreadBlocker"/> (false - exited by timeout)</returns> public bool Wait(int timeout, CancellationToken token) { token.ThrowIfCancellationRequested(); if (timeout < 0) { timeout = Timeout.Infinite; } if (_realWaiterCount >= _expectedWaiterCount) { return(true); } if (timeout == 0) { return(false); } uint startTime = 0; int currentTime = Timeout.Infinite; if (timeout != Timeout.Infinite) { startTime = TimeoutHelper.GetTimestamp(); } for (int i = 0; i < 10; i++) { if (_realWaiterCount >= _expectedWaiterCount) { return(true); } if (i == 5) { Thread.Yield(); } else { Thread.SpinWait(150 + (4 << i)); } } using (CancellationTokenHelper.RegisterWithoutECIfPossible(token, _cancellationTokenCanceledEventHandler, this)) { lock (_lockObj) { if (_realWaiterCount < _expectedWaiterCount) { try { _realWaiterCount++; while (_realWaiterCount <= _expectedWaiterCount) { token.ThrowIfCancellationRequested(); if (timeout != Timeout.Infinite) { currentTime = TimeoutHelper.UpdateTimeout(startTime, timeout); if (currentTime <= 0) { return(false); } } if (!Monitor.Wait(_lockObj, currentTime)) { return(false); } } } finally { _realWaiterCount--; } } } } return(true); }
/// <summary> /// Blocks the current thread until it can enter the semaphore /// </summary> /// <param name="timeout">Tiemout in milliseconds</param> /// <param name="token">Cancellation token</param> /// <param name="throwOnCancellation">Whether the OperationCanceledException should be thrown if cancellation happened</param> /// <returns>True if the current thread successfully entered the semaphore</returns> internal bool Wait(int timeout, CancellationToken token, bool throwOnCancellation) { if (_isDisposed) { throw new ObjectDisposedException(this.GetType().Name); } if (token.IsCancellationRequested) { if (throwOnCancellation) { throw new OperationCanceledException(token); } return(false); } // Делаем захват if (TryTakeLockFree()) { return(true); } // Early exit: nothing to wait if (timeout == 0 && _waitCount >= _currentCountForWait) { return(false); } uint startTime = 0; if (timeout > 0) { startTime = TimeoutHelper.GetTimestamp(); } else if (timeout < -1) { timeout = Timeout.Infinite; } // Ждём появления (лучше активно подождать, чем входить в lock) if (_processorCount > 1) { int currentCountLocFree = _currentCountLockFree; if (_waitCount >= _currentCountForWait && _waitCount <= _currentCountForWait + 2) { for (int i = 0; i < 8; i++) { if (currentCountLocFree > 0 && Interlocked.CompareExchange(ref _currentCountLockFree, currentCountLocFree - 1, currentCountLocFree) == currentCountLocFree) { return(true); } Thread.SpinWait(150 + 16 * i); currentCountLocFree = _currentCountLockFree; } } // Пробуем захватить ещё раз if (currentCountLocFree > 0 && Interlocked.CompareExchange(ref _currentCountLockFree, currentCountLocFree - 1, currentCountLocFree) == currentCountLocFree) { return(true); } } if (timeout == 0 && _waitCount >= _currentCountForWait) { return(false); } if (_waitCount >= _currentCountForWait) { Thread.Yield(); int currentCountLocFree = _currentCountLockFree; if (currentCountLocFree > 0 && Interlocked.CompareExchange(ref _currentCountLockFree, currentCountLocFree - 1, currentCountLocFree) == currentCountLocFree) { return(true); } } // Вынуждены уходить в lock CancellationTokenRegistration cancellationTokenRegistration = default(CancellationTokenRegistration); bool lockTaken = false; try { if (token.CanBeCanceled) { cancellationTokenRegistration = CancellationTokenHelper.RegisterWithoutECIfPossible(token, _cancellationTokenCanceledEventHandler, this); } try { } finally { Monitor.Enter(_lockObj, ref lockTaken); TurboContract.Assert(lockTaken, conditionString: "lockTaken"); Interlocked.Increment(ref _waitCount); // Release должен увидеть наше появление } // Пробуем забрать из _currentCountForWait if (_currentCountForWait > 0) { _currentCountForWait--; return(true); } // Пока входили в lock могли добавится значения в _currentCountLocFree if (TryTakeLockFree()) { return(true); } if (timeout == 0) { return(false); } // Ожидаем появления элементов и забираем сразу if (WaitUntilCountOrTimeoutAndTake(timeout, startTime, token)) { return(true); } if (token.IsCancellationRequested) { if (throwOnCancellation) { throw new OperationCanceledException(token); } return(false); } if (_isDisposed) { throw new OperationInterruptedException("Semaphore wait was interrupted by Dispose", new ObjectDisposedException(this.GetType().Name)); } } finally { if (lockTaken) { _waitCount--; TurboContract.Assert(_waitCount >= 0, conditionString: "_waitCount >= 0"); Monitor.Exit(_lockObj); } cancellationTokenRegistration.Dispose(); } return(false); }