RE: [boost] Re: Thread lib (good reason for static)

Behalf Of scott Assume use of a technique like the following (think this has been presented as "object shim"?);
T & anastasia() { static T *p = new T; return *p; }
as a means of dealing with global construction issue, i.e. instead of expressions like "anastasia.data_member" the usage is "anastasia().data_member". Also assume that some of these global objects (i.e. shims) deploy mutexes for MT reasons. What happens when there is more that one thread running around making calls to shims? Instantiation of the global itself becomes an MT issue.
That's what the double-checked locking pattern for singletons is for. Regards, Matt Hurd. _______________ Matt Hurd +61.2.8226.5029 hurdm@sig.com Susquehanna _______________ IMPORTANT: The information contained in this email and/or its attachments is confidential. If you are not the intended recipient, please notify the sender immediately by reply and immediately delete this message and all its attachments. Any review, use, reproduction, disclosure or dissemination of this message or any attachment by an unintended recipient is strictly prohibited. Neither this message nor any attachment is intended as or should be construed as an offer, solicitation or recommendation to buy or sell any security or other financial instrument. Neither the sender, his or her employer nor any of their respective affiliates makes any warranties as to the completeness or accuracy of any of the information contained herein or that this message or any of its attachments is free of viruses.

Hurd, Matthew wrote:
Behalf Of scott Assume use of a technique like the following (think this has been presented as "object shim"?);
T & anastasia() { static T *p = new T; return *p; }
as a means of dealing with global construction issue, i.e. instead of expressions like "anastasia.data_member" the usage is "anastasia().data_member". Also assume that some of these global objects (i.e. shims) deploy mutexes for MT reasons. What happens when there is more that one thread running around making calls to shims? Instantiation of the global itself becomes an MT issue.
That's what the double-checked locking pattern for singletons is for.
Regards,
Matt Hurd.
No, the double-checked locking pattern is only good as the subject of talks describing why it's not good for anything. http://www.nwcpp.org/Downloads/2004/DCLP_notes.pdf -- Eric Niebler Boost Consulting www.boost-consulting.com

-----Original Message----- From: boost-bounces@lists.boost.org [mailto:boost-bounces@lists.boost.org]On Behalf Of Eric Niebler Sent: Wednesday, June 09, 2004 11:15 AM To: boost@lists.boost.org Subject: [boost] Re: Thread lib (good reason for static)
[snip]
That's what the double-checked locking pattern for
singletons is for.
Regards,
Matt Hurd.
No, the double-checked locking pattern is only good as the subject of talks describing why it's not good for anything.
Yeah! Of course I have to admit I'd never heard of DCLP before ;-) Read the notes quickly and they are very nicely presented. The actual bug that I created was related to the mutex controlling instantiation. Essentially if there is a multi-access issue around instantiation, how does adding a mutex make it better, i.e. how does the mutex get constructed (safely)? In the notes this is the following line on page 8. I'm assuming "args" is a constructed mutex; Lock L( args); // Acquire mutex or other lock While this is becoming something of a tangent maybe it qualifies as an example of why globals creating threads in ctors is a bear? Cheers, Scott

scott wrote: [...]
The actual bug that I created was related to the mutex controlling instantiation. Essentially if there is a multi-access issue around instantiation, how does adding a mutex make it better, i.e. how does the mutex get constructed (safely)?
See boost.thread once() implementation for windows. I call it "lazy mutex". Note that the use of interlocked there is a bit braindamaged (but it's hard to do better given that present compilers don't understand more generic memory barriers [hoist and sink stuff***]). Here's some "portable" double-checked init in dynamic [not static] context. Just an illustration. < double-checked serialized init > DCSI-TLS: class stuff : private lazy_mutex { // "create/open named mutex" // trick on windows const lazy * m_ptr; thread_specific_ptr<lazy, no_cleanup> m_tsp; public: /* ... */ const lazy & lazy_instance() { const lazy * ptr; if (!(ptr = m_tsp.get())) { lazy_mutex::guard guard(this); if (!m_ptr) m_ptr = new lazy(); m_tsp.set(ptr = m_ptr); } return *ptr; } DCSI-MBR: class stuff : private lazy_mutex { // "create/open named mutex" // trick on windows atomic<const lazy *> m_ptr; public: /* ... */ const lazy & lazy_instance() { const lazy * ptr; if (!(ptr = m_ptr.load(msync::hlb))) { lazy_mutex::guard guard(this); if (!(ptr = m_ptr.load(msync::none))) m_ptr.store(ptr = new lazy(), msync::ssb); } return *ptr; } } DCCI: (lockless double-checked concurrent init) class stuff { atomic<const lazy *> m_ptr; public: /* ... */ const lazy & lazy_instance() { const lazy * ptr; if (!(ptr = m_ptr.load(msync::hlb)) && !m_ptr.attempt_update(0, ptr = new lazy(), msync::ssb)) { delete ptr; ptr = m_ptr.load(msync::hlb); } return *ptr; } } "hlb" stands for "hoist load barrier" and "ssb" stands for "sink store barrier".
In the notes this is the following line on page 8.
Slides aren't bad up to Pg. 22. "Multiprocessors, Cache Coherency, and Memory Barriers" part is somewhat screwed. For example, Pg. 34 and Pg. 35: Keyboard* temp = pInstance; Perform acquire; ... (that notation sucks, BTW) is not really the same (with respect to reordering) as Keyboard* temp = pInstance; Lock L1(args); // acquire ... because the later can be transformed to Lock L1(args); // acquire Keyboard* temp = pInstance; ... While it does stress the point of Pg. 36, the difference is quite significant and it can really hurt you in some other context. Beware. regards, alexander. P.S. < synchronized static locals aside for a moment > typedef aligned_storage< once_call< void > > pthread_once_t; #define PTHREAD_ONCE_INIT ... magic ... extern "C" int pthread_once(pthread_once_t * once_control, void (* init_routine)()) { once_control->object()(init_routine); return 0; } extern "C++" int pthread_once(pthread_once_t * once_control, void (* init_routine)()) { once_control->object()(init_routine); return 0; } ***) < barriers (data dependent stuff aside for a moment) > Full fence means "noop.acquire+release". Acquire operation/access prevents subsequent (in program order) memory accesses from moving "up in time" to before the acquire operation; IOW, it prevents hoisting above acquire operation. Release operation/access prevents prior (in program order) memory accesses from moving "down in time" to after the release operation; IOW, it prevents sinking below release operation. op.acquire is op with "hoist-load+hoist-store" constraints, op.release is op with "sink-load+sink-store" constraints, StoreLoad fence is noop with "sink-store+hoist-load" constraints rdlock() is op with "hoist-load" barrier (just like the first check in DCSI-MBR and DCCI above), and rdunlock() is op with "sink-load" barrier.
participants (4)
-
Alexander Terekhov
-
Eric Niebler
-
Hurd, Matthew
-
scott