A place for spare thoughts

16/06/2011

CLR internals: implementation of .NET timers

Filed under: Uncategorized — Ivan Danilov @ 03:24

Recently I had conversation with Hans Passant on Stack Overflow about internal CLR implementation of timers. So I decide to do some night-source-digging and refresh my dusty C++ knowledge…

First of all I want to recommend excellent book on almost every low-level multithreading topic in Windows: Concurrent programming on Windows by Joe Duffy. Most part of the info in this post could be read inside.

First steps are fairly simple with some reflector’ing so I will skip them. Managed code ends with call to internally implemented AddNativeTimer function. And thus the story begins…

From ROTOR sources I can see that AddNativeTimer function is implemented in TimerNative::CorCreateTimer [1] which calls ThreadpoolMgr::CreateTimerQueueTimer [2]:

BOOL ThreadpoolMgr::CreateTimerQueueTimer(...)
{
...
    if (NULL == TimerThread)
    {
...
            HANDLE TimerThreadHandle
                        = CreateThread(NULL,                // security descriptor
                                       0,                   // default stack size
                                       TimerThreadStart,        //
                                       &params,
                                       0,
                                       &threadId);
...
            TimerThread = TimerThreadHandle;
   }
...
   QueueUserAPC((PAPCFUNC)InsertNewTimer,TimerThread,(size_t)timerInfo);
...
}

Here we see creating of new timer thread (in the first execution only) and then queuing ThreadpoolMgr::InsertNewTimer [3] APC via QueueUserAPC function. This APC just queues new timerInfo to linked list (I skipped several not-so-important-to-understanding lines):

// Executed as an APC in timer thread
void ThreadpoolMgr::InsertNewTimer(TimerInfo* pArg)
    TimerInfo * timerInfo = pArg;

    if (timerInfo->state & TIMER_DELETE)
    {   // timer was deleted before it could be registered
        DeleteTimer(timerInfo);
        return;
    }

    // set the firing time = current time + due time (note initially firing time = due time)
    DWORD currentTime = GetTickCount();
    if (timerInfo->FiringTime == (ULONG) -1)
    {
        timerInfo->state = TIMER_REGISTERED;
        timerInfo->refCount = 1;
    }
    else
    {
        timerInfo->FiringTime += currentTime;

        timerInfo->state = (TIMER_REGISTERED | TIMER_ACTIVE);
        timerInfo->refCount = 1;

        // insert the timer in the queue
        InsertTailList(&TimerQueue,(&timerInfo->link));
    }
}

New thread will start with ThreadpoolMgr::TimerThreadStart [4], setups itself as timer threadpool thread (i.e. it is a thread supporting ThreadQueue and it is belong to ThreadPool) and starts infinite loop of handling pending timers (also APCs described earlier are being posted to this thread, so they gets executed when this thread calls one of Wait* functions. Most probably it is done this way to eliminate synchronization at all as queue with timer infos accessed only from this thread):

DWORD __stdcall ThreadpoolMgr::TimerThreadStart(LPVOID p)
{
    CreateTimerThreadParams* params = (CreateTimerThreadParams*)p;
    Thread* pThread = SetupThreadPoolThreadNoThrow(TimerMgrThread);
...
    for (;;)
    {
         // moved to its own function since EX_TRY consumes stack
        TimerThreadFire();
    }
}

void ThreadpoolMgr::TimerThreadFire()
{
    EX_TRY {
        DWORD timeout = FireTimers();
        SleepEx(timeout, TRUE);

        // the thread could wake up either because an APC completed or the sleep timeout
        // in both case, we need to sweep the timer queue, firing timers, and readjusting
        // the next firing time
    }
    EX_CATCH {
        if (SwallowUnhandledExceptions())
        {
            // Do nothing to swallow the exception
        }
        else
        {
            EX_RETHROW;
        }
    }
    EX_END_CATCH(SwallowAllExceptions);
}

Actual handling is performed by ThreadpoolMgr::FireTimers [5]:

// executed by the Timer thread
// sweeps through the list of timers, readjusting the firing times, queueing APCs for
// those that have expired, and returns the next firing time interval
DWORD ThreadpoolMgr::FireTimers()
{
    for (LIST_ENTRY* node = (LIST_ENTRY*) TimerQueue.Flink;
         node != &TimerQueue;
        )
    {
        TimerInfo* timerInfo = (TimerInfo*) node;
        node = (LIST_ENTRY*) node->Flink;

        if (TimeExpired(LastTickCount, currentTime, timerInfo->FiringTime))
        {
            if (timerInfo->Period == 0 || timerInfo->Period == (ULONG) -1)
            {
                DeactivateTimer(timerInfo);
            }

            InterlockedIncrement(&timerInfo->refCount);

            QueueUserWorkItem(AsyncTimerCallbackCompletion,
                              timerInfo,
                              QUEUE_ONLY /* TimerInfo take care of deleting*/);

            timerInfo->FiringTime = currentTime+timerInfo->Period;

            if ((timerInfo->Period != 0) && (timerInfo->Period != (ULONG) -1) && (nextFiringInterval > timerInfo->Period))
                nextFiringInterval = timerInfo->Period;
        }
        else
        {
            DWORD firingInterval = TimeInterval(timerInfo->FiringTime,currentTime);
            if (firingInterval < nextFiringInterval)
                nextFiringInterval = firingInterval;
        }
    }

    LastTickCount = currentTime;

    return nextFiringInterval;
}

Well, this code just does what comment before function says: going through timers queue and searching for expired ones. And at last when some of the timers expires ThreadpoolMgr::AsyncTimerCallbackCompletion [6] is executed in the thread pool with QueueUserWorkItem. It essentially just calls callback provided with timer creation so comment seems somewhat misleading in part that states APC (or maybe it is just async procedure call in broad sense who knows?).

P.S. Locations of functions mentioned:
[1] TimerNative::CorCreateTimer – \clr\src\vm\comthreadpool.cpp line 956
[2] ThreadpoolMgr::CreateTimerQueueTimer – \clr\src\vm\win32threadpool.cpp line 3628
[3] ThreadpoolMgr::InsertNewTimer – \clr\src\vm\win32threadpool.cpp line 3816
[4] ThreadpoolMgr::TimerThreadStart – \clr\src\vm\win32threadpool.cpp line 3729
[5] ThreadpoolMgr::FireTimers – \clr\src\vm\win32threadpool.cpp line 3864
[6] ThreadpoolMgr::AsyncTimerCallbackCompletion – \clr\src\vm\win32threadpool.cpp line 3917

What we saw above is just an implementation of legacy Win32 thread pool and timers on top of it in the CLR. So my guess that CLR just tries to be as compatible as possible and not rely on newer native thread pool’s features implemented in Windows Vista. I think it is very likely that newer CLR try to switch to newer API when runs on Vista or higher version. And it might be the reason I didn’t find any timer threads myself when I tested timers on .NET 4.0.

So as a short summary:

  • As for SSCLI20 Hans was totally right. There’s really separate thread for handing APCs and queued timers. Thanks for your insistence, btw. I received a chance to dig something interesting :)
  • On newer systems it still could be implemented without additional threads. I was just mistakenly assuming it was already there when CLR 2.0 was written. For details see CreateThreadpoolTimer, SetThreadpoolTimer and CloseThreadpoolTimer. Or better just read the book mentioned in the second sentence.
About these ads

2 Comments »

  1. I ran into “a device attached to the system is not functioning” error. See http://social.msdn.microsoft.com/Forums/vstudio/en-US/302947e9-c164-4185-92fc-f81b870b9eed/capp-connection-to-db-throws-a-device-attached-to-the-system-is-not-functioning-exception-from?prof=required
    The error is too broad to guess the real cause and I would like to get to the bottom of this where which function throws the error within AddTimerInternal().
    Can you point me to the source codes?

    Comment by chris xiao — 09/08/2013 @ 15:47


RSS feed for comments on this post. TrackBack URI

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

The Silver is the New Black Theme. Blog at WordPress.com.

Follow

Get every new post delivered to your Inbox.

%d bloggers like this: