
Call to local static object constructor not thread-safe
says...
Quote:
> >In MSVC++ 6.0 SP3, the code generated to call the constructor of a local
> >static object doesn't guard against race conditions for the construction
> >flag read and write operations or the constructor call.
<snip>
> >For example, here's the assembly language generated for construction of a
> >local static object:
> >0050DE49 xor eax,eax
> >0050DE50 and eax,1
> >0050DE53 test eax,eax
> >0050DE55 jne NGInit+5Ch (0050de87)
> >(005e3ae8)]
> >0050DE5D or cl,1
> >(005e3ae8)],cl
> >0050DE66 push offset InitNetworking (0050e10f)
> >0050DE6B push offset string "ProcL"+0E04h (005d1520)
> >0050DE70 mov ecx,offset oneTime (005e3a28)
> >0050DE75 call CallOnce::CallOnce (0052e720)
> >0050DE7A push offset $E35 (0050e11f)
> >0050DE7F call _atexit (00532452)
> >0050DE84 add esp,4
> >The flag isn't protected by a synchronization primitive or use of a Win32
> >function like InterlockedCompareExchange (the latter requiring a change
> >to a 32-bit flag). Thus, one thread could read the flag and then run out
> >of its time slice, and a second thread could read the same flag and run
> >the constructor, and by the time the first thread continued, it would be
> >working with a stale value but would go ahead and run the constructor a
> >second time.
<snip>
> You can use the so-called Double-Checked Locking Pattern to get
> thread-safe initialization in this context. See this message for more:
> Newsgroups: microsoft.public.vc.language,microsoft.public.vc.mfc
> Subject: Re: "Call Once" function? How to implement?
> Date: Mon, 13 Mar 2000 23:18:54 -0600
Thanks for the reply. The pattern looks like something I was doing
before I tried to encapsulate it into a class. I'm going to try
something like the following:
// doOnceOnly
//
// Arguments:
// dwDone - external flag that is initialized to zero
// before main is called. Holds zero if initFunction never
called.
// Holds nonzero if initFunction already called.
// initFunction - function to call if OK to initialize
//
// Returns TRUE if the caller did the initialization, FALSE if not.
//
#define DOO_DONE 1
#define DOO_NOT_DONE 0
BOOL doOnceOnly(LPLONG plDone, void ((*initFunction)(void)))
{
BOOL bCalledInitFunction = FALSE;
// Do the cheap check first
if (!*plDone)
{
// Do the expensive check
LONG lInitStatus;
// Casts used here because winbase.h declares ICE
// as using PVOIDs, contrary to the
// man page in the Platform SDK in MSDN library,
// which uses LONGs.
lInitStatus = (LONG) InterlockedCompareExchange(
(void**)plDone,
(void*)DOO_DONE,
(void*)DOO_NOT_DONE);
if (DOO_NOT_DONE == lInitStatus)
{
(*initFunction)();
bCalledInitFunction = TRUE;
}
}
return bCalledInitFunction;
Quote:
}
> Doug Harrison
> Visual C++ MVP
--
.:O:.:O:.:O:.:O:.:O:.:O:.:O:.:O:.:O:.:O:
Bob McDonald
Remove spam block when replying directly.
I'll cancel this account when I receive too much spam,
so don't expect this address to remain valid long-term.
.:O:.:O:.:O:.:O:.:O:.:O:.:O:.:O:.:O:.:O: