/// <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 HOW_MANY_SPIN_BEFORE_YIELD = 10; int HOW_MANY_YIELD_EVERY_SLEEP_0 = 5; int HOW_MANY_YIELD_EVERY_SLEEP_1 = 20; int spinCount = SpinCount; for (int i = 0; i < spinCount; i++) { if (IsSet) { return(true); } else if (i < HOW_MANY_SPIN_BEFORE_YIELD) { if (i == HOW_MANY_SPIN_BEFORE_YIELD / 2) { Thread.Yield(); } else { Thread.SpinWait(PlatformHelper.ProcessorCount * (4 << i)); } } else if (i % HOW_MANY_YIELD_EVERY_SLEEP_1 == 0) { Thread.Sleep(1); } else if (i % HOW_MANY_YIELD_EVERY_SLEEP_0 == 0) { Thread.Sleep(0); } else { Thread.Yield(); } if (i >= 100 && i % 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 deregister the token outside of the lock, to avoid deadlocks. using (cancellationToken.InternalRegisterWithoutEC(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 deregisters) the callback return(true); //done. The wait was satisfied. }
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", (object)millisecondsTimeout, Environment.GetResourceString("SpinLock_TryEnter_ArgumentOutOfRange")); } uint startTime = 0; 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); } else { int num1 = int.MaxValue; int comparand1 = this.m_owner; if ((comparand1 & 1) == 0) { Thread.BeginCriticalRegion(); if (Interlocked.CompareExchange(ref this.m_owner, comparand1 | 1, comparand1, ref lockTaken) == comparand1) { return; } Thread.EndCriticalRegion(); } else if ((comparand1 & 2147483646) != SpinLock.MAXIMUM_WAITERS) { num1 = (Interlocked.Add(ref this.m_owner, 2) & 2147483646) >> 1; } if (millisecondsTimeout == 0 || millisecondsTimeout != -1 && TimeoutHelper.UpdateTimeOut(startTime, millisecondsTimeout) <= 0) { this.DecrementWaiters(); } else { int processorCount = PlatformHelper.ProcessorCount; if (num1 < processorCount) { int num2 = 1; for (int index = 1; index <= num1 * 100; ++index) { Thread.SpinWait((num1 + index) * 100 * num2); if (num2 < processorCount) { ++num2; } int comparand2 = this.m_owner; if ((comparand2 & 1) == 0) { Thread.BeginCriticalRegion(); if (Interlocked.CompareExchange(ref this.m_owner, (comparand2 & 2147483646) == 0 ? comparand2 | 1 : comparand2 - 2 | 1, comparand2, ref lockTaken) == comparand2) { return; } Thread.EndCriticalRegion(); } } } if (millisecondsTimeout != -1 && TimeoutHelper.UpdateTimeOut(startTime, millisecondsTimeout) <= 0) { this.DecrementWaiters(); } else { int num2 = 0; while (true) { int comparand2 = this.m_owner; if ((comparand2 & 1) == 0) { Thread.BeginCriticalRegion(); if (Interlocked.CompareExchange(ref this.m_owner, (comparand2 & 2147483646) == 0 ? comparand2 | 1 : comparand2 - 2 | 1, comparand2, ref lockTaken) != comparand2) { Thread.EndCriticalRegion(); } else { break; } } if (num2 % 40 == 0) { Thread.Sleep(1); } else if (num2 % 10 == 0) { Thread.Sleep(0); } else { Thread.Yield(); } if (num2 % 10 != 0 || millisecondsTimeout == -1 || TimeoutHelper.UpdateTimeOut(startTime, millisecondsTimeout) > 0) { ++num2; } else { goto label_40; } } return; label_40: this.DecrementWaiters(); } } } }
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); } long startTimeTicks = 0L; bool flag = false; int num2 = millisecondsTimeout; if (millisecondsTimeout != -1) { startTimeTicks = DateTime.UtcNow.Ticks; flag = true; } int num3 = 10; int num4 = 5; int num5 = 20; for (int i = 0; i < this.SpinCount; i++) { if (this.IsSet) { return(true); } if (i < num3) { if (i == (num3 / 2)) { Thread.Yield(); } else { Thread.SpinWait(Environment.ProcessorCount * (((int)4) << i)); } } else if ((i % num5) == 0) { Thread.Sleep(1); } else if ((i % num4) == 0) { Thread.Sleep(0); } else { Thread.Yield(); } if ((i >= 100) && ((i % 10) == 0)) { cancellationToken.ThrowIfCancellationRequested(); } } this.EnsureLockObjectCreated(); using (cancellationToken.Register(s_cancellationTokenCallback, this)) { lock (this.m_lock) { while (!this.IsSet) { cancellationToken.ThrowIfCancellationRequested(); if (flag) { num2 = UpdateTimeOut(startTimeTicks, millisecondsTimeout); if (num2 <= 0) { return(false); } } this.Waiters++; if (this.IsSet) { this.Waiters--; return(true); } try { if (!Monitor.Wait(this.m_lock, num2)) { return(false); } continue; } finally { this.Waiters--; } } } } } return(true); }
private static void WorkerThreadStart() { Thread.CurrentThread.SetThreadPoolWorkerThreadName(); PortableThreadPool threadPoolInstance = ThreadPoolInstance; if (NativeRuntimeEventSource.Log.IsEnabled()) { NativeRuntimeEventSource.Log.ThreadPoolWorkerThreadStart( (uint)threadPoolInstance._separated.counts.VolatileRead().NumExistingThreads); } LowLevelLock hillClimbingThreadAdjustmentLock = threadPoolInstance._hillClimbingThreadAdjustmentLock; LowLevelLifoSemaphore semaphore = s_semaphore; while (true) { bool spinWait = true; while (semaphore.Wait(ThreadPoolThreadTimeoutMs, spinWait)) { bool alreadyRemovedWorkingWorker = false; while (TakeActiveRequest(threadPoolInstance)) { Volatile.Write(ref threadPoolInstance._separated.lastDequeueTime, Environment.TickCount); if (!ThreadPoolWorkQueue.Dispatch()) { // ShouldStopProcessingWorkNow() caused the thread to stop processing work, and it would have // already removed this working worker in the counts. This typically happens when hill climbing // decreases the worker thread count goal. alreadyRemovedWorkingWorker = true; break; } if (threadPoolInstance._separated.numRequestedWorkers <= 0) { break; } // In highly bursty cases with short bursts of work, especially in the portable thread pool // implementation, worker threads are being released and entering Dispatch very quickly, not finding // much work in Dispatch, and soon afterwards going back to Dispatch, causing extra thrashing on // data and some interlocked operations, and similarly when the thread pool runs out of work. Since // there is a pending request for work, introduce a slight delay before serving the next request. // The spin-wait is mainly for when the sleep is not effective due to there being no other threads // to schedule. Thread.UninterruptibleSleep0(); if (!Environment.IsSingleProcessor) { Thread.SpinWait(1); } } // Don't spin-wait on the semaphore next time if the thread was actively stopped from processing work, // as it's unlikely that the worker thread count goal would be increased again so soon afterwards that // the semaphore would be released within the spin-wait window spinWait = !alreadyRemovedWorkingWorker; if (!alreadyRemovedWorkingWorker) { // If we woke up but couldn't find a request, or ran out of work items to process, we need to update // the number of working workers to reflect that we are done working for now RemoveWorkingWorker(threadPoolInstance); } } hillClimbingThreadAdjustmentLock.Acquire(); try { // At this point, the thread's wait timed out. We are shutting down this thread. // We are going to decrement the number of exisiting threads to no longer include this one // and then change the max number of threads in the thread pool to reflect that we don't need as many // as we had. Finally, we are going to tell hill climbing that we changed the max number of threads. ThreadCounts counts = threadPoolInstance._separated.counts.VolatileRead(); while (true) { // Since this thread is currently registered as an existing thread, if more work comes in meanwhile, // this thread would be expected to satisfy the new work. Ensure that NumExistingThreads is not // decreased below NumProcessingWork, as that would be indicative of such a case. short numExistingThreads = counts.NumExistingThreads; if (numExistingThreads <= counts.NumProcessingWork) { // In this case, enough work came in that this thread should not time out and should go back to work. break; } ThreadCounts newCounts = counts; newCounts.SubtractNumExistingThreads(1); short newNumExistingThreads = (short)(numExistingThreads - 1); short newNumThreadsGoal = Math.Max(threadPoolInstance._minThreads, Math.Min(newNumExistingThreads, newCounts.NumThreadsGoal)); newCounts.NumThreadsGoal = newNumThreadsGoal; ThreadCounts oldCounts = threadPoolInstance._separated.counts.InterlockedCompareExchange(newCounts, counts); if (oldCounts == counts) { HillClimbing.ThreadPoolHillClimber.ForceChange(newNumThreadsGoal, HillClimbing.StateOrTransition.ThreadTimedOut); if (NativeRuntimeEventSource.Log.IsEnabled()) { NativeRuntimeEventSource.Log.ThreadPoolWorkerThreadStop((uint)newNumExistingThreads); } return; } counts = oldCounts; } } finally { hillClimbingThreadAdjustmentLock.Release(); } } }
private void ContinueTryEnter(int millisecondsTimeout, ref bool lockTaken) { int owner; long startTicks = 0L; if ((millisecondsTimeout != -1) && (millisecondsTimeout != 0)) { startTicks = DateTime.UtcNow.Ticks; } if (CdsSyncEtwBCLProvider.Log.IsEnabled()) { CdsSyncEtwBCLProvider.Log.SpinLock_FastPathFailed(this.m_owner); } if (this.IsThreadOwnerTrackingEnabled) { this.ContinueTryEnterWithThreadTracking(millisecondsTimeout, startTicks, ref lockTaken); return; } SpinWait wait = new SpinWait(); while (true) { 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 & 0x7ffffffe) == MAXIMUM_WAITERS) || (Interlocked.CompareExchange(ref this.m_owner, owner + 2, owner) == owner)) { break; } wait.SpinOnce(); } if ((millisecondsTimeout == 0) || ((millisecondsTimeout != -1) && TimeoutExpired(startTicks, millisecondsTimeout))) { this.DecrementWaiters(); return; } int num3 = ((owner + 2) & 0x7ffffffe) / 2; int processorCount = PlatformHelper.ProcessorCount; if (num3 < processorCount) { int num5 = 1; for (int i = 1; i <= (num3 * 100); i++) { Thread.SpinWait(((num3 + i) * 100) * num5); if (num5 < processorCount) { num5++; } owner = this.m_owner; if ((owner & 1) == 0) { Thread.BeginCriticalRegion(); int num7 = ((owner & 0x7ffffffe) == 0) ? (owner | 1) : ((owner - 2) | 1); if (Interlocked.CompareExchange(ref this.m_owner, num7, owner, ref lockTaken) == owner) { return; } Thread.EndCriticalRegion(); } } } if ((millisecondsTimeout != -1) && TimeoutExpired(startTicks, millisecondsTimeout)) { this.DecrementWaiters(); return; } int num8 = 0; Label_015F: owner = this.m_owner; if ((owner & 1) == 0) { Thread.BeginCriticalRegion(); int num9 = ((owner & 0x7ffffffe) == 0) ? (owner | 1) : ((owner - 2) | 1); if (Interlocked.CompareExchange(ref this.m_owner, num9, owner, ref lockTaken) == owner) { return; } Thread.EndCriticalRegion(); } if ((num8 % 40) == 0) { Thread.Sleep(1); } else if ((num8 % 10) == 0) { Thread.Sleep(0); } else { Thread.Yield(); } if ((((num8 % 10) == 0) && (millisecondsTimeout != -1)) && TimeoutExpired(startTicks, millisecondsTimeout)) { this.DecrementWaiters(); } else { num8++; goto Label_015F; } }
public static void SpinWait(int iterations) { NativeThread.SpinWait(iterations); }
private bool TryAcquireContended(IntPtr currentThreadId, int millisecondsTimeout, bool trackContentions = false) { // // If we already own the lock, just increment the recursion count. // if (_owningThreadId == currentThreadId) { checked { _recursionCount++; } return(true); } // // We've already made one lock attempt at this point, so bail early if the timeout is zero. // if (millisecondsTimeout == 0) { return(false); } int spins = 1; if (s_maxSpinCount == SpinningNotInitialized) { s_maxSpinCount = (Environment.ProcessorCount > 1) ? MaxSpinningValue : SpinningDisabled; } while (true) { // // Try to grab the lock. We may take the lock here even if there are existing waiters. This creates the possibility // of starvation of waiters, but it also prevents lock convoys from destroying perf. // The starvation issue is largely mitigated by the priority boost the OS gives to a waiter when we set // the event, after we release the lock. Eventually waiters will be boosted high enough to preempt this thread. // int oldState = _state; if ((oldState & Locked) == 0 && Interlocked.CompareExchange(ref _state, oldState | Locked, oldState) == oldState) { goto GotTheLock; } // // Back off by a factor of 2 for each attempt, up to MaxSpinCount // if (spins <= s_maxSpinCount) { Thread.SpinWait(spins); spins *= 2; } else if (oldState != 0) { // // We reached our spin limit, and need to wait. Increment the waiter count. // Note that we do not do any overflow checking on this increment. In order to overflow, // we'd need to have about 1 billion waiting threads, which is inconceivable anytime in the // forseeable future. // int newState = (oldState + WaiterCountIncrement) & ~WaiterWoken; if (Interlocked.CompareExchange(ref _state, newState, oldState) == oldState) { break; } } } // // Now we wait. // if (trackContentions) { Monitor.IncrementLockContentionCount(); } TimeoutTracker timeoutTracker = TimeoutTracker.Start(millisecondsTimeout); AutoResetEvent ev = Event; while (true) { Debug.Assert(_state >= WaiterCountIncrement); bool waitSucceeded = ev.WaitOne(timeoutTracker.Remaining); while (true) { int oldState = _state; Debug.Assert(oldState >= WaiterCountIncrement); // Clear the "waiter woken" bit. int newState = oldState & ~WaiterWoken; if ((oldState & Locked) == 0) { // The lock is available, try to get it. newState |= Locked; newState -= WaiterCountIncrement; if (Interlocked.CompareExchange(ref _state, newState, oldState) == oldState) { goto GotTheLock; } } else if (!waitSucceeded) { // The lock is not available, and we timed out. We're not going to wait agin. newState -= WaiterCountIncrement; if (Interlocked.CompareExchange(ref _state, newState, oldState) == oldState) { return(false); } } else { // The lock is not available, and we didn't time out. We're going to wait again. if (Interlocked.CompareExchange(ref _state, newState, oldState) == oldState) { break; } } } } GotTheLock: Debug.Assert((_state | Locked) != 0); Debug.Assert(_owningThreadId == IntPtr.Zero); Debug.Assert(_recursionCount == 0); _owningThreadId = currentThreadId; return(true); }
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) { //Leave the critical region which is entered by the fast path #if !FEATURE_CORECLR Thread.EndCriticalRegion(); #endif // The fast path doesn't throw any exception, so we have to validate the parameters here if (lockTaken) { lockTaken = false; throw new System.ArgumentException(Environment.GetResourceString("SpinLock_TryReliableEnter_ArgumentException")); } if (millisecondsTimeout < -1) { throw new ArgumentOutOfRangeException( nameof(millisecondsTimeout), millisecondsTimeout, Environment.GetResourceString("SpinLock_TryEnter_ArgumentOutOfRange")); } uint startTime = 0; if (millisecondsTimeout != Timeout.Infinite && millisecondsTimeout != 0) { startTime = TimeoutHelper.GetTime(); } #if !FEATURE_CORECLR if (CdsSyncEtwBCLProvider.Log.IsEnabled()) { CdsSyncEtwBCLProvider.Log.SpinLock_FastPathFailed(m_owner); } #endif 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 !FEATURE_CORECLR Thread.BeginCriticalRegion(); #endif if (Interlocked.CompareExchange(ref m_owner, observedOwner | 1, observedOwner, ref lockTaken) == observedOwner) { // Aquired lock return; } #if !FEATURE_CORECLR Thread.EndCriticalRegion(); #endif 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 = (Interlocked.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++) { Thread.SpinWait((turn + i) * SPINNING_FACTOR * processFactor); if (processFactor < processorCount) { processFactor++; } observedOwner = m_owner; if ((observedOwner & LOCK_ANONYMOUS_OWNED) == LOCK_UNOWNED) { #if !FEATURE_CORECLR Thread.BeginCriticalRegion(); #endif 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 Contract.Assert((newOwner & WAITERS_MASK) >= 0); if (Interlocked.CompareExchange(ref m_owner, newOwner, observedOwner, ref lockTaken) == observedOwner) { return; } #if !FEATURE_CORECLR Thread.EndCriticalRegion(); #endif } } // 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) { #if !FEATURE_CORECLR Thread.BeginCriticalRegion(); #endif 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 Contract.Assert((newOwner & WAITERS_MASK) >= 0); if (Interlocked.CompareExchange(ref m_owner, newOwner, observedOwner, ref lockTaken) == observedOwner) { return; } #if !FEATURE_CORECLR Thread.EndCriticalRegion(); #endif } if (yieldsoFar % SLEEP_ONE_FREQUENCY == 0) { Thread.Sleep(1); } else if (yieldsoFar % SLEEP_ZERO_FREQUENCY == 0) { Thread.Sleep(0); } else { Thread.Yield(); } if (yieldsoFar % TIMEOUT_CHECK_FREQUENCY == 0) { //Check the timeout. if (millisecondsTimeout != Timeout.Infinite && TimeoutHelper.UpdateTimeOut(startTime, millisecondsTimeout) <= 0) { DecrementWaiters(); return; } } yieldsoFar++; } }
/// <summary> /// Performs a single spin. /// </summary> /// <remarks> /// This is typically called in a loop, and may change in behavior based on the number of times a /// <see cref="SpinOnce"/> has been called thus far on this instance. /// </remarks> public void SpinOnce() { if (NextSpinWillYield) { // // We must yield. // // We prefer to call Thread.Yield first, triggering a SwitchToThread. This // unfortunately doesn't consider all runnable threads on all OS SKUs. In // some cases, it may only consult the runnable threads whose ideal processor // is the one currently executing code. Thus we oc----ionally issue a call to // Sleep(0), which considers all runnable threads at equal priority. Even this // is insufficient since we may be spin waiting for lower priority threads to // execute; we therefore must call Sleep(1) once in a while too, which considers // all runnable threads, regardless of ideal processor and priority, but may // remove the thread from the scheduler's queue for 10+ms, if the system is // configured to use the (default) coarse-grained system timer. // int yieldsSoFar = (m_count >= YIELD_THRESHOLD ? m_count - YIELD_THRESHOLD : m_count); if ((yieldsSoFar % SLEEP_1_EVERY_HOW_MANY_TIMES) == (SLEEP_1_EVERY_HOW_MANY_TIMES - 1)) { Thread.Sleep(1); } else if ((yieldsSoFar % SLEEP_0_EVERY_HOW_MANY_TIMES) == (SLEEP_0_EVERY_HOW_MANY_TIMES - 1)) { Thread.Sleep(0); } else { // Should Yield // TODO: benchmark SpinWait on multi- and single-processor. #if WindowsCE Thread.Sleep(0); #else Thread.SpinWait(4 << YIELD_THRESHOLD); #endif } } else { // // Otherwise, we will spin. // // We do this using the CLR's SpinWait API, which is just a busy loop that // issues YIELD/PAUSE instructions to ensure multi-threaded CPUs can react // intelligently to avoid starving. (These are NOOPs on other CPUs.) We // choose a number for the loop iteration count such that each successive // call spins for longer, to reduce cache contention. We cap the total // number of spins we are willing to tolerate to reduce delay to the caller, // since we expect most callers will eventually block anyway. // #if WindowsCE Thread.Sleep(0); #else Thread.SpinWait(4 << m_count); #endif } // Finally, increment our spin counter. m_count = (m_count == int.MaxValue ? YIELD_THRESHOLD : m_count + 1); }
// 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(); }