/// <summary> /// Adds an element in the first available slot, beginning the search from the tail-to-head. /// If no slots are available, the array is grown. The method doesn't return until successful. /// </summary> /// <param name="element">The element to add.</param> /// <returns>Information about where the add happened, to enable O(1) deregistration.</returns> internal SparselyPopulatedArrayAddInfo <T> Add(T element) { while (true) { // Get the tail, and ensure it's up to date. SparselyPopulatedArrayFragment <T> tail = m_tail; while (tail.m_next != null) { m_tail = (tail = tail.m_next); } // Search for a free index, starting from the tail. SparselyPopulatedArrayFragment <T> curr = tail; while (curr != null) { const int RE_SEARCH_THRESHOLD = -10; // Every 10 skips, force a search. if (curr.m_freeCount < 1) { --curr.m_freeCount; } if (curr.m_freeCount > 0 || curr.m_freeCount < RE_SEARCH_THRESHOLD) { int c = curr.Length; // We'll compute a start offset based on how many free slots we think there // are. This optimizes for ordinary the LIFO deregistration pattern, and is // far from perfect due to the non-threadsafe ++ and -- of the free counter. int start = ((c - curr.m_freeCount) % c); if (start < 0) { start = 0; curr.m_freeCount--; // Too many free elements; fix up. } Contract.Assert(start >= 0 && start < c, "start is outside of bounds"); // Now walk the array until we find a free slot (or reach the end). for (int i = 0; i < c; i++) { // If the slot is null, try to CAS our element into it. int tryIndex = (start + i) % c; Contract.Assert(tryIndex >= 0 && tryIndex < curr.m_elements.Length, "tryIndex is outside of bounds"); if (curr.m_elements[tryIndex] == null && Interlocked.CompareExchange(ref curr.m_elements[tryIndex], element, null) == null) { // We adjust the free count by --. Note: if this drops to 0, we will skip // the fragment on the next search iteration. Searching threads will -- the // count and force a search every so often, just in case fragmentation occurs. int newFreeCount = curr.m_freeCount - 1; curr.m_freeCount = newFreeCount > 0 ? newFreeCount : 0; return(new SparselyPopulatedArrayAddInfo <T>(curr, tryIndex)); } } } curr = curr.m_prev; } // If we got here, we need to add a new chunk to the tail and try again. SparselyPopulatedArrayFragment <T> newTail = new SparselyPopulatedArrayFragment <T>( tail.m_elements.Length == 4096 ? 4096 : tail.m_elements.Length * 2, tail); if (Interlocked.CompareExchange(ref tail.m_next, newTail, null) == null) { m_tail = newTail; } } }
/// <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( "totalMilliSeconds", millisecondsTimeout, GetResourceString("SemaphoreSlim_Wait_TimeoutWrong")); } cancellationToken.ThrowIfCancellationRequested(); long startTimeTicks = 0; if (millisecondsTimeout != Timeout.Infinite && millisecondsTimeout > 0) { startTimeTicks = DateTime.UtcNow.Ticks; } 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.Register(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 Monitor2.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 { Monitor2.Enter(m_lockObj, ref lockTaken); if (lockTaken) { m_waitCount++; } } // If the count > 0 we are good to move on. // If not, then wait if we were given allowed some wait duration 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 if (!WaitUntilCountOrTimeout(millisecondsTimeout, startTimeTicks, cancellationToken)) { return(false); } } // At this point the count should be greater than zero Contract.Assert(m_currentCount > 0); m_currentCount--; // 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_lockObj); } // Unregister the cancellation callback. cancellationTokenRegistration.Dispose(); } return(true); }
internal void Reacquire(uint previousRecursionCount) { Acquire(); Contract.Assert(_recursionCount == 0); _recursionCount = previousRecursionCount; }
/// <summary> /// Invoke the Canceled event. /// </summary> /// <remarks> /// The handlers are invoked synchronously in LIFO order. /// </remarks> private void ExecuteCallbackHandlers(bool throwOnFirstException) { Contract.Assert(IsCancellationRequested, "ExecuteCallbackHandlers should only be called after setting IsCancellationRequested->true"); Contract.Assert(ThreadIDExecutingCallbacks != -1, "ThreadIDExecutingCallbacks should have been set."); // Design decision: call the delegates in LIFO order so that callbacks fire 'deepest first'. // This is intended to help with nesting scenarios so that child enlisters cancel before their parents. List <Exception> exceptionList = null; SparselyPopulatedArray <CancellationCallbackInfo>[] callbackLists = m_registeredCallbacksLists; // If there are no callbacks to run, we can safely exit. Any races to lazy initialize it // will see IsCancellationRequested and will then run the callback themselves. if (callbackLists == null) { Interlocked.Exchange(ref m_state, NOTIFYINGCOMPLETE); return; } try { for (int index = 0; index < callbackLists.Length; index++) { SparselyPopulatedArray <CancellationCallbackInfo> list = callbackLists[index]; if (list != null) { SparselyPopulatedArrayFragment <CancellationCallbackInfo> currArrayFragment = list.Tail; while (currArrayFragment != null) { for (int i = currArrayFragment.Length - 1; i >= 0; i--) { // 1a. publish the indended callback, to ensure ctr.Dipose can tell if a wait is necessary. // 1b. transition to the target syncContext and continue there.. // On the target SyncContext. // 2. actually remove the callback // 3. execute the callback // re:#2 we do the remove on the syncCtx so that we can be sure we have control of the syncCtx before // grabbing the callback. This prevents a deadlock if ctr.Dispose() might run on the syncCtx too. m_executingCallback = currArrayFragment[i]; if (m_executingCallback != null) { //Transition to the target sync context (if necessary), and continue our work there. CancellationCallbackCoreWorkArguments args = new CancellationCallbackCoreWorkArguments(currArrayFragment, i); // marshal exceptions: either aggregate or perform an immediate rethrow // We assume that syncCtx.Send() has forwarded on user exceptions when appropriate. try { if (m_executingCallback.TargetSyncContext != null) { #pragma warning disable 0618 // This API isn't available in Metro, but we never run in metro. m_executingCallback.TargetSyncContext.Send(CancellationCallbackCoreWork_OnSyncContext, args); #pragma warning restore 0618 // CancellationCallbackCoreWork_OnSyncContext may have altered ThreadIDExecutingCallbacks, so reset it. ThreadIDExecutingCallbacks = Thread.CurrentThread.ManagedThreadId; } else { CancellationCallbackCoreWork_OnSyncContext(args); } } catch (Exception ex) { if (throwOnFirstException) { throw; } // Otherwise, log it and proceed. if (exceptionList == null) { exceptionList = new List <Exception>(); } exceptionList.Add(ex); } } } currArrayFragment = currArrayFragment.Prev; } } } } finally { m_state = NOTIFYINGCOMPLETE; m_executingCallback = null; Thread.MemoryBarrier(); // for safety, prevent reorderings crossing this point and seeing inconsistent state. } if (exceptionList != null) { Contract.Assert(exceptionList.Count > 0, "Expected exception count > 0"); throw new AggregateException(exceptionList); } }
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 < 0) { s_maxSpinCount = (Environment.ProcessorCount > 1) ? 10000 : 0; } 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) { System.Runtime.RuntimeImports.RhSpinWait(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) { Contract.Assert(_state >= WaiterCountIncrement); bool waitSucceeded = ev.WaitOne(timeoutTracker.Remaining); while (true) { int oldState = _state; Contract.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: Contract.Assert((_state | Locked) != 0); Contract.Assert(_owningThreadId == 0); Contract.Assert(_recursionCount == 0); _owningThreadId = currentThreadId; return(true); }
// // Fire any timers that have expired, and update the native timer to schedule the rest of them. // private void FireNextTimers() { // // we fire the first timer on this thread; any other timers that might have fired are queued // to the ThreadPool. // TimerQueueTimer timerToFireOnThisThread = null; lock (this) { // prevent ThreadAbort while updating state try { } finally { // // since we got here, that means our previous timer has fired. // m_isAppDomainTimerScheduled = false; bool haveTimerToSchedule = false; uint nextAppDomainTimerDuration = uint.MaxValue; int nowTicks = TickCount; // // Sweep through all timers. The ones that have reached their due time // will fire. We will calculate the next native timer due time from the // other timers. // TimerQueueTimer timer = m_timers; while (timer != null) { Contract.Assert(timer.m_dueTime != Timeout.UnsignedInfinite); uint elapsed = (uint)(nowTicks - timer.m_startTicks); if (elapsed >= timer.m_dueTime) { // // Remember the next timer in case we delete this one // TimerQueueTimer nextTimer = timer.m_next; if (timer.m_period != Timeout.UnsignedInfinite) { timer.m_startTicks = nowTicks; timer.m_dueTime = timer.m_period; // // This is a repeating timer; schedule it to run again. // if (timer.m_dueTime < nextAppDomainTimerDuration) { haveTimerToSchedule = true; nextAppDomainTimerDuration = timer.m_dueTime; } } else { // // Not repeating; remove it from the queue // DeleteTimer(timer); } // // If this is the first timer, we'll fire it on this thread. Otherwise, queue it // to the ThreadPool. // if (timerToFireOnThisThread == null) { timerToFireOnThisThread = timer; } else { QueueTimerCompletion(timer); } timer = nextTimer; } else { // // This timer hasn't fired yet. Just update the next time the native timer fires. // uint remaining = timer.m_dueTime - elapsed; if (remaining < nextAppDomainTimerDuration) { haveTimerToSchedule = true; nextAppDomainTimerDuration = remaining; } timer = timer.m_next; } } if (haveTimerToSchedule) { EnsureAppDomainTimerFiresBy(nextAppDomainTimerDuration); } } } // // Fire the user timer outside of the lock! // if (timerToFireOnThisThread != null) { timerToFireOnThisThread.Fire(); } }
internal void Resume() { // // Update timers to adjust their due-time to accomodate Pause/Resume // lock (this) { // prevent ThreadAbort while updating state try { } finally { int pauseTicks = m_pauseTicks; m_pauseTicks = 0; // Set this to 0 so that now timers can be scheduled int resumedTicks = TickCount; int pauseDuration = resumedTicks - pauseTicks; bool haveTimerToSchedule = false; uint nextAppDomainTimerDuration = uint.MaxValue; TimerQueueTimer timer = m_timers; while (timer != null) { Contract.Assert(timer.m_dueTime != Timeout.UnsignedInfinite); Contract.Assert(resumedTicks >= timer.m_startTicks); uint elapsed; // How much of the timer dueTime has already elapsed // Timers started before the paused event has to be sufficiently delayed to accomodate // for the Pause time. However, timers started after the Paused event shouldnt be adjusted. // E.g. ones created by the app in its Activated event should fire when it was designated. // The Resumed event which is where this routine is executing is after this Activated and hence // shouldn't delay this timer if (timer.m_startTicks <= pauseTicks) { elapsed = (uint)(pauseTicks - timer.m_startTicks); } else { elapsed = (uint)(resumedTicks - timer.m_startTicks); } // Handling the corner cases where a Timer was already due by the time Resume is happening, // We shouldn't delay those timers. // Example is a timer started in App's Activated event with a very small duration timer.m_dueTime = (timer.m_dueTime > elapsed) ? timer.m_dueTime - elapsed : 0;; timer.m_startTicks = resumedTicks; // re-baseline if (timer.m_dueTime < nextAppDomainTimerDuration) { haveTimerToSchedule = true; nextAppDomainTimerDuration = timer.m_dueTime; } timer = timer.m_next; } if (haveTimerToSchedule) { EnsureAppDomainTimerFiresBy(nextAppDomainTimerDuration); } } } }
private bool EnsureAppDomainTimerFiresBy(uint requestedDuration) { // // The VM's timer implementation does not work well for very long-duration timers. // See kb 950807. // So we'll limit our native timer duration to a "small" value. // This may cause us to attempt to fire timers early, but that's ok - // we'll just see that none of our timers has actually reached its due time, // and schedule the native timer again. // const uint maxPossibleDuration = 0x0fffffff; uint actualDuration = Math.Min(requestedDuration, maxPossibleDuration); if (m_isAppDomainTimerScheduled) { uint elapsed = (uint)(TickCount - m_currentAppDomainTimerStartTicks); if (elapsed >= m_currentAppDomainTimerDuration) { return(true); //the timer's about to fire } uint remainingDuration = m_currentAppDomainTimerDuration - elapsed; if (actualDuration >= remainingDuration) { return(true); //the timer will fire earlier than this request } } // If Pause is underway then do not schedule the timers // A later update during resume will re-schedule if (m_pauseTicks != 0) { Contract.Assert(!m_isAppDomainTimerScheduled); Contract.Assert(m_appDomainTimer == null); return(true); } if (m_appDomainTimer == null || m_appDomainTimer.IsInvalid) { Contract.Assert(!m_isAppDomainTimerScheduled); m_appDomainTimer = CreateAppDomainTimer(actualDuration); if (!m_appDomainTimer.IsInvalid) { m_isAppDomainTimerScheduled = true; m_currentAppDomainTimerStartTicks = TickCount; m_currentAppDomainTimerDuration = actualDuration; return(true); } else { return(false); } } else { if (ChangeAppDomainTimer(m_appDomainTimer, actualDuration)) { m_isAppDomainTimerScheduled = true; m_currentAppDomainTimerStartTicks = TickCount; m_currentAppDomainTimerDuration = actualDuration; return(true); } else { return(false); } } }
void IThreadPoolWorkItem.ExecuteWorkItem() { bool setSuccessfully = TrySetResult(true); Contract.Assert(setSuccessfully, "Should have been able to complete task"); }