Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

local statics and TLS objects

10 views
Skip to first unread message

Alexander Terekhov

unread,
Oct 10, 2002, 10:42:42 AM10/10/02
to

< was: Re: volatile -- what does it mean in relation to member functions?
comp.lang.c++.moderated >

James Kanze wrote:
[...]
> > > Posix says nothing about C++. A C++ compiler can do what it wants,
> > > and not affect Posix compliance. Still, I can't imagine a C++
> > > compiler not extending the guarantees of C to cover its operations.
> > > (It does mean, however, that a fair number of C++ compilers do not
> > > generate thread safe code, particularly when it comes to
> > > initializing local statics.
[...]
> > Been there done that. That static local initialization doesn't work in
> > a lot of compilers is very unfortunate. Since the C++ standard clearly
> > says that it should be initialized on the first call there's really no
> > excuse. It doesn't say under which circumstances so this means this
> > should hold under *any* circumstances and if your compiler even has a
> > switch to notify it about multithreaded code...
>
> I tend to agree with you here. ....

I disagree. And, BTW, I'd be quite happy to be able to write
something like:

ThreadLocalLazySingleton&
ThreadLocalLazySingleton::get() // static
{
// Kinda "static" but might throw std::bad_alloc even
// if used with throw() [throw nothing] constructor[1].
static(thread_private) ThreadLocalLazySingleton thing(/**/);
return thing;
}

and

GlobalLazyImmutableSingleton const&
GlobalLazyImmutableSingleton::get() // static
{
// Thread shared/safe version of "classic" local static. See
// the link below (i.e. it's synchronized using __cxa__guards).
static(thread_shared) const GlobalLazyImmutableSingleton thing(/**/);
return thing;
}

leaving

ThreadsUnawareGlobalLazySingleton&
ThreadsUnawareGlobalLazySingleton::get() // static
{
// This thing is "single threaded" -- NOT meant to use
// http://www.codesourcery.com/cxx-abi/abi.html#once-ctor
// "sync-guards"/synchronization-by-default
static ThreadsUnawareGlobalLazySingleton thing(/**/);
return thing;
}

alone (with respect to threading)... for things like:

// Module

static ReadWriteLock s_lock;

LazySingletonWithReadWriteAccess*
LazySingletonWithReadWriteAccess::s_inst = 0; // set in c-tor

auto_unlock_ptr< LazySingletonWithReadWriteAccess >
LazySingletonWithReadWriteAccess::getForWrite() // static
{
ReadWriteLock::WriteGuard guard( s_lock );
static LazySingletonWithReadWriteAccess thing(/**/);
return auto_unlock_ptr<
LazySingletonWithReadWriteAccess >(&thing,guard);
}

auto_unlock_ptr< const LazySingletonWithReadWriteAccess >
LazySingletonWithReadWriteAccess::getForRead() // static
{
{
ReadWriteLock::ReadGuard guard( s_lock );
if ( s_inst )
return auto_unlock_ptr<
const LazySingletonWithReadWriteAccess >(s_inst,guard);
}
return getForWrite().degradeToReadOnlyAccess();
}

Or am I just missing and/or misunderstanding something?

How is this currently done by the existing C++ implementations
that already provide TLS (supporting constructors/destructors)?

regards,
alexander.

[1] So, basically:

extern void operation( Something& );
extern(thread_private) Something thing = blah_blah;

/* ... */

operation( thing );

would actually mean something along the lines of:

extern void operation( Something& );
extern Something& __thing() // throw( std::bad_alloc, ... )
{
static(thread_private) Something thing = blah_blah;
return thing;
}

operation( __thing() );

Alexander Terekhov

unread,
Oct 11, 2002, 8:38:02 AM10/11/02
to

Alexander Terekhov wrote:
>

Well, "thread-private" was a bit confusing. I should have used something
like "thread-local". Also... Momchil, see yet another "fixed" DCL below.

< was: Re: Talking about volatile and threads synchronization... >

Momchil Velikov wrote:
[...]
> So, the DCL case will looke like this, right ?
>
> void foo ()
> {
> static int initialized;
> int i;
>
> i = initialized;
>
> /* Read memory barrier, so the above read of ``initialized''
> is issued before reads of memory, initialized by ``do_stuff''.
> We may not need this on some architectures. Which ones ? */
> rmb ();
> if (i == 0)
> {
> lock ();
> if (initialized == 0)
> {
> do_stuff ();
>
> /* Write memory barrier - prevent CPU from reordering
> writes across this point. Necessary (but not
> necessarily sufficient) to ensure the reader sees
> the new values written in ``do_stuff''
> whenever it sees ``initialized'' being nonzero. */
> wmb ();
> initialized = 1;
> }
> unlock ();
> }
>
> do_more_stuff ();
> }

Nah, given the following {rather intereseting(*)}
"ISO/EIC xxxx:xxxx edits": ;-)

http://gcc.gnu.org/onlinedocs/gcc/C99-Thread-Local-Edits.html#C99%20Thread-Local%20Edits
(ISO/IEC 9899:1999 Edits for Thread-Local Storage)

http://gcc.gnu.org/onlinedocs/gcc/C--98-Thread-Local-Edits.html#C++98%20Thread-Local%20Edits
(ISO/IEC 14882:1998 Edits for Thread-Local Storage)

So, the DCL case will look like this:

void foo ()
{
static int initializedGlobal;
static __thread int initializedThreadLocal;

if (initializedThreadLocal == 0)
{
lock ();
if (initializedGlobal == 0)
{
do_stuff ();

initializedGlobal = 1;
}
unlock ();

initializedThreadLocal = 1;
}

do_more_stuff ();
}

regards,
alexander.

(*) Does DEC's/Compaq's/NewHP's TLS have the same "semantics" with
respect to non-lazy/eager "static" TLS inits -- NOT supporting
dynamic initialization/C++ c-tors? What about dlopen(), then?

0 new messages