System.Net.TimerThread.ThreadProc C# (CSharp) Method

ThreadProc() private static method

Thread for the timer. Ignores all exceptions. If no activity occurs for a while, the thread will shut down.

private static ThreadProc ( ) : void
return void
        private static void ThreadProc()
        {
            if (NetEventSource.IsEnabled) NetEventSource.Enter(null);
#if DEBUG
            DebugThreadTracking.SetThreadSource(ThreadKinds.Timer);
            using (DebugThreadTracking.SetThreadKind(ThreadKinds.System | ThreadKinds.Async))
            {
#endif
                // 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;

                                if (NetEventSource.IsEnabled) NetEventSource.Info(null, $"Waiting for {waitDuration}ms");

                                int waitResult = WaitHandle.WaitAny(s_ThreadEvents, waitDuration, false);

                                // 0 is s_ThreadShutdownEvent - die.
                                if (waitResult == 0)
                                {
                                    if (NetEventSource.IsEnabled) NetEventSource.Info(null, "Awoke, cause: Shutdown");
                                    running = false;
                                    break;
                                }

                                if (NetEventSource.IsEnabled) NetEventSource.Info(null, $"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 (ExceptionCheck.IsFatal(exception))
                                throw;

                            if (NetEventSource.IsEnabled) NetEventSource.Error(null, 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 exception thrown within TimerThread - the rest are caught in Fire().
#if !DEBUG
                            Thread.Sleep(1000);
#else
                            throw;
#endif
                        }
                    }
                }

                if (NetEventSource.IsEnabled) NetEventSource.Info(null, "Stop");
#if DEBUG
            }
#endif
        }