Boost logo

Boost :

From: Anthony Williams (anthony_w.geo_at_[hidden])
Date: 2005-09-08 08:54:45

Hi all,

I've been thinking a lot about synchronization primitives, especially
around call_once, but also around mutexes, semaphores and conditions.

Ensuring that they are correctly initialized, safely, is a hard problem,
especially if we try and allow them to be used both as objects with static
storage duration, and with non-static storage duration, or as members of other

Firstly, objects with static storage duration must be completely initialized
statically, and not require any dynamic initialization. This is because
dynamic initialization is not thread safe in general, and exposes the object
to potential race conditions on the initialization, unless an alternative
synchronization mechanism is used to ensure thread safety. The POSIX spec
gives a good example, relating to pthread_mutex_init:

  "Without static initialization, a self-initializing routine foo() might look
  as follows:

  static pthread_once_t foo_once = PTHREAD_ONCE_INIT;
  static pthread_mutex_t foo_mutex;

  void foo_init()
      pthread_mutex_init(&foo_mutex, NULL);

  void foo()
      pthread_once(&foo_once, foo_init);
     /* Do work. */

  With static initialization, the same routine could be coded as follows:

  static pthread_mutex_t foo_mutex = PTHREAD_MUTEX_INITIALIZER;

  void foo()
     /* Do work. */

This requires that the objects are PODs or aggregates of PODs, so that no
constructors are run (which would be dynamic initialization); if
zero-initialization is not satisfactory, then a static initializer akin to

Secondly, objects of non-static storage duration cannot assume anything about
the underlying memory, so they must be initialized dynamically. If the same
class type is to be used for both purposes, this cannot be with a constructor,
so it must be with an initializer. Whether this is the same as the static
initializer is another question.

Thirdly, these requirements apply recursively to any class which contains a
synchronization primitive as a member: if a class has a constructor, then it
is not safe for static initialization. If it does not have a constructor, then
it requires an initializer to ensure safe use as an object with non-static
storage duration. For such a class with a constructor, the synchronization
primitive member must therefore be initialized in the member-initialization
list. This implies that there must be a suitable initializer provided, as an
aggregate initializer which might suffice for standalone variables cannot be
used for member initialization.

This implies to me that we probably therefore want two versions of each
synchronization primitive. The first is a POD, which is therefore safe for
static initialization. The second is non-POD, and has an appropriate default
constructor, which is therefore safe for non-static objects. Any object which
contains a synchronization primitive as a member must therefore choose which
version to use, and therefore limit its own use as a consequence.

Section 3.6.2 of the C++ standard allows implementers leeway to statically
initialize objects with the result of dynamic initialization if such
initialization yields a constant value, and doesn't affect any other
object. On specific compilers and platforms where we can be sure that the
implementors have taken advantage of this leeway, we can possibly relax some
of these guidelines.

Also, some compilers and platforms guarantee that dynamic initialization of
objects with static storage duration is thread-safe. On such platforms, it is
therefore safe for synchronization primitives to be non-POD objects with
appropriate constructors, under all circumstances.


Anthony Williams
Software Developer
Just Software Solutions Ltd

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