public override bool ConnectAsync(TimeSpan timeout, TransportAsyncCallbackArgs callbackArgs) { AmqpTrace.Provider.AmqpLogOperationInformational(this, TraceOperation.Connect, this.transportSettings); TransportInitiator innerInitiator = this.transportSettings.CreateInitiator(); TransportAsyncCallbackArgs args = new TransportAsyncCallbackArgs(); args.CompletedCallback = this.OnConnectComplete; args.UserToken = callbackArgs; callbackArgs.CompletedSynchronously = false; this.timeoutHelper = new TimeoutHelper(timeout); if (innerInitiator.ConnectAsync(timeout, args)) { return true; } int currentThread = CurrentThreadId; Interlocked.Exchange(ref this.completingThread, currentThread); this.OnConnectComplete(args); return Interlocked.Exchange(ref this.completingThread, -1) != 0; }
AcquireContextAsyncResult(InstanceHandle handle, Transaction hostTransaction, TimeSpan timeout, out bool setOperationPending, bool synchronous, AsyncCallback callback, object state) : base(callback, state) { // Need to report back to the caller whether or not we set OperationPending. setOperationPending = false; this.handle = handle; HostTransaction = hostTransaction; this.timeoutHelper = new TimeoutHelper(timeout); AcquireContextAsyncResult transactionWait; bool reuseContext = false; lock (this.handle.ThisLock) { if (!this.handle.IsValid) { throw Fx.Exception.AsError(new OperationCanceledException(SRCore.HandleFreed)); } if (this.handle.OperationPending) { throw Fx.Exception.AsError(new InvalidOperationException(SRCore.CommandExecutionCannotOverlap)); } setOperationPending = true; this.handle.OperationPending = true; transactionWait = this.handle.CurrentTransactionalAsyncResult; if (transactionWait != null) { Fx.Assert(this.handle.AcquirePending == null, "Overlapped acquires pending."); // If the transaction matches but is already completed (or completing), the easiest ting to do // is wait for it to complete, then try to re-enlist, and have that failure be the failure mode for Execute. // We do that by following the regular, non-matching transaction path. if (transactionWait.HostTransaction.Equals(hostTransaction) && !this.handle.TooLateToEnlist) { reuseContext = true; this.executionContext = transactionWait.ReuseContext(); this.handle.CurrentExecutionContext = this.executionContext; } else { this.handle.AcquirePending = this; } } } if (transactionWait != null) { Fx.Assert(transactionWait.IsCompleted, "Old AsyncResult must be completed by now."); // Reuse the existing InstanceExecutionContext if this is the same transaction we're waiting for. if (reuseContext) { Complete(true); return; } TimeSpan waitTimeout = this.timeoutHelper.RemainingTime(); if (synchronous) { if (!transactionWait.WaitForHostTransaction.Wait(waitTimeout)) { throw Fx.Exception.AsError(new TimeoutException(InternalSR.TimeoutOnOperation(waitTimeout))); } } else { if (!transactionWait.WaitForHostTransaction.WaitAsync(AcquireContextAsyncResult.onHostTransaction, this, waitTimeout)) { return; } } } if (DoAfterTransaction()) { Complete(true); } }
ExecuteAsyncResult(InstancePersistenceCommand command, TimeSpan timeout, AsyncCallback callback, object state) : base(callback, state) { this.executionStack = new Stack<IEnumerator<InstancePersistenceCommand>>(2); this.timeoutHelper = new TimeoutHelper(timeout); this.currentExecution = (new List<InstancePersistenceCommand> { command }).GetEnumerator(); }
/// <summary> /// Try acquire the lock with long path, this is usually called after the first path in Enter and /// TryEnter failed The reason for short path is to make it inline in the run time which improves the /// performance. This method assumed that the parameter are validated in Enter or TryEnter method. /// </summary> /// <param name="millisecondsTimeout">The timeout milliseconds</param> /// <param name="lockTaken">The lockTaken param</param> private void ContinueTryEnter(int millisecondsTimeout, ref bool lockTaken) { // The fast path doesn't throw any exception, so we have to validate the parameters here if (lockTaken) { lockTaken = false; throw new ArgumentException(SR.SpinLock_TryReliableEnter_ArgumentException); } if (millisecondsTimeout < -1) { throw new ArgumentOutOfRangeException( nameof(millisecondsTimeout), millisecondsTimeout, SR.SpinLock_TryEnter_ArgumentOutOfRange); } uint startTime = 0; if (millisecondsTimeout != Timeout.Infinite && millisecondsTimeout != 0) { startTime = TimeoutHelper.GetTime(); } if (IsThreadOwnerTrackingEnabled) { // Slow path for enabled thread tracking mode ContinueTryEnterWithThreadTracking(millisecondsTimeout, startTime, ref lockTaken); return; } // then thread tracking is disabled // In this case there are three ways to acquire the lock // 1- the first way the thread either tries to get the lock if it's free or updates the waiters, if the turn >= the processors count then go to 3 else go to 2 // 2- In this step the waiter threads spins and tries to acquire the lock, the number of spin iterations and spin count is dependent on the thread turn // the late the thread arrives the more it spins and less frequent it check the lock availability // Also the spins count is increases each iteration // If the spins iterations finished and failed to acquire the lock, go to step 3 // 3- This is the yielding step, there are two ways of yielding Thread.Yield and Sleep(1) // If the timeout is expired in after step 1, we need to decrement the waiters count before returning int observedOwner; int turn = int.MaxValue; // ***Step 1, take the lock or update the waiters // try to acquire the lock directly if possible or update the waiters count observedOwner = _owner; if ((observedOwner & LOCK_ANONYMOUS_OWNED) == LOCK_UNOWNED) { if (CompareExchange(ref _owner, observedOwner | 1, observedOwner, ref lockTaken) == observedOwner) { // Acquired lock return; } if (millisecondsTimeout == 0) { // Did not acquire lock in CompareExchange and timeout is 0 so fail fast return; } } else if (millisecondsTimeout == 0) { // Did not acquire lock as owned and timeout is 0 so fail fast return; } else // failed to acquire the lock, then try to update the waiters. If the waiters count reached the maximum, just break the loop to avoid overflow { if ((observedOwner & WAITERS_MASK) != MAXIMUM_WAITERS) { // This can still overflow, but maybe there will never be that many waiters turn = (Interlocked.Add(ref _owner, 2) & WAITERS_MASK) >> 1; } } // lock acquired failed and waiters updated // *** Step 2, Spinning and Yielding var spinner = new SpinWait(); if (turn > PlatformHelper.ProcessorCount) { spinner.Count = SpinWait.YieldThreshold; } while (true) { spinner.SpinOnce(SLEEP_ONE_FREQUENCY); observedOwner = _owner; if ((observedOwner & LOCK_ANONYMOUS_OWNED) == LOCK_UNOWNED) { int newOwner = (observedOwner & WAITERS_MASK) == 0 ? // Gets the number of waiters, if zero observedOwner | 1 // don't decrement it. just set the lock bit, it is zero because a previous call of Exit(false) which corrupted the waiters : (observedOwner - 2) | 1; // otherwise decrement the waiters and set the lock bit Debug.Assert((newOwner & WAITERS_MASK) >= 0); if (CompareExchange(ref _owner, newOwner, observedOwner, ref lockTaken) == observedOwner) { return; } } if (spinner.Count % TIMEOUT_CHECK_FREQUENCY == 0) { // Check the timeout. if (millisecondsTimeout != Timeout.Infinite && TimeoutHelper.UpdateTimeOut(startTime, millisecondsTimeout) <= 0) { DecrementWaiters(); return; } } } }
/// <summary> /// Blocks the current thread until it can enter the <see cref="SemaphoreSlim"/>, /// using a 32-bit signed integer to measure the time interval, /// while observing a <see cref="T:System.Threading.CancellationToken"/>. /// </summary> /// <param name="millisecondsTimeout">The number of milliseconds to wait, or <see cref="Timeout.Infinite"/>(-1) to /// wait indefinitely.</param> /// <param name="cancellationToken">The <see cref="T:System.Threading.CancellationToken"/> to observe.</param> /// <returns>true if the current thread successfully entered the <see cref="SemaphoreSlim"/>; otherwise, false.</returns> /// <exception cref="ArgumentOutOfRangeException"><paramref name="millisecondsTimeout"/> is a negative number other than -1, /// which represents an infinite time-out.</exception> /// <exception cref="System.OperationCanceledException"><paramref name="cancellationToken"/> was canceled.</exception> public bool Wait(int millisecondsTimeout, CancellationToken cancellationToken) { CheckDispose(); // Validate input if (millisecondsTimeout < -1) { throw new ArgumentOutOfRangeException( nameof(millisecondsTimeout), millisecondsTimeout, SR.SemaphoreSlim_Wait_TimeoutWrong); } cancellationToken.ThrowIfCancellationRequested(); // Perf: Check the stack timeout parameter before checking the volatile count if (millisecondsTimeout == 0 && m_currentCount == 0) { // Pessimistic fail fast, check volatile count outside lock (only when timeout is zero!) return(false); } uint startTime = 0; if (millisecondsTimeout != Timeout.Infinite && millisecondsTimeout > 0) { startTime = TimeoutHelper.GetTime(); } bool waitSuccessful = false; Task <bool> asyncWaitTask = null; bool lockTaken = false; //Register for cancellation outside of the main lock. //NOTE: Register/deregister inside the lock can deadlock as different lock acquisition orders could // occur for (1)this.m_lockObj and (2)cts.internalLock CancellationTokenRegistration cancellationTokenRegistration = cancellationToken.InternalRegisterWithoutEC(s_cancellationTokenCanceledEventHandler, this); try { // Perf: first spin wait for the count to be positive, but only up to the first planned yield. // This additional amount of spinwaiting in addition // to Monitor.Enter()’s spinwaiting has shown measurable perf gains in test scenarios. // SpinWait spin = new SpinWait(); while (m_currentCount == 0 && !spin.NextSpinWillYield) { spin.SpinOnce(); } // entering the lock and incrementing waiters must not suffer a thread-abort, else we cannot // clean up m_waitCount correctly, which may lead to deadlock due to non-woken waiters. try { } finally { m_lock.Acquire(); lockTaken = true; if (lockTaken) { m_waitCount++; } } // If there are any async waiters, for fairness we'll get in line behind // then by translating our synchronous wait into an asynchronous one that we // then block on (once we've released the lock). if (m_asyncHead != null) { Debug.Assert(m_asyncTail != null, "tail should not be null if head isn't"); asyncWaitTask = WaitAsync(millisecondsTimeout, cancellationToken); } // There are no async waiters, so we can proceed with normal synchronous waiting. else { // If the count > 0 we are good to move on. // If not, then wait if we were given allowed some wait duration OperationCanceledException oce = null; if (m_currentCount == 0) { if (millisecondsTimeout == 0) { return(false); } // Prepare for the main wait... // wait until the count become greater than zero or the timeout is expired try { waitSuccessful = WaitUntilCountOrTimeout(millisecondsTimeout, startTime, cancellationToken); } catch (OperationCanceledException e) { oce = e; } } // Now try to acquire. We prioritize acquisition over cancellation/timeout so that we don't // lose any counts when there are asynchronous waiters in the mix. Asynchronous waiters // defer to synchronous waiters in priority, which means that if it's possible an asynchronous // waiter didn't get released because a synchronous waiter was present, we need to ensure // that synchronous waiter succeeds so that they have a chance to release. Debug.Assert(!waitSuccessful || m_currentCount > 0, "If the wait was successful, there should be count available."); if (m_currentCount > 0) { waitSuccessful = true; m_currentCount--; } else if (oce != null) { throw oce; } // Exposing wait handle which is lazily initialized if needed if (m_waitHandle != null && m_currentCount == 0) { m_waitHandle.Reset(); } } } finally { // Release the lock if (lockTaken) { m_waitCount--; m_lock.Release(); } // Unregister the cancellation callback. cancellationTokenRegistration.Dispose(); } // If we had to fall back to asynchronous waiting, block on it // here now that we've released the lock, and return its // result when available. Otherwise, this was a synchronous // wait, and whether we successfully acquired the semaphore is // stored in waitSuccessful. return((asyncWaitTask != null) ? asyncWaitTask.GetAwaiter().GetResult() : waitSuccessful); }
/// <summary> /// Blocks the current thread until the current <see cref="ManualResetEventSlim"/> is set, using a /// 32-bit signed integer to measure the time interval, while observing a <see /// cref="T:System.Threading.CancellationToken"/>. /// </summary> /// <param name="millisecondsTimeout">The number of milliseconds to wait, or <see /// cref="Timeout.Infinite"/>(-1) to wait indefinitely.</param> /// <param name="cancellationToken">The <see cref="T:System.Threading.CancellationToken"/> to /// observe.</param> /// <returns>true if the <see cref="System.Threading.ManualResetEventSlim"/> was set; otherwise, /// false.</returns> /// <exception cref="T:System.ArgumentOutOfRangeException"><paramref name="millisecondsTimeout"/> is a /// negative number other than -1, which represents an infinite time-out.</exception> /// <exception cref="T:System.InvalidOperationException"> /// The maximum number of waiters has been exceeded. /// </exception> /// <exception cref="T:System.Threading.OperationCanceledException"><paramref /// name="cancellationToken"/> was canceled.</exception> public bool Wait(int millisecondsTimeout, CancellationToken cancellationToken) { ThrowIfDisposed(); cancellationToken.ThrowIfCancellationRequested(); // an early convenience check if (millisecondsTimeout < -1) { throw new ArgumentOutOfRangeException(nameof(millisecondsTimeout)); } if (!IsSet) { if (millisecondsTimeout == 0) { // For 0-timeouts, we just return immediately. return(false); } // We spin briefly before falling back to allocating and/or waiting on a true event. uint startTime = 0; bool bNeedTimeoutAdjustment = false; int realMillisecondsTimeout = millisecondsTimeout; //this will be adjusted if necessary. if (millisecondsTimeout != Timeout.Infinite) { // We will account for time spent spinning, so that we can decrement it from our // timeout. In most cases the time spent in this section will be negligible. But // we can't discount the possibility of our thread being switched out for a lengthy // period of time. The timeout adjustments only take effect when and if we actually // decide to block in the kernel below. startTime = TimeoutHelper.GetTime(); bNeedTimeoutAdjustment = true; } // Spin int spinCount = SpinCount; var spinner = new SpinWait(); while (spinner.Count < spinCount) { spinner.SpinOnce(sleep1Threshold: -1); if (IsSet) { return(true); } if (spinner.Count >= 100 && spinner.Count % 10 == 0) // check the cancellation token if the user passed a very large spin count { cancellationToken.ThrowIfCancellationRequested(); } } // Now enter the lock and wait. EnsureLockObjectCreated(); // We must register and unregister the token outside of the lock, to avoid deadlocks. using (cancellationToken.UnsafeRegister(s_cancellationTokenCallback, this)) { lock (m_lock) { // Loop to cope with spurious wakeups from other waits being canceled while (!IsSet) { // If our token was canceled, we must throw and exit. cancellationToken.ThrowIfCancellationRequested(); //update timeout (delays in wait commencement are due to spinning and/or spurious wakeups from other waits being canceled) if (bNeedTimeoutAdjustment) { realMillisecondsTimeout = TimeoutHelper.UpdateTimeOut(startTime, millisecondsTimeout); if (realMillisecondsTimeout <= 0) { return(false); } } // There is a race condition that Set will fail to see that there are waiters as Set does not take the lock, // so after updating waiters, we must check IsSet again. // Also, we must ensure there cannot be any reordering of the assignment to Waiters and the // read from IsSet. This is guaranteed as Waiters{set;} involves an Interlocked.CompareExchange // operation which provides a full memory barrier. // If we see IsSet=false, then we are guaranteed that Set() will see that we are // waiting and will pulse the monitor correctly. Waiters = Waiters + 1; if (IsSet) //This check must occur after updating Waiters. { Waiters--; //revert the increment. return(true); } // Now finally perform the wait. try { // ** the actual wait ** if (!Monitor.Wait(m_lock, realMillisecondsTimeout)) { return(false); //return immediately if the timeout has expired. } } finally { // Clean up: we're done waiting. Waiters = Waiters - 1; } // Now just loop back around, and the right thing will happen. Either: // 1. We had a spurious wake-up due to some other wait being canceled via a different cancellationToken (rewait) // or 2. the wait was successful. (the loop will break) } } } } // automatically disposes (and unregisters) the callback return(true); //done. The wait was satisfied. }
public bool Wait(int millisecondsTimeout, CancellationToken cancellationToken) { this.ThrowIfDisposed(); cancellationToken.ThrowIfCancellationRequested(); if (millisecondsTimeout < -1) { throw new ArgumentOutOfRangeException("millisecondsTimeout"); } if (!this.IsSet) { if (millisecondsTimeout == 0) { return(false); } uint startTime = 0U; bool flag = false; int num = millisecondsTimeout; if (millisecondsTimeout != -1) { startTime = TimeoutHelper.GetTime(); flag = true; } int num2 = 10; int num3 = 5; int num4 = 20; int spinCount = this.SpinCount; for (int i = 0; i < spinCount; i++) { if (this.IsSet) { return(true); } if (i < num2) { if (i == num2 / 2) { Thread.Yield(); } else { Thread.SpinWait(4 << i); } } else if (i % num4 == 0) { Thread.Sleep(1); } else if (i % num3 == 0) { Thread.Sleep(0); } else { Thread.Yield(); } if (i >= 100 && i % 10 == 0) { cancellationToken.ThrowIfCancellationRequested(); } } this.EnsureLockObjectCreated(); using (cancellationToken.InternalRegisterWithoutEC(ManualResetEventSlim.s_cancellationTokenCallback, this)) { object @lock = this.m_lock; lock (@lock) { while (!this.IsSet) { cancellationToken.ThrowIfCancellationRequested(); if (flag) { num = TimeoutHelper.UpdateTimeOut(startTime, millisecondsTimeout); if (num <= 0) { return(false); } } this.Waiters++; if (this.IsSet) { int waiters = this.Waiters; this.Waiters = waiters - 1; return(true); } try { if (!Monitor.Wait(this.m_lock, num)) { return(false); } } finally { this.Waiters--; } } } } return(true); } return(true); }
/// <summary> /// Try acquire the lock with long path, this is usually called after the first path in Enter and /// TryEnter failed The reason for short path is to make it inline in the run time which improves the /// performance. This method assumed that the parameter are validated in Enter ir TryENter method /// </summary> /// <param name="millisecondsTimeout">The timeout milliseconds</param> /// <param name="lockTaken">The lockTaken param</param> private void ContinueTryEnter(int millisecondsTimeout, ref bool lockTaken) { // The fast path doesn't throw any exception, so we have to validate the parameters here if (lockTaken) { lockTaken = false; throw new System.ArgumentException(SR.SpinLock_TryReliableEnter_ArgumentException); } if (millisecondsTimeout < -1) { throw new ArgumentOutOfRangeException2( nameof(millisecondsTimeout), millisecondsTimeout, SR.SpinLock_TryEnter_ArgumentOutOfRange); } uint startTime = 0; if (millisecondsTimeout != Timeout.Infinite && millisecondsTimeout != 0) { startTime = TimeoutHelper.GetTime(); } //if (SpinLockTrace.Enabled) //{ // SpinLockTrace.SpinLock_FastPathFailed(m_owner); //} if (IsThreadOwnerTrackingEnabled) { // Slow path for enabled thread tracking mode ContinueTryEnterWithThreadTracking(millisecondsTimeout, startTime, ref lockTaken); return; } // then thread tracking is disabled // In this case there are three ways to acquire the lock // 1- the first way the thread either tries to get the lock if it's free or updates the waiters, if the turn >= the processors count then go to 3 else go to 2 // 2- In this step the waiter threads spins and tries to acquire the lock, the number of spin iterations and spin count is dependent on the thread turn // the late the thread arrives the more it spins and less frequent it check the lock avilability // Also the spins count is increases each iteration // If the spins iterations finished and failed to acquire the lock, go to step 3 // 3- This is the yielding step, there are two ways of yielding Thread.Yield and Sleep(1) // If the timeout is expired in after step 1, we need to decrement the waiters count before returning int observedOwner; int turn = int.MaxValue; //***Step 1, take the lock or update the waiters // try to acquire the lock directly if possible or update the waiters count observedOwner = m_owner; if ((observedOwner & LOCK_ANONYMOUS_OWNED) == LOCK_UNOWNED) { if (CompareExchange(ref m_owner, observedOwner | 1, observedOwner, ref lockTaken) == observedOwner) { // Aquired lock return; } if (millisecondsTimeout == 0) { // Did not aquire lock in CompareExchange and timeout is 0 so fail fast return; } } else if (millisecondsTimeout == 0) { // Did not aquire lock as owned and timeout is 0 so fail fast return; } else //failed to acquire the lock,then try to update the waiters. If the waiters count reached the maximum, jsut break the loop to avoid overflow { if ((observedOwner & WAITERS_MASK) != MAXIMUM_WAITERS) { turn = (Interlocked2.Add(ref m_owner, 2) & WAITERS_MASK) >> 1; } } //***Step 2. Spinning //lock acquired failed and waiters updated int processorCount = PlatformHelper.ProcessorCount; if (turn < processorCount) { int processFactor = 1; for (int i = 1; i <= turn * SPINNING_FACTOR; i++) { //RuntimeThread.SpinWait((turn + i) * SPINNING_FACTOR * processFactor); Thread.Sleep(0); if (processFactor < processorCount) { processFactor++; } observedOwner = m_owner; if ((observedOwner & LOCK_ANONYMOUS_OWNED) == LOCK_UNOWNED) { int newOwner = (observedOwner & WAITERS_MASK) == 0 ? // Gets the number of waiters, if zero observedOwner | 1 // don't decrement it. just set the lock bit, it is zzero because a previous call of Exit(false) ehich corrupted the waiters : (observedOwner - 2) | 1; // otherwise decrement the waiters and set the lock bit Debug.Assert((newOwner & WAITERS_MASK) >= 0); if (CompareExchange(ref m_owner, newOwner, observedOwner, ref lockTaken) == observedOwner) { return; } } } // Check the timeout. if (millisecondsTimeout != Timeout.Infinite && TimeoutHelper.UpdateTimeOut(startTime, millisecondsTimeout) <= 0) { DecrementWaiters(); return; } } //*** Step 3, Yielding //Sleep(1) every 50 yields int yieldsoFar = 0; while (true) { observedOwner = m_owner; if ((observedOwner & LOCK_ANONYMOUS_OWNED) == LOCK_UNOWNED) { int newOwner = (observedOwner & WAITERS_MASK) == 0 ? // Gets the number of waiters, if zero observedOwner | 1 // don't decrement it. just set the lock bit, it is zzero because a previous call of Exit(false) ehich corrupted the waiters : (observedOwner - 2) | 1; // otherwise decrement the waiters and set the lock bit Debug.Assert((newOwner & WAITERS_MASK) >= 0); if (CompareExchange(ref m_owner, newOwner, observedOwner, ref lockTaken) == observedOwner) { return; } } if (yieldsoFar % SLEEP_ONE_FREQUENCY == 0) { //RuntimeThread.Sleep(1); Thread.Sleep(1); } else if (yieldsoFar % SLEEP_ZERO_FREQUENCY == 0) { //RuntimeThread.Sleep(0); Thread.Sleep(0); } else { //RuntimeThread.Yield(); Thread.Sleep(0); } if (yieldsoFar % TIMEOUT_CHECK_FREQUENCY == 0) { //Check the timeout. if (millisecondsTimeout != Timeout.Infinite && TimeoutHelper.UpdateTimeOut(startTime, millisecondsTimeout) <= 0) { DecrementWaiters(); return; } } yieldsoFar++; } }
public bool Wait(int millisecondsTimeout, CancellationToken cancellationToken) { this.CheckDispose(); if (millisecondsTimeout < -1) { throw new ArgumentOutOfRangeException("totalMilliSeconds", (object)millisecondsTimeout, SemaphoreSlim.GetResourceString("SemaphoreSlim_Wait_TimeoutWrong")); } cancellationToken.ThrowIfCancellationRequested(); uint startTime = 0; if (millisecondsTimeout != -1 && millisecondsTimeout > 0) { startTime = TimeoutHelper.GetTime(); } bool flag = false; Task <bool> task = (Task <bool>)null; bool lockTaken = false; CancellationTokenRegistration tokenRegistration = cancellationToken.InternalRegisterWithoutEC(SemaphoreSlim.s_cancellationTokenCanceledEventHandler, (object)this); try { SpinWait spinWait = new SpinWait(); while (this.m_currentCount == 0 && !spinWait.NextSpinWillYield) { spinWait.SpinOnce(); } try { } finally { Monitor.Enter(this.m_lockObj, ref lockTaken); if (lockTaken) { this.m_waitCount = this.m_waitCount + 1; } } if (this.m_asyncHead != null) { task = this.WaitAsync(millisecondsTimeout, cancellationToken); } else { OperationCanceledException canceledException = (OperationCanceledException)null; if (this.m_currentCount == 0) { if (millisecondsTimeout == 0) { return(false); } try { flag = this.WaitUntilCountOrTimeout(millisecondsTimeout, startTime, cancellationToken); } catch (OperationCanceledException ex) { canceledException = ex; } } if (this.m_currentCount > 0) { flag = true; this.m_currentCount = this.m_currentCount - 1; } else if (canceledException != null) { throw canceledException; } if (this.m_waitHandle != null) { if (this.m_currentCount == 0) { this.m_waitHandle.Reset(); } } } } finally { if (lockTaken) { this.m_waitCount = this.m_waitCount - 1; Monitor.Exit(this.m_lockObj); } tokenRegistration.Dispose(); } if (task == null) { return(flag); } return(task.GetAwaiter().GetResult()); }
public static bool SpinUntil(Func <bool> condition, int millisecondsTimeout) { if (millisecondsTimeout < -1) { throw new ArgumentOutOfRangeException("millisecondsTimeout", millisecondsTimeout, Environment.GetResourceString("SpinWait_SpinUntil_TimeoutWrong")); } if (condition == null) { throw new ArgumentNullException("condition", Environment.GetResourceString("SpinWait_SpinUntil_ArgumentNull")); } uint num = 0U; if (millisecondsTimeout != 0 && millisecondsTimeout != -1) { num = TimeoutHelper.GetTime(); } SpinWait spinWait = default(SpinWait); while (!condition()) { if (millisecondsTimeout == 0) { return(false); } spinWait.SpinOnce(); if (millisecondsTimeout != -1 && spinWait.NextSpinWillYield && (long)millisecondsTimeout <= (long)((ulong)(TimeoutHelper.GetTime() - num))) { return(false); } } return(true); }
public bool Wait(int millisecondsTimeout, CancellationToken cancellationToken) { CheckDispose(); // Validate input if (millisecondsTimeout < -1) { throw new ArgumentOutOfRangeException( nameof(millisecondsTimeout), millisecondsTimeout, SR.SemaphoreSlim_Wait_TimeoutWrong); } cancellationToken.ThrowIfCancellationRequested(); // Perf: Check the stack timeout parameter before checking the volatile count if (millisecondsTimeout == 0 && m_currentCount == 0) { // Pessimistic fail fast, check volatile count outside lock (only when timeout is zero!) return(false); } uint startTime = 0; if (millisecondsTimeout != Timeout.Infinite && millisecondsTimeout > 0) { startTime = TimeoutHelper.GetTime(); } bool waitSuccessful = false; Task <bool>?asyncWaitTask = null; bool lockTaken = false; // Register for cancellation outside of the main lock. // NOTE: Register/unregister inside the lock can deadlock as different lock acquisition orders could // occur for (1)this.m_lockObjAndDisposed and (2)cts.internalLock CancellationTokenRegistration cancellationTokenRegistration = cancellationToken.UnsafeRegister(s_cancellationTokenCanceledEventHandler, this); try { // Perf: first spin wait for the count to be positive. // This additional amount of spinwaiting in addition // to Monitor.Enter()'s spinwaiting has shown measurable perf gains in test scenarios. if (m_currentCount == 0) { // Monitor.Enter followed by Monitor.Wait is much more expensive than waiting on an event as it involves another // spin, contention, etc. The usual number of spin iterations that would otherwise be used here is increased to // lessen that extra expense of doing a proper wait. int spinCount = SpinWait.SpinCountforSpinBeforeWait * 4; SpinWait spinner = default; while (spinner.Count < spinCount) { spinner.SpinOnce(sleep1Threshold: -1); if (m_currentCount != 0) { break; } } } Monitor.Enter(m_lockObjAndDisposed, ref lockTaken); m_waitCount++; // If there are any async waiters, for fairness we'll get in line behind // then by translating our synchronous wait into an asynchronous one that we // then block on (once we've released the lock). if (m_asyncHead != null) { Debug.Assert(m_asyncTail != null, "tail should not be null if head isn't"); asyncWaitTask = WaitAsync(millisecondsTimeout, cancellationToken); } // There are no async waiters, so we can proceed with normal synchronous waiting. else { // If the count > 0 we are good to move on. // If not, then wait if we were given allowed some wait duration OperationCanceledException?oce = null; if (m_currentCount == 0) { if (millisecondsTimeout == 0) { return(false); } // Prepare for the main wait... // wait until the count become greater than zero or the timeout is expired try { waitSuccessful = WaitUntilCountOrTimeout(millisecondsTimeout, startTime, cancellationToken); } catch (OperationCanceledException e) { oce = e; } } // Now try to acquire. We prioritize acquisition over cancellation/timeout so that we don't // lose any counts when there are asynchronous waiters in the mix. Asynchronous waiters // defer to synchronous waiters in priority, which means that if it's possible an asynchronous // waiter didn't get released because a synchronous waiter was present, we need to ensure // that synchronous waiter succeeds so that they have a chance to release. Debug.Assert(!waitSuccessful || m_currentCount > 0, "If the wait was successful, there should be count available."); if (m_currentCount > 0) { waitSuccessful = true; m_currentCount--; } else if (oce != null) { throw oce; } // Exposing wait handle which is lazily initialized if needed if (m_waitHandle != null && m_currentCount == 0) { m_waitHandle.Reset(); } } } finally { // Release the lock if (lockTaken) { m_waitCount--; Monitor.Exit(m_lockObjAndDisposed); } // Unregister the cancellation callback. cancellationTokenRegistration.Dispose(); } // If we had to fall back to asynchronous waiting, block on it // here now that we've released the lock, and return its // result when available. Otherwise, this was a synchronous // wait, and whether we successfully acquired the semaphore is // stored in waitSuccessful. return((asyncWaitTask != null) ? asyncWaitTask.GetAwaiter().GetResult() : waitSuccessful); }
// Token: 0x06003D91 RID: 15761 RVA: 0x000E49DC File Offset: 0x000E2BDC private void ContinueTryEnter(int millisecondsTimeout, ref bool lockTaken) { Thread.EndCriticalRegion(); if (lockTaken) { lockTaken = false; throw new ArgumentException(Environment.GetResourceString("SpinLock_TryReliableEnter_ArgumentException")); } if (millisecondsTimeout < -1) { throw new ArgumentOutOfRangeException("millisecondsTimeout", millisecondsTimeout, Environment.GetResourceString("SpinLock_TryEnter_ArgumentOutOfRange")); } uint startTime = 0U; if (millisecondsTimeout != -1 && millisecondsTimeout != 0) { startTime = TimeoutHelper.GetTime(); } if (CdsSyncEtwBCLProvider.Log.IsEnabled()) { CdsSyncEtwBCLProvider.Log.SpinLock_FastPathFailed(this.m_owner); } if (this.IsThreadOwnerTrackingEnabled) { this.ContinueTryEnterWithThreadTracking(millisecondsTimeout, startTime, ref lockTaken); return; } int num = int.MaxValue; int owner = this.m_owner; if ((owner & 1) == 0) { Thread.BeginCriticalRegion(); if (Interlocked.CompareExchange(ref this.m_owner, owner | 1, owner, ref lockTaken) == owner) { return; } Thread.EndCriticalRegion(); } else if ((owner & 2147483646) != SpinLock.MAXIMUM_WAITERS) { num = (Interlocked.Add(ref this.m_owner, 2) & 2147483646) >> 1; } if (millisecondsTimeout == 0 || (millisecondsTimeout != -1 && TimeoutHelper.UpdateTimeOut(startTime, millisecondsTimeout) <= 0)) { this.DecrementWaiters(); return; } int processorCount = PlatformHelper.ProcessorCount; if (num < processorCount) { int num2 = 1; for (int i = 1; i <= num * 100; i++) { Thread.SpinWait((num + i) * 100 * num2); if (num2 < processorCount) { num2++; } owner = this.m_owner; if ((owner & 1) == 0) { Thread.BeginCriticalRegion(); int value = ((owner & 2147483646) == 0) ? (owner | 1) : (owner - 2 | 1); if (Interlocked.CompareExchange(ref this.m_owner, value, owner, ref lockTaken) == owner) { return; } Thread.EndCriticalRegion(); } } } if (millisecondsTimeout != -1 && TimeoutHelper.UpdateTimeOut(startTime, millisecondsTimeout) <= 0) { this.DecrementWaiters(); return; } int num3 = 0; for (;;) { owner = this.m_owner; if ((owner & 1) == 0) { Thread.BeginCriticalRegion(); int value2 = ((owner & 2147483646) == 0) ? (owner | 1) : (owner - 2 | 1); if (Interlocked.CompareExchange(ref this.m_owner, value2, owner, ref lockTaken) == owner) { break; } Thread.EndCriticalRegion(); } if (num3 % 40 == 0) { Thread.Sleep(1); } else if (num3 % 10 == 0) { Thread.Sleep(0); } else { Thread.Yield(); } if (num3 % 10 == 0 && millisecondsTimeout != -1 && TimeoutHelper.UpdateTimeOut(startTime, millisecondsTimeout) <= 0) { goto Block_26; } num3++; } return; Block_26: this.DecrementWaiters(); }
// Token: 0x06003D93 RID: 15763 RVA: 0x000E4C34 File Offset: 0x000E2E34 private void ContinueTryEnterWithThreadTracking(int millisecondsTimeout, uint startTime, ref bool lockTaken) { int num = 0; int managedThreadId = Thread.CurrentThread.ManagedThreadId; if (this.m_owner == managedThreadId) { throw new LockRecursionException(Environment.GetResourceString("SpinLock_TryEnter_LockRecursionException")); } SpinWait spinWait = default(SpinWait); for (;;) { spinWait.SpinOnce(); if (this.m_owner == num) { Thread.BeginCriticalRegion(); if (Interlocked.CompareExchange(ref this.m_owner, managedThreadId, num, ref lockTaken) == num) { break; } Thread.EndCriticalRegion(); } if (millisecondsTimeout == 0 || (millisecondsTimeout != -1 && spinWait.NextSpinWillYield && TimeoutHelper.UpdateTimeOut(startTime, millisecondsTimeout) <= 0)) { return; } } }