Beispiel #1
0
        private static void CancellationTokenCanceledEventHandler(object obj)
        {
            SemaphoreSlim semaphore = obj as SemaphoreSlim;

            Debug.Assert(semaphore != null, "Expected a SemaphoreSlim");
            using (LockHolder.Hold(semaphore.m_lock))
            {
                semaphore.m_condition.SignalAll(); //wake up all waiters.
            }
        }
Beispiel #2
0
        private bool SetApartmentStateUnchecked(ApartmentState state, bool throwOnError)
        {
            ApartmentState retState;

            if (this != CurrentThread)
            {
                using (LockHolder.Hold(_lock))
                {
                    if (HasStarted())
                    {
                        throw new ThreadStateException();
                    }

                    // Compat: Disallow resetting the initial apartment state
                    if (_initialApartmentState == ApartmentState.Unknown)
                    {
                        _initialApartmentState = state;
                    }

                    retState = _initialApartmentState;
                }
            }
            else
            {
                if ((t_comState & ComState.Locked) == 0)
                {
                    if (state != ApartmentState.Unknown)
                    {
                        InitializeCom(state);
                    }
                    else
                    {
                        UninitializeCom();
                    }
                }

                // Clear the cache and check whether new state matches the desired state
                t_apartmentType = ApartmentType.Unknown;

                retState = GetApartmentState();
            }

            if (retState != state)
            {
                if (throwOnError)
                {
                    string msg = SR.Format(SR.Thread_ApartmentState_ChangeFailed, retState);
                    throw new InvalidOperationException(msg);
                }

                return(false);
            }

            return(true);
        }
        private static void CancellationTokenCallback(object obj)
        {
            ManualResetEventSlim mre = obj as ManualResetEventSlim;

            Debug.Assert(mre != null, "Expected a ManualResetEventSlim");
            Debug.Assert(mre.m_lock != null); //the lock should have been created before this callback is registered for use.
            using (LockHolder.Hold(mre.m_lock))
            {
                mre.m_condition.SignalAll(); // awaken all waiters
            }
        }
Beispiel #4
0
 // Return an ID to the pool
 internal void ReturnId(int id)
 {
     using (LockHolder.Hold(m_lock))
     {
         m_freeIds[id] = true;
         if (id < m_nextIdToTry)
         {
             m_nextIdToTry = id;
         }
     }
 }
Beispiel #5
0
 public void Close()
 {
     using (LockHolder.Hold(TimerQueue.Instance.Lock))
     {
         if (!m_canceled)
         {
             m_canceled = true;
             TimerQueue.Instance.DeleteTimer(this);
         }
     }
 }
Beispiel #6
0
        volatile int m_pauseTicks = 0; // Time when Pause was called

        internal void Pause()
        {
            using (LockHolder.Hold(Lock))
            {
                // Delete the native timer so that no timers are fired in the Pause zone
                if (m_appDomainTimer != null && !m_appDomainTimer.IsInvalid)
                {
                    m_appDomainTimer.Dispose();
                    m_appDomainTimer            = null;
                    m_isAppDomainTimerScheduled = false;
                    m_pauseTicks = TickCount;
                }
            }
        }
        /// <summary>
        /// Private helper to actually perform the Set.
        /// </summary>
        /// <param name="duringCancellation">Indicates whether we are calling Set() during cancellation.</param>
        /// <exception cref="T:System.OperationCanceledException">The object has been canceled.</exception>
        private void Set(bool duringCancellation)
        {
            // We need to ensure that IsSet=true does not get reordered past the read of m_eventObj
            // This would be a legal movement according to the .NET memory model.
            // The code is safe as IsSet involves an Interlocked.CompareExchange which provides a full memory barrier.
            IsSet = true;

            // If there are waiting threads, we need to pulse them.
            if (Waiters > 0)
            {
                Debug.Assert(m_lock != null && m_condition != null); //if waiters>0, then m_lock has already been created.
                using (LockHolder.Hold(m_lock))
                {
                    m_condition.SignalAll();
                }
            }

            ManualResetEvent eventObj = m_eventObj;

            //Design-decision: do not set the event if we are in cancellation -> better to deadlock than to wake up waiters incorrectly
            //It would be preferable to wake up the event and have it throw OCE. This requires MRE to implement cancellation logic

            if (eventObj != null && !duringCancellation)
            {
                // We must surround this call to Set in a lock.  The reason is fairly subtle.
                // Sometimes a thread will issue a Wait and wake up after we have set m_state,
                // but before we have gotten around to setting m_eventObj (just below). That's
                // because Wait first checks m_state and will only access the event if absolutely
                // necessary.  However, the coding pattern { event.Wait(); event.Dispose() } is
                // quite common, and we must support it.  If the waiter woke up and disposed of
                // the event object before the setter has finished, however, we would try to set a
                // now-disposed Win32 event.  Crash!  To deal with this race, we use a lock to
                // protect access to the event object when setting and disposing of it.  We also
                // double-check that the event has not become null in the meantime when in the lock.

                lock (eventObj)
                {
                    if (m_eventObj != null)
                    {
                        // If somebody is waiting, we must set the event.
                        m_eventObj.Set();
                    }
                }
            }

#if DEBUG
            m_lastSetTime = Environment.TickCount;
#endif
        }
Beispiel #8
0
 internal void Remove(T e)
 {
     T[] array = m_array;
     using (LockHolder.Hold(m_lock))
     {
         for (int i = 0; i < m_array.Length; i++)
         {
             if (m_array[i] == e)
             {
                 Volatile.Write(ref m_array[i], null);
                 break;
             }
         }
     }
 }
        public static void ResetEvent(WaitableObject waitableObject)
        {
            Debug.Assert(waitableObject != null);

            LockHolder lockHolder = new LockHolder(s_lock);

            try
            {
                waitableObject.UnsignalEvent(ref lockHolder);
            }
            finally
            {
                lockHolder.Dispose();
            }
        }
            public void UnsignalEvent(ref LockHolder lockHolder)
            {
                s_lock.VerifyIsLocked();

                if (!IsEvent)
                {
                    lockHolder.Dispose();
                    WaitHandle.ThrowInvalidHandleException();
                }

                if (IsSignaled)
                {
                    --_signalCount;
                }
            }
Beispiel #11
0
        public static void ReleaseMutex(WaitableObject waitableObject)
        {
            Debug.Assert(waitableObject != null);

            LockHolder lockHolder = new LockHolder(s_lock);

            try
            {
                waitableObject.SignalMutex(ref lockHolder);
            }
            finally
            {
                lockHolder.Dispose();
            }
        }
Beispiel #12
0
        private void StartInternal(object parameter)
        {
            using (LockHolder.Hold(_lock))
            {
                if (!GetThreadStateBit(ThreadState.Unstarted))
                {
                    throw new ThreadStateException(SR.ThreadState_AlreadyStarted);
                }

                bool     waitingForThreadStart = false;
                GCHandle threadHandle          = GCHandle.Alloc(this);
                _threadStartArg = parameter;

                try
                {
                    if (!CreateThread(threadHandle))
                    {
                        throw new OutOfMemoryException();
                    }

                    // Skip cleanup if any asynchronous exception happens while waiting for the thread start
                    waitingForThreadStart = true;

                    // Wait until the new thread either dies or reports itself as started
                    while (GetThreadStateBit(ThreadState.Unstarted) && !JoinInternal(0))
                    {
                        Yield();
                    }

                    waitingForThreadStart = false;
                }
                finally
                {
                    Debug.Assert(!waitingForThreadStart, "Leaked threadHandle");
                    if (!waitingForThreadStart)
                    {
                        threadHandle.Free();
                        _threadStartArg = null;
                    }
                }

                if (GetThreadStateBit(ThreadState.Unstarted))
                {
                    // Lack of memory is the only expected reason for thread creation failure
                    throw new ThreadStartException(new OutOfMemoryException());
                }
            }
        }
Beispiel #13
0
        public static int ReleaseSemaphore(WaitableObject waitableObject, int count)
        {
            Debug.Assert(waitableObject != null);
            Debug.Assert(count > 0);

            LockHolder lockHolder = new LockHolder(s_lock);

            try
            {
                return(waitableObject.SignalSemaphore(count, ref lockHolder));
            }
            finally
            {
                lockHolder.Dispose();
            }
        }
Beispiel #14
0
 public void Close()
 {
     using (LockHolder.Hold(TimerQueue.Instance.Lock))
     {
         // prevent ThreadAbort while updating state
         try { }
         finally
         {
             if (!m_canceled)
             {
                 m_canceled = true;
                 TimerQueue.Instance.DeleteTimer(this);
             }
         }
     }
 }
Beispiel #15
0
        /// <summary>
        /// Asynchronously waits to 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>
        /// A task that will complete with a result of true if the current thread successfully entered
        /// the <see cref="SemaphoreSlim"/>, otherwise with a result of false.
        /// </returns>
        /// <exception cref="T:System.ObjectDisposedException">The current instance has already been
        /// disposed.</exception>
        /// <exception cref="ArgumentOutOfRangeException"><paramref name="millisecondsTimeout"/> is a negative number other than -1,
        /// which represents an infinite time-out.
        /// </exception>
        public Task <bool> WaitAsync(int millisecondsTimeout, CancellationToken cancellationToken)
        {
            CheckDispose();

            // Validate input
            if (millisecondsTimeout < -1)
            {
                throw new ArgumentOutOfRangeException(
                          nameof(millisecondsTimeout), millisecondsTimeout, SR.SemaphoreSlim_Wait_TimeoutWrong);
            }

            // Bail early for cancellation
            if (cancellationToken.IsCancellationRequested)
            {
                return(Task.FromCancellation <bool>(cancellationToken));
            }

            using (LockHolder.Hold(m_lock))
            {
                // If there are counts available, allow this waiter to succeed.
                if (m_currentCount > 0)
                {
                    --m_currentCount;
                    if (m_waitHandle != null && m_currentCount == 0)
                    {
                        m_waitHandle.Reset();
                    }
                    return(s_trueTask);
                }
                else if (millisecondsTimeout == 0)
                {
                    // No counts, if timeout is zero fail fast
                    return(s_falseTask);
                }
                // If there aren't, create and return a task to the caller.
                // The task will be completed either when they've successfully acquired
                // the semaphore or when the timeout expired or cancellation was requested.
                else
                {
                    Debug.Assert(m_currentCount == 0, "m_currentCount should never be negative");
                    var asyncWaiter = CreateAndAddAsyncWaiter();
                    return((millisecondsTimeout == Timeout.Infinite && !cancellationToken.CanBeCanceled) ?
                           asyncWaiter :
                           WaitUntilCountOrTimeoutAsync(asyncWaiter, millisecondsTimeout, cancellationToken));
                }
            }
        }
Beispiel #16
0
        /// <summary>
        /// Sets the hash code in a thread-safe way.
        /// </summary>
        public static int SetHashCode(int syncIndex, int hashCode)
        {
            Debug.Assert((0 < syncIndex) && (syncIndex < s_unusedEntryIndex));

            // Acquire the lock to ensure we are updating the latest version of s_entries.  This
            // lock may be avoided if we store the hash code and Monitor synchronization data in
            // the same object accessed by a reference.
            using (LockHolder.Hold(s_usedEntriesLock))
            {
                int currentHash = s_entries[syncIndex].HashCode;
                if (currentHash != 0)
                {
                    return(currentHash);
                }
                s_entries[syncIndex].HashCode = hashCode;
                return(hashCode);
            }
        }
Beispiel #17
0
        /// <summary>
        /// Grows the sync table.  If memory is not available, it throws an OOM exception keeping
        /// the state valid.
        /// </summary>
        private static void Grow()
        {
            Debug.Assert(s_freeEntriesLock.IsAcquired);

            int oldSize = s_entries.Length;
            int newSize = CalculateNewSize(oldSize);

            Entry[] newEntries = new Entry[newSize];

            using (LockHolder.Hold(s_usedEntriesLock))
            {
                // Copy the shallow content of the table
                Array.Copy(s_entries, newEntries, oldSize);

                // Publish the new table.  Lock-free reader threads must not see the new value of
                // s_entries until all the content is copied to the new table.
                Volatile.Write(ref s_entries, newEntries);
            }
        }
            public int SignalSemaphore(int count, ref LockHolder lockHolder)
            {
                s_lock.VerifyIsLocked();
                Debug.Assert(count > 0);

                if (!IsSemaphore)
                {
                    lockHolder.Dispose();
                    WaitHandle.ThrowInvalidHandleException();
                }

                int oldSignalCount = _signalCount;

                Debug.Assert(oldSignalCount <= _maximumSignalCount);
                if (count > _maximumSignalCount - oldSignalCount)
                {
                    lockHolder.Dispose();
                    throw new SemaphoreFullException();
                }

                if (oldSignalCount != 0)
                {
                    _signalCount = oldSignalCount + count;
                    return(oldSignalCount);
                }

                for (ThreadWaitInfo.WaitedListNode?waiterNode = _waitersHead, nextWaiterNode;
                     waiterNode != null;
                     waiterNode = nextWaiterNode)
                {
                    // Signaling the waiter will unregister the waiter node, so keep the next node before trying
                    nextWaiterNode = waiterNode.NextThread;

                    if (waiterNode.WaitInfo.TrySignalToSatisfyWait(waiterNode, isAbandonedMutex: false) && --count == 0)
                    {
                        return(oldSignalCount);
                    }
                }

                _signalCount = count;
                return(oldSignalCount);
            }
            public void SignalEvent(ref LockHolder lockHolder)
            {
                s_lock.VerifyIsLocked();

                switch (_type)
                {
                case WaitableObjectType.ManualResetEvent:
                    SignalManualResetEvent();
                    break;

                case WaitableObjectType.AutoResetEvent:
                    SignalAutoResetEvent();
                    break;

                default:
                    lockHolder.Dispose();
                    WaitHandle.ThrowInvalidHandleException();
                    break;
                }
            }
Beispiel #20
0
        /// <summary>
        /// Assigns a sync table entry to the object in a thread-safe way.
        /// </summary>
        public static unsafe int AssignEntry(object obj, int *pHeader)
        {
            // Allocate the synchronization object outside the lock
            Lock lck = new Lock();
            DeadEntryCollector collector = new DeadEntryCollector();
            DependentHandle    handle    = new DependentHandle(obj, collector);

            try
            {
                using (LockHolder.Hold(s_lock))
                {
                    // After acquiring the lock check whether another thread already assigned the sync entry
                    if (ObjectHeader.GetSyncEntryIndex(*pHeader, out int hashOrIndex))
                    {
                        return(hashOrIndex);
                    }

                    int syncIndex;
                    if (s_freeEntryList != 0)
                    {
                        // Grab a free entry from the list
                        syncIndex = s_freeEntryList;

                        ref Entry freeEntry = ref s_entries[syncIndex];
                        s_freeEntryList = freeEntry.Next;
                        freeEntry.Next  = 0;
                    }
                    else
                    {
                        if (s_unusedEntryIndex >= s_entries.Length)
                        {
                            // No free entries, use the slow path.  This call may OOM.
                            Grow();
                        }

                        // Grab the next unused entry
                        Debug.Assert(s_unusedEntryIndex < s_entries.Length);
                        syncIndex = s_unusedEntryIndex++;
                    }

                    ref Entry entry = ref s_entries[syncIndex];
Beispiel #21
0
        /// <summary>Performs the asynchronous wait.</summary>
        /// <param name="millisecondsTimeout">The timeout.</param>
        /// <param name="cancellationToken">The cancellation token.</param>
        /// <returns>The task to return to the caller.</returns>
        private async Task <bool> WaitUntilCountOrTimeoutAsync(TaskNode asyncWaiter, int millisecondsTimeout, CancellationToken cancellationToken)
        {
            Debug.Assert(asyncWaiter != null, "Waiter should have been constructed");
            Debug.Assert(m_lock.IsAcquired, "Requires the lock be held");

            // Wait until either the task is completed, timeout occurs, or cancellation is requested.
            // We need to ensure that the Task.Delay task is appropriately cleaned up if the await
            // completes due to the asyncWaiter completing, so we use our own token that we can explicitly
            // cancel, and we chain the caller's supplied token into it.
            using (var cts = cancellationToken.CanBeCanceled ?
                             CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, default(CancellationToken)) :
                             new CancellationTokenSource())
            {
                var waitCompleted = Task.WhenAny(asyncWaiter, Task.Delay(millisecondsTimeout, cts.Token));
                if (asyncWaiter == await waitCompleted.ConfigureAwait(false))
                {
                    cts.Cancel(); // ensure that the Task.Delay task is cleaned up
                    return(true); // successfully acquired
                }
            }

            // If we get here, the wait has timed out or been canceled.

            // If the await completed synchronously, we still hold the lock.  If it didn't,
            // we no longer hold the lock.  As such, acquire it.
            using (LockHolder.Hold(m_lock))
            {
                // Remove the task from the list.  If we're successful in doing so,
                // we know that no one else has tried to complete this waiter yet,
                // so we can safely cancel or timeout.
                if (RemoveAsyncWaiter(asyncWaiter))
                {
                    cancellationToken.ThrowIfCancellationRequested(); // cancellation occurred
                    return(false);                                    // timeout occurred
                }
            }

            // The waiter had already been removed, which means it's already completed or is about to
            // complete, so let it, and don't return until it does.
            return(await asyncWaiter.ConfigureAwait(false));
        }
            public void SignalMutex(ref LockHolder lockHolder)
            {
                s_lock.VerifyIsLocked();

                if (!IsMutex)
                {
                    lockHolder.Dispose();
                    WaitHandle.ThrowInvalidHandleException();
                }

                if (IsSignaled || _ownershipInfo !.Thread != Thread.CurrentThread)
                {
                    lockHolder.Dispose();
                    throw new ApplicationException(SR.Arg_SynchronizationLockException);
                }

                if (!_ownershipInfo !.TryDecrementReacquireCount())
                {
                    SignalMutex(isAbandoned: false);
                }
            }
Beispiel #23
0
        /// <summary>
        /// Releases the resources used by this <see cref="T:System.Threading.ThreadLocal{T}" /> instance.
        /// </summary>
        /// <param name="disposing">
        /// A Boolean value that indicates whether this method is being called due to a call to <see cref="Dispose()"/>.
        /// </param>
        /// <remarks>
        /// Unlike most of the members of <see cref="T:System.Threading.ThreadLocal{T}"/>, this method is not thread-safe.
        /// </remarks>
        protected virtual void Dispose(bool disposing)
        {
            int id;

            using (LockHolder.Hold(s_idManager.m_lock))
            {
                id             = ~m_idComplement;
                m_idComplement = 0;

                if (id < 0 || !m_initialized)
                {
                    Debug.Assert(id >= 0 || !m_initialized, "expected id >= 0 if initialized");

                    // Handle double Dispose calls or disposal of an instance whose constructor threw an exception.
                    return;
                }
                m_initialized = false;

                for (LinkedSlot linkedSlot = m_linkedSlot.Next; linkedSlot != null; linkedSlot = linkedSlot.Next)
                {
                    LinkedSlotVolatile[] slotArray = linkedSlot.SlotArray;

                    if (slotArray == null)
                    {
                        // The thread that owns this slotArray has already finished.
                        continue;
                    }

                    // Remove the reference from the LinkedSlot to the slot table.
                    linkedSlot.SlotArray = null;

                    // And clear the references from the slot table to the linked slot and the value so that
                    // both can get garbage collected.
                    slotArray[id].Value.Value = default(T);
                    slotArray[id].Value       = null;
                }
            }
            m_linkedSlot = null;
            s_idManager.ReturnId(id);
        }
Beispiel #24
0
        public static int SignalAndWait(
            WaitableObject waitableObjectToSignal,
            WaitableObject waitableObjectToWaitOn,
            int timeoutMilliseconds,
            bool interruptible = true,
            bool prioritize    = false)
        {
            Debug.Assert(waitableObjectToSignal != null);
            Debug.Assert(waitableObjectToWaitOn != null);
            Debug.Assert(timeoutMilliseconds >= -1);

            ThreadWaitInfo waitInfo   = Thread.CurrentThread.WaitInfo;
            LockHolder     lockHolder = new LockHolder(s_lock);

            try
            {
                // A pending interrupt does not signal the specified handle
                if (interruptible && waitInfo.CheckAndResetPendingInterrupt)
                {
                    lockHolder.Dispose();
                    throw new ThreadInterruptedException();
                }

                try
                {
                    waitableObjectToSignal.Signal(1, ref lockHolder);
                }
                catch (SemaphoreFullException ex)
                {
                    s_lock.VerifyIsNotLocked();
                    throw new InvalidOperationException(SR.Threading_WaitHandleTooManyPosts, ex);
                }
                return(waitableObjectToWaitOn.Wait_Locked(waitInfo, timeoutMilliseconds, interruptible, prioritize, ref lockHolder));
            }
            finally
            {
                lockHolder.Dispose();
            }
        }
Beispiel #25
0
        /// <summary>
        /// Creates a LinkedSlot and inserts it into the linked list for this ThreadLocal instance.
        /// </summary>
        private void CreateLinkedSlot(LinkedSlotVolatile[] slotArray, int id, T value)
        {
            // Create a LinkedSlot
            var linkedSlot = new LinkedSlot(slotArray);

            // Insert the LinkedSlot into the linked list maintained by this ThreadLocal<> instance and into the slot array
            using (LockHolder.Hold(s_idManager.m_lock))
            {
                // Check that the instance has not been disposed. It is important to check this under a lock, since
                // Dispose also executes under a lock.
                if (!m_initialized)
                {
                    throw new ObjectDisposedException(SR.ThreadLocal_Disposed);
                }

                LinkedSlot firstRealNode = m_linkedSlot.Next;

                //
                // Insert linkedSlot between nodes m_linkedSlot and firstRealNode.
                // (m_linkedSlot is the dummy head node that should always be in the front.)
                //
                linkedSlot.Next     = firstRealNode;
                linkedSlot.Previous = m_linkedSlot;
                linkedSlot.Value    = value;

                if (firstRealNode != null)
                {
                    firstRealNode.Previous = linkedSlot;
                }
                m_linkedSlot.Next = linkedSlot;

                // Assigning the slot under a lock prevents a race with Dispose (dispose also acquires the lock).
                // Otherwise, it would be possible that the ThreadLocal instance is disposed, another one gets created
                // with the same ID, and the write would go to the wrong instance.
                slotArray[id].Value = linkedSlot;
            }
        }
Beispiel #26
0
        internal void Fire()
        {
            bool canceled = false;

            lock (TimerQueue.Instance)
            {
                canceled = _canceled;
                if (!canceled)
                {
                    _callbacksRunning++;
                }
            }

            if (canceled)
            {
                return;
            }

            CallCallback();

            bool shouldSignal = false;

            using (LockHolder.Hold(TimerQueue.Instance.Lock))
            {
                _callbacksRunning--;
                if (_canceled && _callbacksRunning == 0 && _notifyWhenNoCallbacksRunning != null)
                {
                    shouldSignal = true;
                }
            }

            if (shouldSignal)
            {
                SignalNoCallbacksRunning();
            }
        }
            public int Wait(ThreadWaitInfo waitInfo, int timeoutMilliseconds, bool interruptible, bool prioritize)
            {
                Debug.Assert(waitInfo != null);
                Debug.Assert(waitInfo.Thread == Thread.CurrentThread);

                Debug.Assert(timeoutMilliseconds >= -1);

                var lockHolder = new LockHolder(s_lock);

                try
                {
                    if (interruptible && waitInfo.CheckAndResetPendingInterrupt)
                    {
                        lockHolder.Dispose();
                        throw new ThreadInterruptedException();
                    }

                    return(Wait_Locked(waitInfo, timeoutMilliseconds, interruptible, prioritize, ref lockHolder));
                }
                finally
                {
                    lockHolder.Dispose();
                }
            }
Beispiel #28
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;

            using (LockHolder.Hold(Lock))
            {
                //
                // since we got here, that means our previous timer has fired.
                //
                ReleaseTimer();
                m_currentNativeTimerDuration = UInt32.MaxValue;

                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)
                {
                    Debug.Assert(timer.m_dueTime != Timer.UnsignedInfiniteTimeout);

                    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 != Timer.UnsignedInfiniteTimeout)
                        {
                            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();
            }
        }
        /// <summary>
        /// Attempt to acquire a lock on the queue to mark it as in-use.
        /// </summary>
        /// <param name="lockObject">If locking the queue for use was
        /// successful (returned true), lockObject is an IDisposable that
        /// will discard the lock when disposed.</param>
        /// <returns>True if the queue is now marked as in-use, false if the
        /// queue could not be marked as in-use due to being blocked (or
        /// one of its lockqueues was in-use).</returns>
        public bool TryLock(out IDisposable lockObject)
        {
            Log.Info(string.Format("Queue: '{0}' is attempting to be in-use, trying to lock related queues", Name));

            lockObject = null;
            lock (blockingLockObject)
            {
                if (IsBlocked)
                {
                    Log.Info(string.Format("Queue: '{0}' is locked and cannot be in-use", Name));
                    return false;
                }

                IList<IIntegrationQueue> lockedQueues = new List<IIntegrationQueue>();
                bool failed = false;

                foreach (IIntegrationQueue queue in LockQueues)
                {
                    if (queue.BlockQueue(this))
                    {
                        Log.Info(string.Format("Queue: '{0}' has acquired a lock against queue '{1}'", Name, queue.Name));
                        lockedQueues.Add(queue);
                    }
                    else
                    {
                        Log.Info(string.Format("Queue: '{0}' has FAILED to acquire a lock against queue '{1}'", Name, queue.Name));
                        failed = true;
                        break;
                    }
                }

                if (failed)
                {
                    foreach (IIntegrationQueue queue in lockedQueues)
                    {
                        Log.Info(string.Format("Queue: '{0}' has released a lock against queue '{1}'", Name, queue.Name));
                        queue.UnblockQueue(this);
                        return false;
                    }
                }

                lockObject = new LockHolder(this, lockedQueues);
                inUse = true;
                return true;
            }
        }
Beispiel #30
0
        /// <summary>
        /// Exits the <see cref="SemaphoreSlim"/> a specified number of times.
        /// </summary>
        /// <param name="releaseCount">The number of times to exit the semaphore.</param>
        /// <returns>The previous count of the <see cref="SemaphoreSlim"/>.</returns>
        /// <exception cref="T:System.ArgumentOutOfRangeException"><paramref name="releaseCount"/> is less
        /// than 1.</exception>
        /// <exception cref="T:System.Threading.SemaphoreFullException">The <see cref="SemaphoreSlim"/> has
        /// already reached its maximum size.</exception>
        /// <exception cref="T:System.ObjectDisposedException">The current instance has already been
        /// disposed.</exception>
        public int Release(int releaseCount)
        {
            CheckDispose();

            // Validate input
            if (releaseCount < 1)
            {
                throw new ArgumentOutOfRangeException(
                          nameof(releaseCount), releaseCount, SR.SemaphoreSlim_Release_CountWrong);
            }
            int returnCount;

            using (LockHolder.Hold(m_lock))
            {
                // Read the m_currentCount into a local variable to avoid unnecessary volatile accesses inside the lock.
                int currentCount = m_currentCount;
                returnCount = currentCount;

                // If the release count would result exceeding the maximum count, throw SemaphoreFullException.
                if (m_maxCount - currentCount < releaseCount)
                {
                    throw new SemaphoreFullException();
                }

                // Increment the count by the actual release count
                currentCount += releaseCount;

                // Signal to any synchronous waiters
                int waitCount = m_waitCount;

                int waitersToNotify = Math.Min(releaseCount, waitCount);
                for (int i = 0; i < waitersToNotify; i++)
                {
                    m_condition.SignalOne();
                }

                // Now signal to any asynchronous waiters, if there are any.  While we've already
                // signaled the synchronous waiters, we still hold the lock, and thus
                // they won't have had an opportunity to acquire this yet.  So, when releasing
                // asynchronous waiters, we assume that all synchronous waiters will eventually
                // acquire the semaphore.  That could be a faulty assumption if those synchronous
                // waits are canceled, but the wait code path will handle that.
                if (m_asyncHead != null)
                {
                    Debug.Assert(m_asyncTail != null, "tail should not be null if head isn't null");
                    int maxAsyncToRelease = currentCount - waitCount;
                    while (maxAsyncToRelease > 0 && m_asyncHead != null)
                    {
                        --currentCount;
                        --maxAsyncToRelease;

                        // Get the next async waiter to release and queue it to be completed
                        var waiterTask = m_asyncHead;
                        RemoveAsyncWaiter(waiterTask); // ensures waiterTask.Next/Prev are null
                        QueueWaiterTask(waiterTask);
                    }
                }
                m_currentCount = currentCount;

                // Exposing wait handle if it is not null
                if (m_waitHandle != null && returnCount == 0 && currentCount > 0)
                {
                    m_waitHandle.Set();
                }
            }

            // And return the count
            return(returnCount);
        }
        /// <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 deregister 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 deregisters) the callback

            return(true); //done. The wait was satisfied.
        }