Boost logo

Boost :

From: williamkempf_at_[hidden]
Date: 2001-08-01 09:15:20

--- In boost_at_y..., "Alexander Terekhov" <terekhov_at_d...> wrote:
> > > POSIX uses pthread_once to do this,
> > > but there's no Win32 equivalent. Instead, Win32 programmers are
> > > expected to make use of DllMain to handle this sort of
> > initialization
> > in Win32 pthread_once can be simulated by using something similar
> > the the following:
> >
> > struct OnceInfo {
> > DWORD counter;
> > bool done;
> > };
> >
> > static OnceInfo info = {-1,false};
> >
> > void doOnce(OnceInfo& info,void (*foo)())
> > {
> > if (InterlockedIncrement(&info.counter)==0) {
> > foo();
> > info.done=true;
> > } else while (!info.done) Sleep(0);
> > }
> without initial check of info.done you could end up
> calling foo() multiple times. that is incorrect.
> also, Interlocked stuff does not formally provide
> memory synch guaranties (does IA64 windows version
> have "memory fence" build into it ??) i would really
> try to avoid it.
> another problem is that you misuse scheduler
> to wait for something. this is really dangerous.
> consider the situation when a lower priority thread
> gets preempted in favor of some higher priority thread(s)
> (on uniprocessor with priority scheduling) while it was
> still busy inside your "critical section"; your while loop
> may never end; deadlock.
> consider the following example (just an illustration;
> no error checking, etc):
> int process_private_once_status = 0;
> int __declspec(thread) thread_local_once_status = 0;
> void dcl_once()
> {
> // 1st check
> if ( 0 == thread_local_once_status ) {
> // Create/Open named mutex
> HANDLE hndlMutex = CreateMutex( NULL,
> process_private_once_status ) );

I never thought of using a named mutex approach. Not overly
efficient, but a very nice idea non-the-less. I've been thinking too
much about portable threads to see this one. This makes me wonder
why you use TLS at all here. There seems to be no reason for it.

typedef void (*once_routine)();
typedef bool once_t;
#define BOOST_ONCE_INIT false

void run_once(once_t& once, once_routine func)
   if (!once)
      HANDLE mutex = CreateMutex(NULL, FALSE, "boost_once");
      WaitForSingleObject(mutex, INFINITE);
      if (!once)
         once = true;

Or is the TLS used solely to insure the DCL doesn't suffer from
memory barrier problems? On Win32 this won't be an issue (I honestly
don't know if it will be on IA64 and at this point don't care, since
this is a platform implementation issue and not a concern for
portability). To be honest, I don't understand the issue any way...
shouldn't the mutex lock (WaitForSingleObject() here) invoke a memory
barrier here any way? (Maybe responses to that question should go to
e-mail... I'm not sure others care about this.)

> ps. __declspec(thread):
> "When you use the compiler directive __declspec(thread),
> the data that you define doesn't go into either the
> .data or .bss sections. It ends up in the .tls section,
> which refers to "thread local storage," and is related
> to the TlsAlloc family of Win32 functions. When dealing
> with a .tls section, the memory manager sets up the
> page tables so that whenever a process switches threads,
> a new set of physical memory pages is mapped to the
> .tls section's address space. This permits per-thread
> global variables. In most cases, it is much easier to
> use this mechanism than to allocate memory on a
> per-thread basis and store its pointer in a
> TlsAlloc'ed slot."

__declspec(thread) won't work in DLLs that are dynamically loaded, so
for library code such as Boost.Threads it should not be used. To do
this safely we'd have to use TlsAlloc and call it from within
DllMain, which means that Boost.Threads has to reside in a DLL for
Windows platforms. This is the same ugliness I'll have to contend
with to clean up boost::tss data on Win32 :(.

Bill Kempf

Boost list run by bdawes at, gregod at, cpdaniel at, john at