Beispiel #1
0
        /// <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;
                }
            }
        }
Beispiel #2
0
        /// <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);
        }
Beispiel #3
0
 internal void Reacquire(uint previousRecursionCount)
 {
     Acquire();
     Contract.Assert(_recursionCount == 0);
     _recursionCount = previousRecursionCount;
 }
Beispiel #4
0
        /// <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);
            }
        }
Beispiel #5
0
        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);
        }
Beispiel #6
0
        //
        // 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();
            }
        }
Beispiel #7
0
        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);
                    }
                }
            }
        }
Beispiel #8
0
        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);
                }
            }
        }
Beispiel #9
0
            void IThreadPoolWorkItem.ExecuteWorkItem()
            {
                bool setSuccessfully = TrySetResult(true);

                Contract.Assert(setSuccessfully, "Should have been able to complete task");
            }