Boost logo

Boost :

From: William E. Kempf (williamkempf_at_[hidden])
Date: 2002-08-08 14:56:31

----- Original Message -----
From: "Anthony Williams" <anthwil_at_[hidden]>
> > > Now it doesn't matter whether the mutex is statically or dynamically
> > > initialized --- static objects are initialized to all zero
> > before their
> > > dynamic initialization, and reinitializing the flag to zero
> > does no harm,
> > as
> > > CreateMutex will always return the same value.
> >
> > Just to be sure we're all using the same language here, when
> > I say "static
> > initialization" I mean the definition stated in 3.6.2/1.
> > This requires a
> > POD type. The code you posted isn't POD and can't be statically
> > initialized. In particular this means the call to your
> > constructor may not
> > have occurred when a thread started before main attempts to
> > call lock(), and
> > thus the lazyInit() will likey not get called either (though
> > it's undefined
> > behavior).
> I was referring to that paragraph. "The storage for objects with static
> storage duration shall be zero-initialized before any other initialization
> takes place" (first sentence) therefore all the members of a static
> of my (non-POD) mutex class are initialized to zero at startup. If the
> compiler takes the leeway of 3.6.2p2 to do the dynamic initialization
> statically (since the constructor only initializes flag to zero, it can do
> this) all is --- flag is zero on startup, and never re-zeroed, just set to
> when lazyInit is called.

I'm still not 100% sure that zero initialization is good enough. The
constructor is still called dynamically, which means that the order of the
call can still step on you here.

main thread starts
some translation unit start thread 2
thread 2 locks the static mutex, causing lazy init to occur
main thread calls the constructor, which zeros out the already initialized

You could still make use of this idea by having the constructor call
layzInit instead of just setting the flag to 0, I think. The trouble is, at
the high level in which I'm working I can't do this in an efficient manner
on all platforms. This idea won't work on POSIX with out using a mutex for
synchronizing the call to lazyInit, which will add lock/unlock overhead to
every function call. In other words, a call to lock this static mutex will
first lock a global static mutex (with probable contention), check the init
flag, actually init the mutex if needed (setting the flag), unlock the
global static mutex, and then lock the associated mutex.

What sounds promising is the second sentence which says "Zeroinitialization
and initialization with a constant expression are collectively called static
initialization; all other initialization is dynamic initialization." Taken
by itself it would seem to indicate that the following should be result in
static initialization (which is what we need):

class static_mutex
   typedef /* implementation defined */ static_initializer_type;
   static_mutex(const static_initializer_type& init) { //... }

   // ...

static static_mutex = BOOST_MUTEX_INIT;

But I don't see how this could work since the constructor could use the data
in the static expression to dynamically initialize some of its members. I
can't find any clarification on this issue in the document, however, so I'd
love to have some language lawyers speak up here.

> If the compiler doesn't take this leeway, and performs explicit dynamic
> initialization, then we may indeed enter the territory of undefined
> behaviour if any thread calls lock before the constructor is completed.

Yes, I guess you saw the same problem I did, though I think we came to
different conclusions. You want to assume the compiler won't do this, while
I want to assume it will, since it's allowed (even if sub-optimal).

> However, in practice, we are targetting a specific platform, and may be
> to get guarantees from the compilers about whether or not such an object
> would be dynamically initialized, and the consequences in such a case.

We're targeting a platform, not a compiler. We can't make any assumptions
about what all available compilers (now and in the future) for this platform
will do here.

> Maybe trying to avoid the call to CreateMutex when it is already created
> a lost cause.

If we were trying to simply allow boost::mutex to be declared statically and
work in both static and dynamic usage scenarios then I'd agree. But with
seperate types gauranteed to work only for specific construction types I
don't think you'd have to. But the problem is still that this is a very
suboptimal solution (again, unless someone can provide some other key piece
to the puzzle that I'm missing).

> > > For POSIX, we can use pthread_once to lazy-init the mutex.
> >
> > Which requires a statically initialized pthread_once_t, so
> > again, the mutex
> > type has to be POD. (At which point you'd be better off
> > using a statically
> > initialized mutex then a call to pthread_once.)
> Yes, I had forgotten that. However, you can use pthread_once within the
> library to initialize some global data, and then lazy init the mutex ---
> my reply to Peter Dimov.

I've looked at it, and ignoring a couple of bugs that could be fixed the
problem is that the implementation is slow. Since this is going to be
called with every lock, the overhead is awfully high.

> [snipped constraints on POD mutex classes]
> I agree that we want to avoid a POD mutex class if possible. I am trying
> find a way.

OK. That's a good thing. But I don't think we're close to finding a way

> However, unless we can guarantee that a non-POD with a simple constructor
> statically initialized as per the leeway of 8.6.2p2, we leave ourselves
> exposed to the possibility of undefined behaviour if any of the member
> functions are called before the constructor has completed.

Killer problem, I think.

> In practice, I can't imagine a compiler _not_ doing static initialization
> for a simple inline constructor that assigned constants to the members.

I can, since the standard allows it.

Bill Kempf

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