/// <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.RegisterWithoutEC(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="ConditionVariableAlt"/> 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 ConditionVariableAltWaiter Enter(int timeout, CancellationToken token) { if (_isDisposed) { throw new ObjectDisposedException(nameof(ConditionVariableAlt), $"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.RegisterWithoutEC(token, _cancellationTokenCanceledEventHandler, this); Monitor.Enter(_externalLock); // Can be interrupted } catch { cancellationTokenRegistration.Dispose(); throw; } } else { Monitor.Enter(_externalLock); } Interlocked.Increment(ref _waiterCount); return(new ConditionVariableAltWaiter(this, timeout, startTime, token, cancellationTokenRegistration)); }
/// <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.RegisterWithoutEC(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); }
/// <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.RegisterWithoutEC(token, _cancellationTokenCanceledEventHandler, this); } try { } finally { Monitor.Enter(_internalLock, ref internalLockTaken); Debug.Assert(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--; Debug.Assert(_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.RegisterWithoutEC(token, _cancellationTokenCanceledEventHandler, this); } try { } finally { Monitor.Enter(_internalLock, ref internalLockTaken); Debug.Assert(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--; Debug.Assert(_waitCount >= 0); Monitor.Exit(_internalLock); } EnterLock(_externalLock, ref externalLockTaken); cancellationTokenRegistration.Dispose(); } 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.RegisterWithoutEC(token, _cancellationTokenCanceledEventHandler, this); } try { } finally { Monitor.Enter(_lockObj, ref lockTaken); Debug.Assert(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--; Debug.Assert(_waitCount >= 0); Monitor.Exit(_lockObj); } cancellationTokenRegistration.Dispose(); } return(false); }
/// <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.RegisterWithoutEC(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.RegisterWithoutEC(token, _cancellationTokenCanceledEventHandler, this)); }
/// <summary> /// Заблокироваться, если требуется /// </summary> /// <param name="timeout">Таймаут в миллисекундах</param> /// <param name="token">Токен отмены</param> /// <returns>Успешность (false - выход по таймауту)</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.RegisterWithoutEC(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); }