Subj : Re: Can C++ local static objects be made thread safe? To : comp.programming.threads From : Gianni Mariani Date : Mon Feb 14 2005 09:45 pm Bruce Visscher wrote: > Okay, I wasn't paying attention: > > >>#define MTSafeStatic( Type, Name, Initializer ) \ >> static __thread Type * x_ ## Name; \ > > > According to David Swartz, this makes x_ ## Name thread local. > > Doesn't that go against the grain of what this thread asks to > accomplish? v_ ## Name is the interesting object, x_ ## Name is just used as a way to both determine if v_ ## Name is initialized and it's address. The reference Name is then seated with the reference to v_ ## Name. The "Double Checked Lock" or DCL is only interesting when it can avoid having to aquire the mutex. By using __thread (or thread specific data) each thread aquires the mutex exactly once which is slightly less efficient than a theoretically optimal DCL, but since that's not practical on modern CPU's, that point is moot. Suffice to say, this is not an issue on any commercially available Pentium, Opteron or Athlon, which kind of covers all CPU's most people care about. (even though, Intel have warned that they're considering making it a requirement to add memory barriers in cases like this. > > Okay, you admited that you were "not sure about the __thread thing". I > think the answer is "no, that doesn't help". > > (I wanted to point this out since in my later posts I made the > assumption that this was going to be helpful. After pondering this I > realized I was confused.) > Let's think about this for a minute. The regular DCL pattern has a problem - i.e. #define BROKENAndNotSoSafeStatic( Type, Name, Initializer ) \ *1* static volatile Type * x_ ## Name; \ \ if ( ! x_ ## Name ) \ { \ Lock l_lock( StaticInitializerMutex );\ \ static Type v_ ## Name Initializer; \ \ *2* x_ ## Name = & v_ ## Name Initializer; \ } \ \ Type & Name = * x_ ## Name; \ // End macro An MP system may re-order the 2 and 1 which happens in different threads and so you need to have a memory barrier that cleans this up as discussed elsewhere in this thread. However, if each thread has it's own x_Name variable, there is no way that it can get re-ordered if you have a conforming compiler because essentially __thread variables are accessed by a single thread. It does not even need to be volatile, because of the same reason. The Lock must perform all the appropriate memory synchronization, so in theory, it's impossible for the re-ordering to happen when __thread is used. #define MTSafeStatic( Type, Name, Initializer ) \ static __thread Type * x_ ## Name; \ \ if ( ! x_ ## Name ) \ { \ Lock l_lock( StaticInitializerMutex );\ \ static Type v_ ## Name Initializer; \ \ x_ ## Name = & v_ ## Name Initializer; \ } \ \ Type & Name = * x_ ## Name; \ // End macro The reason I'm not sure about the __thread directive is that I can't be sure how it is implemented on other compilers. GCC seems to work like I describe here. Each thread has different memory mapped to the same address (don't try to take the address of an __thread variable though). .