/// <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) { RuntimeThread.Yield(); } else { RuntimeThread.SpinWait(PlatformHelper.ProcessorCount * (4 << i)); } } else if (i % HOW_MANY_YIELD_EVERY_SLEEP_1 == 0) { RuntimeThread.Sleep(1); } else if (i % HOW_MANY_YIELD_EVERY_SLEEP_0 == 0) { RuntimeThread.Sleep(0); } else { RuntimeThread.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 unregister the token outside of the lock, to avoid deadlocks. using (cancellationToken.InternalRegisterWithoutEC(s_cancellationTokenCallback, this)) { using (LockHolder.Hold(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 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 (!m_condition.Wait(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 static bool Yield() => RuntimeThread.Yield();
private static unsafe int WaitForMultipleObjectsIgnoringSyncContext(IntPtr *pHandles, int numHandles, bool waitAll, int millisecondsTimeout, bool interruptible) { Debug.Assert(millisecondsTimeout >= -1); // // In the CLR, we use CoWaitForMultipleHandles to pump messages while waiting in an STA. In that case, we cannot use WAIT_ALL. // That's because the wait would only be satisfied if a message arrives while the handles are signalled. // if (waitAll) { if (numHandles == 1) { waitAll = false; } else if (RuntimeThread.GetCurrentApartmentType() == RuntimeThread.ApartmentType.STA) { throw new NotSupportedException(SR.NotSupported_WaitAllSTAThread); } } RuntimeThread currentThread = RuntimeThread.CurrentThread; currentThread.SetWaitSleepJoinState(); int result; if (RuntimeThread.ReentrantWaitsEnabled) { Debug.Assert(!waitAll); result = RuntimeImports.RhCompatibleReentrantWaitAny(false, millisecondsTimeout, numHandles, pHandles); } else { result = (int)Interop.mincore.WaitForMultipleObjectsEx((uint)numHandles, (IntPtr)pHandles, waitAll, (uint)millisecondsTimeout, false); } currentThread.ClearWaitSleepJoinState(); if (result == WaitHandle.WaitFailed) { int errorCode = Interop.mincore.GetLastError(); if (waitAll && errorCode == Interop.Errors.ERROR_INVALID_PARAMETER) { // Check for duplicate handles. This is a brute force O(n^2) search, which is intended since the typical // array length is short enough that this would actually be faster than using a hash set. Also, the worst // case is not so bad considering that the array length is limited by // <see cref="WaitHandle.MaxWaitHandles"/>. for (int i = 1; i < numHandles; ++i) { IntPtr handle = pHandles[i]; for (int j = 0; j < i; ++j) { if (pHandles[j] == handle) { throw new DuplicateWaitObjectException("waitHandles[" + i + ']'); } } } } ThrowWaitFailedException(errorCode); } return(result); }
public static void Sleep(int millisecondsTimeout) => RuntimeThread.Sleep(millisecondsTimeout);
public static void SpinWait(int iterations) => RuntimeThread.SpinWait(iterations);
internal static void Sleep(int milliseconds) { RuntimeThread.Sleep(milliseconds); }
public static int GetCurrentProcessorId() => RuntimeThread.GetCurrentProcessorId();
private static void WorkerThreadStart() { // TODO: Event: Worker Thread Start event RuntimeThread currentThread = RuntimeThread.CurrentThread; while (true) { // TODO: Event: Worker thread wait event while (s_semaphore.Wait(TimeoutMs)) { if (TakeActiveRequest()) { Volatile.Write(ref ThreadPoolInstance._separated.lastDequeueTime, Environment.TickCount); if (ThreadPoolWorkQueue.Dispatch()) { // If the queue runs out of work for us, we need to update the number of working workers to reflect that we are done working for now RemoveWorkingWorker(); } // Reset thread-local state that we control. if (currentThread.Priority != ThreadPriority.Normal) { currentThread.Priority = ThreadPriority.Normal; } CultureInfo.CurrentCulture = CultureInfo.InstalledUICulture; CultureInfo.CurrentUICulture = CultureInfo.InstalledUICulture; } else { // If we woke up but couldn't find a request, 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 = ThreadCounts.VolatileReadCounts(ref ThreadPoolInstance._separated.counts); while (true) { if (counts.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.numExistingThreads--; newCounts.numThreadsGoal = Math.Max(ThreadPoolInstance._minThreads, Math.Min(newCounts.numExistingThreads, newCounts.numThreadsGoal)); ThreadCounts oldCounts = ThreadCounts.CompareExchangeCounts(ref ThreadPoolInstance._separated.counts, newCounts, counts); if (oldCounts == counts) { HillClimbing.ThreadPoolHillClimber.ForceChange(newCounts.numThreadsGoal, HillClimbing.StateOrTransition.ThreadTimedOut); // TODO: Event: Worker Thread stop event return; } counts = oldCounts; } } finally { ThreadPoolInstance._hillClimbingThreadAdjustmentLock.Release(); } } }
private static void DispatchCallback(IntPtr instance, IntPtr context, IntPtr work) { RuntimeThread.InitializeThreadPoolThread(); Debug.Assert(s_work == work); ThreadPoolWorkQueue.Dispatch(); }
/// <summary> /// Obtains all of the corresponding safe wait handles and adds a ref to each. Since the <see cref="SafeWaitHandle"/> /// property is publically modifiable, this makes sure that we add and release refs one the same set of safe wait /// handles to keep them alive during a multi-wait operation. /// </summary> private static SafeWaitHandle[] ObtainSafeWaitHandles( RuntimeThread currentThread, WaitHandle[] waitHandles, int numWaitHandles, out SafeWaitHandle[] rentedSafeWaitHandles) { Debug.Assert(currentThread == RuntimeThread.CurrentThread); Debug.Assert(waitHandles != null); Debug.Assert(numWaitHandles > 0); Debug.Assert(numWaitHandles <= MaxWaitHandles); Debug.Assert(numWaitHandles <= waitHandles.Length); rentedSafeWaitHandles = currentThread.RentWaitedSafeWaitHandleArray(numWaitHandles); SafeWaitHandle[] safeWaitHandles = rentedSafeWaitHandles ?? new SafeWaitHandle[numWaitHandles]; bool success = false; try { for (int i = 0; i < numWaitHandles; ++i) { WaitHandle waitHandle = waitHandles[i]; if (waitHandle == null) { throw new ArgumentNullException("waitHandles[" + i + ']', SR.ArgumentNull_ArrayElement); } SafeWaitHandle safeWaitHandle = waitHandle._waitHandle; if (safeWaitHandle == null) { // Throw ObjectDisposedException for backward compatibility even though it is not be representative of the issue throw new ObjectDisposedException(null, SR.ObjectDisposed_Generic); } safeWaitHandle.DangerousAddRef(); safeWaitHandles[i] = safeWaitHandle; } success = true; } finally { if (!success) { for (int i = 0; i < numWaitHandles; ++i) { SafeWaitHandle safeWaitHandle = safeWaitHandles[i]; if (safeWaitHandle == null) { break; } safeWaitHandle.DangerousRelease(); safeWaitHandles[i] = null; } if (rentedSafeWaitHandles != null) { currentThread.ReturnWaitedSafeWaitHandleArray(rentedSafeWaitHandles); } } } return(safeWaitHandles); }
private bool TryAcquireContended(int currentThreadId, int millisecondsTimeout) { // // 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) { RuntimeThread.SpinWait(spins); spins *= 2; } else { // // 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. // 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 == 0); Debug.Assert(_recursionCount == 0); _owningThreadId = currentThreadId; return(true); }
private static void TimerCallback(IntPtr instance, IntPtr context, IntPtr timer) { RuntimeThread.InitializeThreadPoolThread(); Instance.FireNextTimers(); }
public static void OnThreadExiting(RuntimeThread thread) { thread.WaitInfo.OnThreadExiting(); }
internal static void Spin(int iterations) { RuntimeThread.SpinWait(iterations); }
public static int Wait( RuntimeThread currentThread, SafeWaitHandle[] safeWaitHandles, WaitHandle[] waitHandles, int numWaitHandles, bool waitForAll, int timeoutMilliseconds) { Debug.Assert(currentThread == RuntimeThread.CurrentThread); Debug.Assert(safeWaitHandles != null); Debug.Assert(numWaitHandles > 0); Debug.Assert(numWaitHandles <= safeWaitHandles.Length); Debug.Assert(numWaitHandles <= waitHandles.Length); Debug.Assert(numWaitHandles <= WaitHandle.MaxWaitHandles); Debug.Assert(timeoutMilliseconds >= -1); ThreadWaitInfo waitInfo = currentThread.WaitInfo; WaitableObject[] waitableObjects = waitInfo.GetWaitedObjectArray(numWaitHandles); bool success = false; try { for (int i = 0; i < numWaitHandles; ++i) { Debug.Assert(safeWaitHandles[i] != null); WaitableObject waitableObject = HandleManager.FromHandle(safeWaitHandles[i].DangerousGetHandle()); if (waitForAll) { /// Check if this is a duplicate, as wait-for-all does not support duplicates. Including the parent /// loop, this becomes a brute force O(n^2) search, which is intended since the typical array length is /// short enough that this would actually be faster than other alternatives. Also, the worst case is not /// so bad considering that the array length is limited by <see cref="WaitHandle.MaxWaitHandles"/>. for (int j = 0; j < i; ++j) { if (waitableObject == waitableObjects[j]) { throw new DuplicateWaitObjectException("waitHandles[" + i + ']'); } } } waitableObjects[i] = waitableObject; } success = true; } finally { if (!success) { for (int i = 0; i < numWaitHandles; ++i) { waitableObjects[i] = null; } } } if (numWaitHandles == 1) { WaitableObject waitableObject = waitableObjects[0]; waitableObjects[0] = null; return (waitableObject.Wait(waitInfo, timeoutMilliseconds, interruptible: true, prioritize: false) ? 0 : WaitHandle.WaitTimeout); } return (WaitableObject.Wait( waitableObjects, numWaitHandles, waitForAll, waitInfo, timeoutMilliseconds, interruptible: true, prioritize: false, waitHandlesForAbandon: waitHandles)); }
private Thread(RuntimeThread runtimeThread) { Debug.Assert(runtimeThread != null); _runtimeThread = runtimeThread; }
public IThread CreateThread(IRuntime runtime, string name, Action<Action> dispatchMethod) { lock (_lock) { if (String.IsNullOrWhiteSpace(name)) { throw new ArgumentNullException(name); } if (_threadsByName.ContainsKey(name)) { throw new ArgumentException("Thread already exists: " + name); } IThread thread; if (dispatchMethod == null) { thread = new RuntimeThread(name, runtime, _logger, _errorHandler); } else { thread = new ExternalThread(name, runtime, _logger, _errorHandler, dispatchMethod); } _threadsByName.Add(name, thread); return thread; } }
/// <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 ArgumentOutOfRangeException( 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 = (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++) { RuntimeThread.SpinWait((turn + i) * SPINNING_FACTOR * processFactor); 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); } else if (yieldsoFar % SLEEP_ZERO_FREQUENCY == 0) { RuntimeThread.Sleep(0); } else { RuntimeThread.Yield(); } if (yieldsoFar % TIMEOUT_CHECK_FREQUENCY == 0) { //Check the timeout. if (millisecondsTimeout != Timeout.Infinite && TimeoutHelper.UpdateTimeOut(startTime, millisecondsTimeout) <= 0) { DecrementWaiters(); return; } } yieldsoFar++; } }