This is the mail archive of the
gcc@gcc.gnu.org
mailing list for the GCC project.
Re: private static object construction bug?
- From: Zack Weinberg <zack at codesourcery dot com>
- To: Mark Mitchell <mark at codesourcery dot com>
- Cc: Joe Buck <Joe dot Buck at synopsys dot com>,"joseph dot buehler at spirentcom dot com" <joseph dot buehler at spirentcom dot com>,"gcc at gcc dot gnu dot org" <gcc at gcc dot gnu dot org>
- Date: Mon, 5 Aug 2002 15:50:12 -0700
- Subject: Re: private static object construction bug?
- References: <200208051648.JAA22947@atrus.synopsys.com> <43960000.1028581098@warlock.codesourcery.com>
On Mon, Aug 05, 2002 at 01:58:18PM -0700, Mark Mitchell wrote:
> if (!guard) {
> __cxa_guard_acquire (&guard); // I forget the exact name.
> // Construct variable.
> __cxa_guard_release (&guard):
> }
This sequence has two different races.
1: Thread 1 tests guard, finds it zero, and calls __cxa_guard_acquire.
Before __cxa_guard_acquire can modify guard, Thread 2 tests guard,
finds it still zero, and enters the block. The object will be
constructed twice.
2: Thread 1 tests guard, finds it zero, calls __cxa_guard_acquire
which sets guard to a nonzero value. Before the object can be
constructed, Thread 2 tests guard, finds it nonzero, and proceeds to
use the uninitialized object.
To cure these races, we need to generate code like this instead:
if (guard) {
if (__cxa_guard_acquire (&guard)) {
// construct variable.
__cxa_guard_release (&guard)
}
}
I inverted the outer test because 'guard' is now a tristate variable;
for speed, we still want the outer test to be against zero, so zero
needs to be the 'initialized' state (which unfortunately means the
guard can't go in BSS). The races now look like this:
Thread 1 tests guard, finds it nonzero, and calls __cxa_guard_acquire.
Thread 2 tests guard, finds it nonzero, and calls __cxa_guard_acquire.
__cxa_guard_acquire changes guard from (say) -1 to 1 with an atomic
operation. One of the two threads will win the race to do this. That
thread returns a nonzero value from __cxa_guard_acquire, and proceeds
to construct the object. The other thread blocks until
__cxa_guard_release is called, at which point it returns zero from
__cxa_guard_acquire and skips the constructor. __cxa_guard_release
also changes guard from (say) 1 to 0 with an atomic operation; future
threads will skip the entire block.
It is likely that the ABI standard does specify something like this,
but I wanted to make sure people understood that thread-safe object
construction is not as simple as you made it look. (The guard
variable can't be a simple integer, either; it needs to be a structure
containing an integer and a mutex.)
zw