My, those threads start up really fast nowadays
Here’s a little puzzleinspired by an actual bug:
// global variable DWORD g_WorkerThreadId; bool IsRunningOnWorkerThread() { return GetCurrentThreadId() == g_WorkerThreadId; } bool LaunchWorkerThread() { HANDLE hThread = CreateThread(nullptr, 0, WorkerThread, nullptr, 0, &g_WorkerThreadId); if (hThread != nullptr) { CloseHandle(hThread); return true; } return false; } DWORD CALLBACK WorkerThread(void *Proc) { // Can this assertion ever fire? assert(IsRunningOnWorkerThread()); 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 runningbefore the call the CreateThread
returns.In that case, the caller hasn’t yet received thehandle or ID of the newly-started thread.The new thread callsIsRunningOnWorkerThread
,which returns false
sinceg_WorkerThreadId
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() { DoSomething(); DoSomethingElse(); DoYetAnotherThing(); } DWORD CALLBACK WorkerThread(void *Proc) { ... DoManyThings(); ... return 0; }
If the new thread started up so quickly that the original threaddoesn’t get a chance to receive the new thread ID and putit intog_WorkerThreadID
,then the DoSomething
functioncalled from the worker thread will accidentally do thingsthe 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 threadset the g_WorkerThreadId
variable,you cover both cases of the race.If the original thread runs faster, then theCreateThread
function will set theg_WorkerThreadId
variable to the ID of the workerthread,and the first line of WorkerThread
will be redundant.On the other hand, if the worker thread runs faster,then the assignment at the beginning ofWorkerThread
sets the thread ID,and the assignment performed bythe CreateThread
function will be redundant.
0 comments