File: net\System\Net\_TimerThread.cs
Project: ndp\fx\src\System.csproj (System)
//------------------------------------------------------------------------------
// <copyright file="_TimerThread.cs" company="Microsoft">
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// </copyright>
//------------------------------------------------------------------------------
 
namespace System.Net {
 
    using System.Collections;
    using System.Globalization;
    using System.Threading;
    using System.Collections.Generic;
    using System.Runtime.InteropServices;
 
    /// <summary>
    /// <para>Acts as countdown timer, used to measure elapsed time over a sync operation.</para>
    /// </summary>
    internal static class TimerThread {
        /// <summary>
        /// <para>Represents a queue of timers, which all have the same duration.</para>
        /// </summary>
        internal abstract class Queue {
            private readonly int m_DurationMilliseconds;
 
            internal Queue(int durationMilliseconds) {
                m_DurationMilliseconds = durationMilliseconds;
            }
 
            /// <summary>
            /// <para>The duration in milliseconds of timers in this queue.</para>
            /// </summary>
            internal int Duration {
                get {
                    return m_DurationMilliseconds;
                }
            }
 
            /// <summary>
            /// <para>Creates and returns a handle to a new polled timer.</para>
            /// </summary>
            internal Timer CreateTimer() {
                return CreateTimer(null, null);
            }
 
            /*
            // Consider removing.
            /// <summary>
            /// <para>Creates and returns a handle to a new timer.</para>
            /// </summary>
            internal Timer CreateTimer(Callback callback) {
                return CreateTimer(callback, null);
            }
            */
 
            /// <summary>
            /// <para>Creates and returns a handle to a new timer with attached context.</para>
            /// </summary>
            internal abstract Timer CreateTimer(Callback callback, object context);
        }
 
        /// <summary>
        /// <para>Represents a timer and provides a mechanism to cancel.</para>
        /// </summary>
        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;
            }
 
            /// <summary>
            /// <para>The duration in milliseconds of timer.</para>
            /// </summary>
            internal int Duration {
                get {
                    return m_DurationMilliseconds;
                }
            }
 
            /// <summary>
            /// <para>The time (relative to Environment.TickCount) when the timer started.</para>
            /// </summary>
            internal int StartTime {
                get {
                    return m_StartTimeMilliseconds;
                }
            }
 
            /// <summary>
            /// <para>The time (relative to Environment.TickCount) when the timer will expire.</para>
            /// </summary>
            internal int Expiration {
                get {
                    return unchecked(m_StartTimeMilliseconds + m_DurationMilliseconds);
                }
            }
 
            /*
            // Consider removing.
            /// <summary>
            /// <para>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).</para>
            /// </summary>
            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;
                    }
                }
            } 
            */
 
            /// <summary>
            /// <para>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.</para>
            /// </summary>
            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;
                }
            }
 
            /// <summary>
            /// <para>Cancels the timer.  Returns true if the timer hasn't and won't fire; false if it has or will.</para>
            /// </summary>
            internal abstract bool Cancel();
 
            /// <summary>
            /// <para>Whether or not the timer has expired.</para>
            /// </summary>
            internal abstract bool HasExpired { get; }
 
            public void Dispose()
            {
                Cancel();
            }
        }
 
        /// <summary>
        /// <para>Prototype for the callback that is called when a timer expires.</para>
        /// </summary>
        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<WeakReference> s_Queues = new LinkedList<WeakReference>();
        private static LinkedList<WeakReference> s_NewQueues = new LinkedList<WeakReference>();
        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);
        }
 
        /// <summary>
        /// <para>The possible states of the timer thread.</para>
        /// </summary>
        private enum TimerThreadState {
            Idle,
            Running,
            Stopped
        }
 
        /// <summary>
        /// <para>The main external entry-point, allows creating new timer queues.</para>
        /// </summary>
        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;
        }
 
        /// <summary>
        /// <para>Alternative cache-based queue factory.  Always synchronized.</para>
        /// </summary>
        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<int> garbage = new List<int>();
                            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;
        }
 
        /// <summary>
        /// <para>Represents a queue of timers of fixed duration.</para>
        /// </summary>
        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;
 
            /// <summary>
            /// <para>Create a new TimerQueue.  TimerQueues must be created while s_NewQueues is locked in
            /// order to synchronize with Shutdown().</para>
            /// </summary>
            /// <param name="durationMilliseconds"></param>
            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;
            }
 
            /// <summary>
            /// <para>Creates new timers.  This method is thread-safe.</para>
            /// </summary>
            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;
            }
 
            /// <summary>
            /// <para>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.</para>
            /// </summary>
            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.
            /// <summary>
            /// <para>Release the GCHandle to this object, and prevent it from ever being allocated again.</para>
            /// </summary>
            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();
                }
            }
            */
        }
 
        /// <summary>
        /// <para>A special dummy implementation for a queue of timers of infinite duration.</para>
        /// </summary>
        private class InfiniteTimerQueue : Queue {
            internal InfiniteTimerQueue() : base(Timeout.Infinite) { }
 
            /// <summary>
            /// <para>Always returns a dummy infinite timer.</para>
            /// </summary>
            internal override Timer CreateTimer(Callback callback, object context)
            {
                return new InfiniteTimer();
            }
        }
 
        /// <summary>
        /// <para>Internal representation of an individual timer.</para>
        /// </summary>
        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;
 
            /// <summary>
            /// <para>Status of the timer.</para>
            /// </summary>
            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;
                }
            }
 
            /// <summary>
            /// <para>Cancels the timer.  Returns true if it hasn't and won't fire; false if it has or will, or has already been cancelled.</para>
            /// </summary>
            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;
            }
 
            /// <summary>
            /// <para>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.</para>
            /// </summary>
            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;
            }
        }
 
        /// <summary>
        /// <para>A dummy infinite timer.</para>
        /// </summary>
        private class InfiniteTimer : Timer {
            internal InfiniteTimer() : base(Timeout.Infinite) { }
 
            private int cancelled;
 
            internal override bool HasExpired {
                get {
                    return false;
                }
            }
 
            /// <summary>
            /// <para>Cancels the timer.  Returns true the first time, false after that.</para>
            /// </summary>
            internal override bool Cancel() {
                return Interlocked.Exchange(ref cancelled, 1) == 0;
            }
        }
 
        /// <summary>
        /// <para>Internal mechanism used when timers are added to wake up / create the thread.</para>
        /// </summary>
        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();
            }
        }
 
        /// <summary>
        /// <para>Thread for the timer.  Ignores all exceptions except ThreadAbort.  If no activity occurs for a while,
        /// the thread will shut down.</para>
        /// </summary>
        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<WeakReference> 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<WeakReference> 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<WeakReference> 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.
        /// <summary>
        /// <para>Stops the timer thread and prevents a new one from forming.  No more timers can expire.</para>
        /// </summary>
        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();
        }
 
        /// <summary>
        /// <para>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.</para>
        /// </summary>
        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);
        }
 
        /// <summary>
        /// <para>When the AppDomain is shut down, the timer thread is stopped.</para>
        /// <summary>
        private static void OnDomainUnload(object sender, EventArgs e) {
            try
            {
                StopTimerThread();
            }
            catch { }
        }
 
        /*
        /// <summary>
        /// <para>This thread static can be used to tell whether the current thread is the TimerThread thread.</para>
        /// </summary>
        [ThreadStatic]
        private static bool t_IsTimerThread;
 
        // Consider removing.
        internal static bool IsTimerThread
        {
            get
            {
                return t_IsTimerThread;
            }
        }
        */
    }
}