_TimerThread.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ FX-1434 / FX-1434 / 1.0 / untmp / whidbey / REDBITS / ndp / fx / src / Net / System / Net / _TimerThread.cs / 1 / _TimerThread.cs

                            //------------------------------------------------------------------------------ 
// 
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// 
//----------------------------------------------------------------------------- 

namespace System.Net { 
 
    using System.Collections;
    using System.Globalization; 
    using System.Threading;
    using System.Collections.Generic;
    using System.Runtime.InteropServices;
 
    /// 
    /// Acts as countdown timer, used to measure elapsed time over a sync operation. 
    ///  
    internal static class TimerThread {
        ///  
        /// Represents a queue of timers, which all have the same duration.
        /// 
        internal abstract class Queue {
            private readonly int m_DurationMilliseconds; 

            internal Queue(int durationMilliseconds) { 
                m_DurationMilliseconds = durationMilliseconds; 
            }
 
            /// 
            /// The duration in milliseconds of timers in this queue.
            /// 
            internal int Duration { 
                get {
                    return m_DurationMilliseconds; 
                } 
            }
 
            /// 
            /// Creates and returns a handle to a new polled timer.
            /// 
            internal Timer CreateTimer() { 
                return CreateTimer(null, null);
            } 
 
            /*
            // Consider removing. 
            /// 
            /// Creates and returns a handle to a new timer.
            /// 
            internal Timer CreateTimer(Callback callback) { 
                return CreateTimer(callback, null);
            } 
            */ 

            ///  
            /// Creates and returns a handle to a new timer with attached context.
            /// 
            internal abstract Timer CreateTimer(Callback callback, object context);
        } 

        ///  
        /// Represents a timer and provides a mechanism to cancel. 
        /// 
        internal abstract class Timer : IDisposable 
        {
            private readonly int m_StartTimeMilliseconds;
            private readonly int m_DurationMilliseconds;
 
            internal Timer(int durationMilliseconds) {
                m_DurationMilliseconds = durationMilliseconds; 
                m_StartTimeMilliseconds = Environment.TickCount; 
            }
 
            /// 
            /// The duration in milliseconds of timer.
            /// 
            internal int Duration { 
                get {
                    return m_DurationMilliseconds; 
                } 
            }
 
            /// 
            /// The time (relative to Environment.TickCount) when the timer started.
            /// 
            internal int StartTime { 
                get {
                    return m_StartTimeMilliseconds; 
                } 
            }
 
            /// 
            /// The time (relative to Environment.TickCount) when the timer will expire.
            /// 
            internal int Expiration { 
                get {
                    return unchecked(m_StartTimeMilliseconds + m_DurationMilliseconds); 
                } 
            }
 
            /*
            // Consider removing.
            /// 
            /// The amount of time the timer has been running.  If it equals Duration, it has fired.  1 less means it has expired but 
            /// not yet fired.  Int32.MaxValue is the ceiling - the actual value could be longer.  In the case of infinite timers, this
            /// value becomes unreliable when TickCount wraps (about 46 days). 
            ///  
            internal int Elapsed {
                get { 
                    if (HasExpired || Duration == 0) {
                        return Duration;
                    }
 
                    int now = Environment.TickCount;
                    if (Duration == TimeoutInfinite) 
                    { 
                        return (int) (Math.Min((uint) unchecked(now - StartTime), (uint) Int32.MaxValue);
                    } 
                    else
                    {
                        return (IsTickBetween(StartTime, Expiration, now) && Duration > 1) ?
                            (int) (Math.Min((uint) unchecked(now - StartTime), Duration - 2) : Duration - 1; 
                    }
                } 
            } 
            */
 
            /// 
            /// The amount of time left on the timer.  0 means it has fired.  1 means it has expired but
            /// not yet fired.  -1 means infinite.  Int32.MaxValue is the ceiling - the actual value could be longer.
            ///  
            internal int TimeRemaining {
                get { 
                    if (HasExpired) { 
                        return 0;
                    } 

                    if (Duration == Timeout.Infinite) {
                        return Timeout.Infinite;
                    } 

                    int now = Environment.TickCount; 
                    int remaining = IsTickBetween(StartTime, Expiration, now) ? 
                        (int) (Math.Min((uint) unchecked(Expiration - now), (uint) Int32.MaxValue)) : 0;
                    return remaining < 2 ? remaining + 1 : remaining; 
                }
            }

            ///  
            /// Cancels the timer.  Returns true if the timer hasn't and won't fire; false if it has or will.
            ///  
            internal abstract bool Cancel(); 

            ///  
            /// Whether or not the timer has expired.
            /// 
            internal abstract bool HasExpired { get; }
 
            public void Dispose()
            { 
                Cancel(); 
            }
        } 

        /// 
        /// Prototype for the callback that is called when a timer expires.
        ///  
        internal delegate void Callback(Timer timer, int timeNoticed, object context);
 
        private const int c_ThreadIdleTimeoutMilliseconds = 30 * 1000; 
        private const int c_CacheScanPerIterations = 32;
        private const int c_TickCountResolution = 15; 

        private static LinkedList s_Queues = new LinkedList();
        private static LinkedList s_NewQueues = new LinkedList();
        private static int s_ThreadState = (int) TimerThreadState.Idle;  // Really a TimerThreadState, but need an int for Interlocked. 
        private static AutoResetEvent s_ThreadReadyEvent = new AutoResetEvent(false);
        private static ManualResetEvent s_ThreadShutdownEvent = new ManualResetEvent(false); 
        private static WaitHandle[] s_ThreadEvents; 
        private static int s_CacheScanIteration;
        private static Hashtable s_QueuesCache = new Hashtable(); 

        static TimerThread() {
            s_ThreadEvents = new WaitHandle[] { s_ThreadShutdownEvent, s_ThreadReadyEvent };
            AppDomain.CurrentDomain.DomainUnload += new EventHandler(OnDomainUnload); 
        }
 
        ///  
        /// The possible states of the timer thread.
        ///  
        private enum TimerThreadState {
            Idle,
            Running,
            Stopped 
        }
 
        ///  
        /// The main external entry-point, allows creating new timer queues.
        ///  
        internal static Queue CreateQueue(int durationMilliseconds)
        {
            if (durationMilliseconds == Timeout.Infinite) {
                return new InfiniteTimerQueue(); 
            }
 
            if (durationMilliseconds < 0) { 
                throw new ArgumentOutOfRangeException("durationMilliseconds");
            } 

            // Queues are held with a weak reference so they can simply be abandoned and automatically cleaned up.
            TimerQueue queue;
            lock(s_NewQueues) { 
                queue = new TimerQueue(durationMilliseconds);
                WeakReference weakQueue = new WeakReference(queue); 
                s_NewQueues.AddLast(weakQueue); 
            }
 
            return queue;
        }

        ///  
        /// Alternative cache-based queue factory.  Always synchronized.
        ///  
        internal static Queue GetOrCreateQueue(int durationMilliseconds) { 
            if (durationMilliseconds == Timeout.Infinite) {
                return new InfiniteTimerQueue(); 
            }

            if (durationMilliseconds < 0) {
                throw new ArgumentOutOfRangeException("durationMilliseconds"); 
            }
 
            TimerQueue queue; 
            WeakReference weakQueue = (WeakReference) s_QueuesCache[durationMilliseconds];
            if (weakQueue == null || (queue = (TimerQueue) weakQueue.Target) == null) { 
                lock(s_NewQueues) {
                    weakQueue = (WeakReference) s_QueuesCache[durationMilliseconds];
                    if (weakQueue == null || (queue = (TimerQueue) weakQueue.Target) == null) {
                        queue = new TimerQueue(durationMilliseconds); 
                        weakQueue = new WeakReference(queue);
                        s_NewQueues.AddLast(weakQueue); 
                        s_QueuesCache[durationMilliseconds] = weakQueue; 

                        // Take advantage of this lock to periodically scan the table for garbage. 
                        if (++s_CacheScanIteration % c_CacheScanPerIterations == 0) {
                            List garbage = new List();
                            foreach (DictionaryEntry pair in s_QueuesCache) {
                                if (((WeakReference) pair.Value).Target == null) { 
                                    garbage.Add((int) pair.Key);
                                } 
                            } 
                            for (int i = 0; i < garbage.Count; i++) {
                                s_QueuesCache.Remove(garbage[i]); 
                            }
                        }
                    }
                } 
            }
 
            return queue; 
        }
 
        /// 
        /// Represents a queue of timers of fixed duration.
        /// 
        private class TimerQueue : Queue { 
            // This is a GCHandle that holds onto the TimerQueue when active timers are in it.
            // The TimerThread only holds WeakReferences to it so that it can be collected when the user lets go of it. 
            // But we don't want the user to HAVE to keep a reference to it when timers are active in it. 
            // It gets created when the first timer gets added, and cleaned up when the TimerThread notices it's empty.
            // The TimerThread will always notice it's empty eventually, since the TimerThread will always wake up and 
            // try to fire the timer, even if it was cancelled and removed prematurely.
            private IntPtr m_ThisHandle;

            // This sentinel TimerNode acts as both the head and the tail, allowing nodes to go in and out of the list without updating 
            // any TimerQueue members.  m_Timers.Next is the true head, and .Prev the true tail.  This also serves as the list's lock.
            private readonly TimerNode m_Timers; 
 
            /// 
            /// Create a new TimerQueue.  TimerQueues must be created while s_NewQueues is locked in 
            /// order to synchronize with Shutdown().
            /// 
            /// 
            internal TimerQueue(int durationMilliseconds) : 
                base(durationMilliseconds)
            { 
                // Create the doubly-linked list with a sentinel head and tail so that this member never needs updating. 
                m_Timers = new TimerNode();
                m_Timers.Next = m_Timers; 
                m_Timers.Prev = m_Timers;

                // If ReleaseHandle comes back, we need something like this here.
                // m_HandleFrozen = s_ThreadState == (int) TimerThreadState.Stopped ? 1 : 0; 
            }
 
            ///  
            /// Creates new timers.  This method is thread-safe.
            ///  
            internal override Timer CreateTimer(Callback callback, object context) {
                TimerNode timer = new TimerNode(callback, context, Duration, m_Timers);

                // Add this on the tail.  (Actually, one before the tail - m_Timers is the sentinel tail.) 
                bool needProd = false;
                lock (m_Timers) 
                { 
                    GlobalLog.Assert(m_Timers.Prev.Next == m_Timers, "TimerThread#{0}::CreateTimer()|m_Tail corruption.", Thread.CurrentThread.ManagedThreadId.ToString());
 
                    // If this is the first timer in the list, we need to create a queue handle and prod the timer thread.
                    if (m_Timers.Next == m_Timers)
                    {
                        if (m_ThisHandle == IntPtr.Zero) 
                        {
                            m_ThisHandle = (IntPtr) GCHandle.Alloc(this); 
                        } 
                        needProd = true;
                    } 

                    timer.Next = m_Timers;
                    timer.Prev = m_Timers.Prev;
                    m_Timers.Prev.Next = timer; 
                    m_Timers.Prev = timer;
                } 
 
                // If, after we add the new tail, there is a chance that the tail is the next
                // node to be processed, we need to wake up the timer thread. 
                if (needProd)
                {
                    TimerThread.Prod();
                } 

                return timer; 
            } 

            ///  
            /// Called by the timer thread to fire the expired timers.  Returns true if there are future timers
            /// in the queue, and if so, also sets nextExpiration.
            /// 
            internal bool Fire(out int nextExpiration) { 
                while (true)
                { 
                    // Check if we got to the end.  If so, free the handle. 
                    TimerNode timer = m_Timers.Next;
                    if (timer == m_Timers) 
                    {
                        lock (m_Timers)
                        {
                            timer = m_Timers.Next; 
                            if (timer == m_Timers)
                            { 
                                if(m_ThisHandle != IntPtr.Zero) 
                                {
                                    ((GCHandle) m_ThisHandle).Free(); 
                                    m_ThisHandle = IntPtr.Zero;
                                }

                                nextExpiration = 0; 
                                return false;
                            } 
                        } 
                    }
 
                    if (!timer.Fire())
                    {
                        nextExpiration = timer.Expiration;
                        return true; 
                    }
                } 
            } 

            /* Currently unused.  If revived, needs to be changed to the new design of m_ThisHandle. 
            /// 
            /// Release the GCHandle to this object, and prevent it from ever being allocated again.
            /// 
            internal void ReleaseHandle() 
            {
                if (Interlocked.Exchange(ref m_HandleFrozen, 1) == 1) { 
                    return; 
                }
 
                // Add a fake timer to the count.  This will prevent the count ever again reaching zero, effectively
                // disabling the GCHandle alloc/free logic.  If it finds that one is allocated, deallocate it.
                if (Interlocked.Increment(ref m_ActiveTimerCount) != 1) {
                    IntPtr handle; 
                    while ((handle = Interlocked.Exchange(ref m_ThisHandle, IntPtr.Zero)) == IntPtr.Zero)
                    { 
                        Thread.SpinWait(1); 
                    }
                    ((GCHandle)handle).Free(); 
                }
            }
            */
        } 

        ///  
        /// A special dummy implementation for a queue of timers of infinite duration. 
        /// 
        private class InfiniteTimerQueue : Queue { 
            internal InfiniteTimerQueue() : base(Timeout.Infinite) { }

            /// 
            /// Always returns a dummy infinite timer. 
            /// 
            internal override Timer CreateTimer(Callback callback, object context) 
            { 
                return new InfiniteTimer();
            } 
        }

        /// 
        /// Internal representation of an individual timer. 
        /// 
        private class TimerNode : Timer { 
            private TimerState m_TimerState; 
            private Callback m_Callback;
            private object m_Context; 
            private object m_QueueLock;
            private TimerNode next;
            private TimerNode prev;
 
            /// 
            /// Status of the timer. 
            ///  
            private enum TimerState {
                Ready, 
                Fired,
                Cancelled,
                Sentinel
            } 

            internal TimerNode(Callback callback, object context, int durationMilliseconds, object queueLock) : base(durationMilliseconds) 
            { 
                if (callback != null)
                { 
                    m_Callback = callback;
                    m_Context = context;
                }
                m_TimerState = TimerState.Ready; 
                m_QueueLock = queueLock;
                GlobalLog.Print("TimerThreadTimer#" + StartTime.ToString() + "::.ctor()"); 
            } 

            // A sentinel node - both the head and tail are one, which prevent the head and tail from ever having to be updated. 
            internal TimerNode() : base (0)
            {
                m_TimerState = TimerState.Sentinel;
            } 

            /* 
            // Consider removing. 
            internal bool IsDead
            { 
                get
                {
                    return m_TimerState != TimerState.Ready;
                } 
            }
            */ 
 
            internal override bool HasExpired {
                get { 
                    return m_TimerState == TimerState.Fired;
                }
            }
 
            internal TimerNode Next
            { 
                get 
                {
                    return next; 
                }

                set
                { 
                    next = value;
                } 
            } 

            internal TimerNode Prev 
            {
                get
                {
                    return prev; 
                }
 
                set 
                {
                    prev = value; 
                }
            }

            ///  
            /// Cancels the timer.  Returns true if it hasn't and won't fire; false if it has or will, or has already been cancelled.
            ///  
            internal override bool Cancel() { 
                if (m_TimerState == TimerState.Ready)
                { 
                    lock (m_QueueLock)
                    {
                        if (m_TimerState == TimerState.Ready)
                        { 
                            // Remove it from the list.  This keeps the list from getting to big when there are a lot of rapid creations
                            // and cancellations.  This is done before setting it to Cancelled to try to prevent the Fire() loop from 
                            // seeing it, or if it does, of having to take a lock to synchronize with the state of the list. 
                            Next.Prev = Prev;
                            Prev.Next = Next; 

                            // Just cleanup.  Doesn't need to be in the lock but is easier to have here.
                            Next = null;
                            Prev = null; 
                            m_Callback = null;
                            m_Context = null; 
 
                            m_TimerState = TimerState.Cancelled;
 
                            GlobalLog.Print("TimerThreadTimer#" + StartTime.ToString() + "::Cancel() (success)");
                            return true;
                        }
                    } 
                }
 
                GlobalLog.Print("TimerThreadTimer#" + StartTime.ToString() + "::Cancel() (failure)"); 
                return false;
            } 

            /// 
            /// Fires the timer if it is still active and has expired.  Returns
            /// true if it can be deleted, or false if it is still timing. 
            /// 
            internal bool Fire() { 
                GlobalLog.Assert(m_TimerState != TimerState.Sentinel, "TimerThread#{0}::Fire()|TimerQueue tried to Fire a Sentinel.", Thread.CurrentThread.ManagedThreadId.ToString()); 

                if (m_TimerState != TimerState.Ready) 
                {
                    return true;
                }
 
                // Must get the current tick count within this method so it is guaranteed not to be before
                // StartTime, which is set in the constructor. 
                int nowMilliseconds = Environment.TickCount; 
                if (IsTickBetween(StartTime, Expiration, nowMilliseconds)) {
                    GlobalLog.Print("TimerThreadTimer#" + StartTime + "::Fire() Not firing (" + StartTime + " <= " + nowMilliseconds + " < " + Expiration + ")"); 
                    return false;
                }

                bool needCallback = false; 
                lock (m_QueueLock)
                { 
                    if (m_TimerState == TimerState.Ready) 
                    {
                        GlobalLog.Print("TimerThreadTimer#" + StartTime + "::Fire() Firing (" + StartTime + " <= " + nowMilliseconds + " >= " + Expiration + ")"); 
                        m_TimerState = TimerState.Fired;

                        // Remove it from the list.
                        Next.Prev = Prev; 
                        Prev.Next = Next;
 
                        // Doesn't need to be in the lock but is easier to have here. 
                        Next = null;
                        Prev = null; 
                        needCallback = m_Callback != null;
                    }
                }
 
                if (needCallback)
                { 
                    try { 
                        Callback callback = m_Callback;
                        object context = m_Context; 
                        m_Callback = null;
                        m_Context = null;
                        callback(this, nowMilliseconds, context);
                    } 
                    catch (Exception exception) {
                        if (NclUtilities.IsFatal(exception)) throw; 
 
                        if (Logging.On) Logging.PrintError(Logging.Web, "TimerThreadTimer#" + StartTime.ToString(NumberFormatInfo.InvariantInfo) + "::Fire() - " + SR.GetString(SR.net_log_exception_in_callback, exception));
                        GlobalLog.Print("TimerThreadTimer#" + StartTime + "::Fire() exception in callback: " + exception); 

                        // This thread is not allowed to go into user code, so we should never get an exception here.
                        // So, in debug, throw it up, killing the AppDomain.  In release, we'll just ignore it.
#if DEBUG 
                        throw;
#endif 
                    } 
                }
 
                return true;
            }
        }
 
        /// 
        /// A dummy infinite timer. 
        ///  
        private class InfiniteTimer : Timer {
            internal InfiniteTimer() : base(Timeout.Infinite) { } 

            private int cancelled;

            internal override bool HasExpired { 
                get {
                    return false; 
                } 
            }
 
            /// 
            /// Cancels the timer.  Returns true the first time, false after that.
            /// 
            internal override bool Cancel() { 
                return Interlocked.Exchange(ref cancelled, 1) == 0;
            } 
        } 

        ///  
        /// Internal mechanism used when timers are added to wake up / create the thread.
        /// 
        private static void Prod() {
            s_ThreadReadyEvent.Set(); 
            TimerThreadState oldState = (TimerThreadState) Interlocked.CompareExchange(
                ref s_ThreadState, 
                (int) TimerThreadState.Running, 
                (int) TimerThreadState.Idle);
 
            if (oldState == TimerThreadState.Idle) {
                new Thread(new ThreadStart(ThreadProc)).Start();
            }
        } 

        ///  
        /// Thread for the timer.  Ignores all exceptions except ThreadAbort.  If no activity occurs for a while, 
        /// the thread will shut down.
        ///  
        private static void ThreadProc()
        {
#if DEBUG
            GlobalLog.SetThreadSource(ThreadKinds.Timer); 
            using (GlobalLog.SetThreadKind(ThreadKinds.System | ThreadKinds.Async)) {
#endif 
            GlobalLog.Print("TimerThread#" + Thread.CurrentThread.ManagedThreadId.ToString() + "::ThreadProc() Start"); 

            // t_IsTimerThread = true; -- Not used anywhere. 

            // Set this thread as a background thread.  On AppDomain/Process shutdown, the thread will just be killed.
            Thread.CurrentThread.IsBackground = true;
 
            // Keep a permanent lock on s_Queues.  This lets for example Shutdown() know when this thread isn't running.
            lock (s_Queues) { 
 
                // If shutdown was recently called, abort here.
                if (Interlocked.CompareExchange(ref s_ThreadState, (int) TimerThreadState.Running, (int) TimerThreadState.Running) != 
                    (int) TimerThreadState.Running) {
                    return;
                }
 
                bool running = true;
                while(running) { 
                    try { 
                        s_ThreadReadyEvent.Reset();
 
                        while (true) {
                            // Copy all the new queues to the real queues.  Since only this thread modifies the real queues, it doesn't have to lock it.
                            if (s_NewQueues.Count > 0) {
                                lock (s_NewQueues) { 
                                    for (LinkedListNode node = s_NewQueues.First; node != null; node = s_NewQueues.First) {
                                        s_NewQueues.Remove(node); 
                                        s_Queues.AddLast(node); 
                                    }
                                } 
                            }

                            int now = Environment.TickCount;
                            int nextTick = 0; 
                            bool haveNextTick = false;
                            for (LinkedListNode node = s_Queues.First; node != null; /* node = node.Next must be done in the body */) { 
                                TimerQueue queue = (TimerQueue) node.Value.Target; 
                                if (queue == null) {
                                    LinkedListNode next = node.Next; 
                                    s_Queues.Remove(node);
                                    node = next;
                                    continue;
                                } 

                                // Fire() will always return values that should be interpreted as later than 'now' (that is, even if 'now' is 
                                // returned, it is 0x100000000 milliseconds in the future).  There's also a chance that Fire() will return a value 
                                // intended as > 0x100000000 milliseconds from 'now'.  Either case will just cause an extra scan through the timers.
                                int nextTickInstance; 
                                if (queue.Fire(out nextTickInstance) && (!haveNextTick || IsTickBetween(now, nextTick, nextTickInstance))){
                                    nextTick = nextTickInstance;
                                    haveNextTick = true;
                                } 

                                node = node.Next; 
                            } 

                            // Figure out how long to wait, taking into account how long the loop took. 
                            // Add 15 ms to compensate for poor TickCount resolution (want to guarantee a firing).
                            int newNow = Environment.TickCount;
                            int waitDuration = haveNextTick ?
                                (int) (IsTickBetween(now, nextTick, newNow) ? 
                                    Math.Min(unchecked((uint) (nextTick - newNow)), (uint) (Int32.MaxValue - c_TickCountResolution)) + c_TickCountResolution :
                                    0) : 
                                c_ThreadIdleTimeoutMilliseconds; 

                            GlobalLog.Print("TimerThread#" + Thread.CurrentThread.ManagedThreadId.ToString() + "::ThreadProc() Waiting for " + waitDuration + "ms"); 
                            int waitResult = WaitHandle.WaitAny(s_ThreadEvents, waitDuration, false);

                            // 0 is s_ThreadShutdownEvent - die.
                            if (waitResult == 0) { 
                                GlobalLog.Print("TimerThread#" + Thread.CurrentThread.ManagedThreadId.ToString() + "::ThreadProc() Awoke, cause: Shutdown");
                                running = false; 
                                break; 
                            }
 
                            GlobalLog.Print("TimerThread#" + Thread.CurrentThread.ManagedThreadId.ToString() + "::ThreadProc() Awoke, cause: " + (waitResult == WaitHandle.WaitTimeout ? "Timeout" : "Prod"));

                            // If we timed out with nothing to do, shut down.
                            if (waitResult == WaitHandle.WaitTimeout && !haveNextTick) { 
                                Interlocked.CompareExchange(ref s_ThreadState, (int) TimerThreadState.Idle, (int) TimerThreadState.Running);
                                // There could have been one more prod between the wait and the exchange.  Check, and abort if necessary. 
                                if (s_ThreadReadyEvent.WaitOne(0, false)) { 
                                    if (Interlocked.CompareExchange(ref s_ThreadState, (int) TimerThreadState.Running, (int) TimerThreadState.Idle) ==
                                        (int) TimerThreadState.Idle) { 
                                        continue;
                                    }
                                }
 
                                running = false;
                                break; 
                            } 
                        }
                    } 
                    catch (Exception exception) {
                        if (NclUtilities.IsFatal(exception)) throw;

                        if (Logging.On) Logging.PrintError(Logging.Web, "TimerThread#" + Thread.CurrentThread.ManagedThreadId.ToString(NumberFormatInfo.InvariantInfo) + "::ThreadProc() - Exception:" + exception.ToString()); 
                        GlobalLog.Print("TimerThread#" + Thread.CurrentThread.ManagedThreadId.ToString() + "::ThreadProc() exception: " + exception);
 
                        // The only options are to continue processing and likely enter an error-loop, 
                        // shut down timers for this AppDomain, or shut down the AppDomain.  Go with shutting
                        // down the AppDomain in debug, and going into a loop in retail, but try to make the 
                        // loop somewhat slow.  Note that in retail, this can only be triggered by OutOfMemory or StackOverflow,
                        // or an thrown within TimerThread - the rest are caught in Fire().
#if !DEBUG
                        Thread.Sleep(1000); 
#else
                        throw; 
#endif 
                    }
                } 
            }

            GlobalLog.Print("TimerThread#" + Thread.CurrentThread.ManagedThreadId.ToString() + "::ThreadProc() Stop");
#if DEBUG 
            }
#endif 
        } 

        /* Currently unused. 
        /// 
        /// Stops the timer thread and prevents a new one from forming.  No more timers can expire.
        /// 
        internal static void Shutdown() { 
            StopTimerThread();
 
            // As long as TimerQueues are always created and added to s_NewQueues within the same lock, 
            // this should catch all existing TimerQueues (and all new onew will see s_ThreadState).
            lock (s_NewQueues) { 
                foreach (WeakReference node in s_NewQueues) {
                    TimerQueue queue = (TimerQueue)node.Target;
                    if(queue != null) {
                        queue.ReleaseHandle(); 
                    }
                } 
            } 

            // Once that thread is gone, release all the remaining GCHandles. 
            lock (s_Queues) {
                foreach (WeakReference node in s_Queues) {
                    TimerQueue queue = (TimerQueue)node.Target;
                    if(queue != null) { 
                        queue.ReleaseHandle();
                    } 
                } 
            }
        } 
        */

        private static void StopTimerThread()
        { 
            Interlocked.Exchange(ref s_ThreadState, (int) TimerThreadState.Stopped);
            s_ThreadShutdownEvent.Set(); 
        } 

        ///  
        /// Helper for deciding whether a given TickCount is before or after a given expiration
        /// tick count assuming that it can't be before a given starting TickCount.
        /// 
        private static bool IsTickBetween(int start, int end, int comparand) { 
            // Assumes that if start and end are equal, they are the same time.
            // Assumes that if the comparand and start are equal, no time has passed, 
            // and that if the comparand and end are equal, end has occurred. 
            return ((start <= comparand) == (end <= comparand)) != (start <= end);
        } 

        /// 
        /// When the AppDomain is shut down, the timer thread is stopped.
        ///  
        private static void OnDomainUnload(object sender, EventArgs e) {
            try 
            { 
                StopTimerThread();
            } 
            catch { }
        }

        /* 
        /// 
        /// This thread static can be used to tell whether the current thread is the TimerThread thread. 
        ///  
        [ThreadStatic]
        private static bool t_IsTimerThread; 

        // Consider removing.
        internal static bool IsTimerThread
        { 
            get
            { 
                return t_IsTimerThread; 
            }
        } 
        */
    }
}
                        

Link Menu

Network programming in C#, Network Programming in VB.NET, Network Programming in .NET
This book is available now!
Buy at Amazon US or
Buy at Amazon UK