My, those threads start up really fast nowadays

Raymond Chen


Here’s a little puzzle
inspired by an actual bug:

// global variable
DWORD g_WorkerThreadId;
bool IsRunningOnWorkerThread()
  return GetCurrentThreadId() == g_WorkerThreadId;
bool LaunchWorkerThread()
 HANDLE hThread = CreateThread(nullptr, 0,
                               nullptr, 0,
 if (hThread != nullptr) {
   return true;
 return false;
DWORD CALLBACK WorkerThread(void *Proc)
  // Can this assertion ever fire?
  return 0;

Can the assertion at the start of WorkerThread
ever fire?

Naturally, the answer is Yes,
otherwise it wouldn’t be a very interesting article.

The assertion can fire if the worker thread starts running
before the call the Create­Thread returns.
In that case, the caller hasn’t yet received the
handle or ID of the newly-started thread.
The new thread calls
which returns false since
g_Worker­Thread­Id hasn’t been initialized yet.

The actual bug was something along the lines of this:

void DoSomething()
  if (IsRunningOnWorkerThread()) {
     .. do it one way ..
  } else {
     .. do it the other way ..
void DoManyThings()
DWORD CALLBACK WorkerThread(void *Proc)
  return 0;

If the new thread started up so quickly that the original thread
doesn’t get a chance to receive the new thread ID and put
it into
then the Do­Something function
called from the worker thread will accidentally do things
the not-on-the-worker-thread way,
and then things start go go awry.

One way to address is is to add suspenders to your belt:

DWORD CALLBACK WorkerThread(void *Proc)
  g_WorkerThreadId = GetCurrentThreadId();

By having both the original thread and the created thread
set the g_WorkerThreadId variable,
you cover both cases of the race.
If the original thread runs faster, then the
CreateThread function will set the
g_WorkerThreadId variable to the ID of the worker
and the first line of Worker­Thread
will be redundant.
On the other hand, if the worker thread runs faster,
then the assignment at the beginning of
Worker­Thread sets the thread ID,
and the assignment performed by
the CreateThread function will be redundant.

Raymond Chen
Raymond Chen

Follow Raymond   


Comments are closed.