Boost logo

Boost :

From: Alexander Terekhov (terekhov_at_[hidden])
Date: 2001-08-01 04:53:34


> > 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 to
> 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,
                                    FALSE,
                                    ONCE_MUTEX_NAME(
process_private_once_status ) );

    // Synchronize r/w access to process_private_once_status variable
    WaitForSingleObject( hndlMutex,INFINITE );

    // 2nd check
    if ( 0 == process_private_once_status ) {

      // once
      myfunc();

      // once done
      process_private_once_status = 1;

    }

    // Mutex cleanup
    ReleaseMutex( hndlMutex );
    CloseHandle( hndlMutex );

    // Done for this thread
    thread_local_once_status = 1;

  }

}

it does not use poorly defined (with respect to memory synch)
Interlocked stuff; it follows PORTABLE memory synchronization
rules using a mutex which is also used for proper "waiting".

regards,
alexander.

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."


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk