/// <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"); }
/// <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 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); } }
/// <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); }