Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]
Groups > comp.lang.c++ > #4373 > unrolled thread
| Started by | jl_post@hotmail.com |
|---|---|
| First post | 2011-04-26 09:58 -0700 |
| Last post | 2011-05-01 14:47 -0700 |
| Articles | 13 on this page of 33 — 9 participants |
Back to article view | Back to comp.lang.c++
Please disprove this Double-Checked Locking "fix" jl_post@hotmail.com - 2011-04-26 09:58 -0700
Re: Please disprove this Double-Checked Locking "fix" Leigh Johnston <leigh@i42.co.uk> - 2011-04-26 18:17 +0100
Re: Please disprove this Double-Checked Locking "fix" Pete Becker <pete@versatilecoding.com> - 2011-04-26 13:50 -0400
Re: Please disprove this Double-Checked Locking "fix" Scott Meyers <NeverRead@aristeia.com> - 2011-05-01 17:14 -0700
Re: Please disprove this Double-Checked Locking "fix" Joshua Maurice <joshuamaurice@gmail.com> - 2011-05-02 15:59 -0700
Re: Please disprove this Double-Checked Locking "fix" Pete Becker <pete@versatilecoding.com> - 2011-05-03 08:39 -0400
Re: Please disprove this Double-Checked Locking "fix" Joshua Maurice <joshuamaurice@gmail.com> - 2011-04-26 11:16 -0700
Re: Please disprove this Double-Checked Locking "fix" Joshua Maurice <joshuamaurice@gmail.com> - 2011-04-26 11:19 -0700
Re: Please disprove this Double-Checked Locking "fix" Pete Becker <pete@versatilecoding.com> - 2011-04-26 14:30 -0400
Re: Please disprove this Double-Checked Locking "fix" Joshua Maurice <joshuamaurice@gmail.com> - 2011-04-26 11:50 -0700
Re: Please disprove this Double-Checked Locking "fix" Joshua Maurice <joshuamaurice@gmail.com> - 2011-05-11 19:55 -0700
Re: Please disprove this Double-Checked Locking "fix" Gerhard Fiedler <gelists@gmail.com> - 2011-05-13 19:56 -0300
Re: Please disprove this Double-Checked Locking "fix" Joshua Maurice <joshuamaurice@gmail.com> - 2011-05-13 16:59 -0700
Re: Please disprove this Double-Checked Locking "fix" Gerhard Fiedler <gelists@gmail.com> - 2011-05-18 18:12 -0300
Re: Please disprove this Double-Checked Locking "fix" Joshua Maurice <joshuamaurice@gmail.com> - 2011-05-18 14:53 -0700
Re: Please disprove this Double-Checked Locking "fix" Gerhard Fiedler <gelists@gmail.com> - 2011-05-19 13:46 -0300
Re: Please disprove this Double-Checked Locking "fix" Joshua Maurice <joshuamaurice@gmail.com> - 2011-05-19 15:30 -0700
Re: Please disprove this Double-Checked Locking "fix" Gerhard Fiedler <gelists@gmail.com> - 2011-05-21 11:55 -0300
Re: Please disprove this Double-Checked Locking "fix" Joshua Maurice <joshuamaurice@gmail.com> - 2011-05-22 01:08 -0700
Re: Please disprove this Double-Checked Locking "fix" James Kanze <james.kanze@gmail.com> - 2011-04-30 15:54 -0700
Re: Please disprove this Double-Checked Locking "fix" Leigh Johnston <leigh@i42.co.uk> - 2011-05-01 21:49 +0100
Re: Please disprove this Double-Checked Locking "fix" Pavel <pauldontspamtolk@removeyourself.dontspam.yahoo> - 2011-05-01 17:26 -0400
Re: Please disprove this Double-Checked Locking "fix" Leigh Johnston <leigh@i42.co.uk> - 2011-05-01 22:44 +0100
Re: Please disprove this Double-Checked Locking "fix" Leigh Johnston <leigh@i42.co.uk> - 2011-05-02 01:01 +0100
Re: Please disprove this Double-Checked Locking "fix" Pavel <pauldontspamtolk@removeyourself.dontspam.yahoo> - 2011-05-01 22:04 -0400
Re: Please disprove this Double-Checked Locking "fix" "Chris M. Thomasson" <cristom@charter.net> - 2011-05-04 11:49 -0700
Re: Please disprove this Double-Checked Locking "fix" Pavel <pauldontspamtolk@removeyourself.dontspam.yahoo> - 2011-05-06 00:16 -0400
Re: Please disprove this Double-Checked Locking "fix" Joshua Maurice <joshuamaurice@gmail.com> - 2011-05-02 15:43 -0700
Re: Please disprove this Double-Checked Locking "fix" James Kanze <james.kanze@gmail.com> - 2011-05-01 14:53 -0700
Re: Please disprove this Double-Checked Locking "fix" Pavel <pauldontspamtolk@removeyourself.dontspam.yahoo> - 2011-05-01 19:23 -0400
Re: Please disprove this Double-Checked Locking "fix" James Kanze <james.kanze@gmail.com> - 2011-05-02 09:02 -0700
Re: Please disprove this Double-Checked Locking "fix" Pavel <pauldontspamtolk@removeyourself.dontspam.yahoo> - 2011-05-05 23:46 -0400
Re: Please disprove this Double-Checked Locking "fix" James Kanze <james.kanze@gmail.com> - 2011-05-01 14:47 -0700
Page 2 of 2 — ← Prev page 1 [2]
| From | Leigh Johnston <leigh@i42.co.uk> |
|---|---|
| Date | 2011-05-01 21:49 +0100 |
| Message-ID | <bOudnchWMorRWiDQnZ2dnUVZ8nudnZ2d@giganews.com> |
| In reply to | #4534 |
On 30/04/2011 23:54, James Kanze wrote:
> On Apr 26, 5:58 pm, jl_p...@hotmail.com wrote:
>
>> Recently I've been reading up on "Double-Checked Locking" in
>> C++ and how it's often implemented imperfectly.
>
> You mean, how it is impossible to implement in standard C++
> (03).
>
>> The Article "C++ and the Perils of Double-Checked Locking" by
>> Scott Meyers and Andrei Alexandrescu
>> (http://www.aristeia.com/Papers/DDJ_Jul_Aug_2004_revised.pdf)
>> provides a good overview of how it's usually done and why it's
>> often inadequate.
>
> The standard solution for implementing a singleton works just
> fine:
>
> Singleton* Singleton::instance()
> {
> ScopedLock lock(mutex);
> if ( pInstance == NULL )
> pInstance = new Singleton;
> return pInstance;
> }
>
How does this prevent CPU reordering of stores to the pInstance pointer
and the object pInstance points to?
/Leigh
[toc] | [prev] | [next] | [standalone]
| From | Pavel <pauldontspamtolk@removeyourself.dontspam.yahoo> |
|---|---|
| Date | 2011-05-01 17:26 -0400 |
| Message-ID | <4dbdd00e$0$15955$c3e8da3$92d0a893@news.astraweb.com> |
| In reply to | #4558 |
Leigh Johnston wrote:
> On 30/04/2011 23:54, James Kanze wrote:
>> On Apr 26, 5:58 pm, jl_p...@hotmail.com wrote:
>>
>>> Recently I've been reading up on "Double-Checked Locking" in
>>> C++ and how it's often implemented imperfectly.
>>
>> You mean, how it is impossible to implement in standard C++
>> (03).
>>
>>> The Article "C++ and the Perils of Double-Checked Locking" by
>>> Scott Meyers and Andrei Alexandrescu
>>> (http://www.aristeia.com/Papers/DDJ_Jul_Aug_2004_revised.pdf)
>>> provides a good overview of how it's usually done and why it's
>>> often inadequate.
>>
>> The standard solution for implementing a singleton works just
>> fine:
>>
>> Singleton* Singleton::instance()
>> {
>> ScopedLock lock(mutex);
>> if ( pInstance == NULL )
>> pInstance = new Singleton;
>> return pInstance;
>> }
>>
>
> How does this prevent CPU reordering of stores to the pInstance pointer
> and the object pInstance points to?
Any system-dependent mutex has to behave like a double-side memory barrier (it's
a follow-up from the mutual exclusion requirement).
This does not prevent from unnecessary overhead though (grabbing a mutex in a
highly contentious case can be expensive and it is unnecessary when all
contenders want is to read a pointer rather than change it -- which is the case
after the Singleton has been initialized).
>
> /Leigh
-Pavel
[toc] | [prev] | [next] | [standalone]
| From | Leigh Johnston <leigh@i42.co.uk> |
|---|---|
| Date | 2011-05-01 22:44 +0100 |
| Message-ID | <XOWdnUdm9onISSDQnZ2dnUVZ8q6dnZ2d@giganews.com> |
| In reply to | #4559 |
On 01/05/2011 22:26, Pavel wrote:
> Leigh Johnston wrote:
>> On 30/04/2011 23:54, James Kanze wrote:
>>> On Apr 26, 5:58 pm, jl_p...@hotmail.com wrote:
>>>
>>>> Recently I've been reading up on "Double-Checked Locking" in
>>>> C++ and how it's often implemented imperfectly.
>>>
>>> You mean, how it is impossible to implement in standard C++
>>> (03).
>>>
>>>> The Article "C++ and the Perils of Double-Checked Locking" by
>>>> Scott Meyers and Andrei Alexandrescu
>>>> (http://www.aristeia.com/Papers/DDJ_Jul_Aug_2004_revised.pdf)
>>>> provides a good overview of how it's usually done and why it's
>>>> often inadequate.
>>>
>>> The standard solution for implementing a singleton works just
>>> fine:
>>>
>>> Singleton* Singleton::instance()
>>> {
>>> ScopedLock lock(mutex);
>>> if ( pInstance == NULL )
>>> pInstance = new Singleton;
>>> return pInstance;
>>> }
>>>
>>
>> How does this prevent CPU reordering of stores to the pInstance pointer
>> and the object pInstance points to?
> Any system-dependent mutex has to behave like a double-side memory
> barrier (it's a follow-up from the mutual exclusion requirement).
I didn't realize this was guaranteed for all systems...
>
> This does not prevent from unnecessary overhead though (grabbing a mutex
> in a highly contentious case can be expensive and it is unnecessary when
> all contenders want is to read a pointer rather than change it -- which
> is the case after the Singleton has been initialized).
Indeed which is why we want DCLP.
/Leigh
[toc] | [prev] | [next] | [standalone]
| From | Leigh Johnston <leigh@i42.co.uk> |
|---|---|
| Date | 2011-05-02 01:01 +0100 |
| Message-ID | <aLSdnS3SA-XeaSDQnZ2dnUVZ8vKdnZ2d@giganews.com> |
| In reply to | #4561 |
On 01/05/2011 22:44, Leigh Johnston wrote:
> On 01/05/2011 22:26, Pavel wrote:
>> Leigh Johnston wrote:
>>> On 30/04/2011 23:54, James Kanze wrote:
>>>> On Apr 26, 5:58 pm, jl_p...@hotmail.com wrote:
>>>>
>>>>> Recently I've been reading up on "Double-Checked Locking" in
>>>>> C++ and how it's often implemented imperfectly.
>>>>
>>>> You mean, how it is impossible to implement in standard C++
>>>> (03).
>>>>
>>>>> The Article "C++ and the Perils of Double-Checked Locking" by
>>>>> Scott Meyers and Andrei Alexandrescu
>>>>> (http://www.aristeia.com/Papers/DDJ_Jul_Aug_2004_revised.pdf)
>>>>> provides a good overview of how it's usually done and why it's
>>>>> often inadequate.
>>>>
>>>> The standard solution for implementing a singleton works just
>>>> fine:
>>>>
>>>> Singleton* Singleton::instance()
>>>> {
>>>> ScopedLock lock(mutex);
>>>> if ( pInstance == NULL )
>>>> pInstance = new Singleton;
>>>> return pInstance;
>>>> }
>>>>
>>>
>>> How does this prevent CPU reordering of stores to the pInstance pointer
>>> and the object pInstance points to?
>> Any system-dependent mutex has to behave like a double-side memory
>> barrier (it's a follow-up from the mutual exclusion requirement).
>
> I didn't realize this was guaranteed for all systems...
>
The ellipsis was an indication that I find your claim that all mutex
implementations include memory barriers slightly dubious. :) From Wikipedia:
"These primitives (mutexes) are *usually* implemented with the memory
barriers required to provide the expected memory visibility semantics.
In such environments explicit use of memory barriers is not *generally*
necessary."
I guess a mutex implementation that didn't include memory barriers on a
system where memory barriers are needed would not be very useful.
/Leigh
[toc] | [prev] | [next] | [standalone]
| From | Pavel <pauldontspamtolk@removeyourself.dontspam.yahoo> |
|---|---|
| Date | 2011-05-01 22:04 -0400 |
| Message-ID | <4dbe111c$0$10604$c3e8da3$f017e9df@news.astraweb.com> |
| In reply to | #4565 |
Leigh Johnston wrote:
> On 01/05/2011 22:44, Leigh Johnston wrote:
>> On 01/05/2011 22:26, Pavel wrote:
>>> Leigh Johnston wrote:
>>>> On 30/04/2011 23:54, James Kanze wrote:
>>>>> On Apr 26, 5:58 pm, jl_p...@hotmail.com wrote:
>>>>>
>>>>>> Recently I've been reading up on "Double-Checked Locking" in
>>>>>> C++ and how it's often implemented imperfectly.
>>>>>
>>>>> You mean, how it is impossible to implement in standard C++
>>>>> (03).
>>>>>
>>>>>> The Article "C++ and the Perils of Double-Checked Locking" by
>>>>>> Scott Meyers and Andrei Alexandrescu
>>>>>> (http://www.aristeia.com/Papers/DDJ_Jul_Aug_2004_revised.pdf)
>>>>>> provides a good overview of how it's usually done and why it's
>>>>>> often inadequate.
>>>>>
>>>>> The standard solution for implementing a singleton works just
>>>>> fine:
>>>>>
>>>>> Singleton* Singleton::instance()
>>>>> {
>>>>> ScopedLock lock(mutex);
>>>>> if ( pInstance == NULL )
>>>>> pInstance = new Singleton;
>>>>> return pInstance;
>>>>> }
>>>>>
>>>>
>>>> How does this prevent CPU reordering of stores to the pInstance pointer
>>>> and the object pInstance points to?
>>> Any system-dependent mutex has to behave like a double-side memory
>>> barrier (it's a follow-up from the mutual exclusion requirement).
>>
>> I didn't realize this was guaranteed for all systems...
>>
>
> The ellipsis was an indication that I find your claim that all mutex
> implementations include memory barriers slightly dubious. :) From
> Wikipedia:
>
> "These primitives (mutexes) are *usually* implemented with the memory
> barriers required to provide the expected memory visibility semantics.
> In such environments explicit use of memory barriers is not *generally*
> necessary."
>
> I guess a mutex implementation that didn't include memory barriers on a
> system where memory barriers are needed would not be very useful.
Yes, that's what I meant. Wikipedia is a great resource to find an explanation
of a concept but it does not have to tie all ends together with the strictness
of a specification. In our case, the article on Mutual Exclusion says:
"Mutual exclusion .. algorithms are used in concurrent programming to avoid the
simultaneous use of a common resource, such as a global variable, by pieces of
computer code called critical sections. A critical section is a piece of code in
which a process or thread accesses a common resource".
In our case, we want mutual exclusion for threads and our "common resource" is
memory containing pInstance. A mutex that in fact does *not* allow a thread to
access the common resource (e.g. due to not executing a memory barrier) does not
seem to satisfy to the above definition.
Then of course another article (on memory barriers) written by other people does
not agree.. I do not see an issue with that. Probably they saw something called
"mutex" that required the mutex client code to additionally exercise memory
barrier.. in my and the Wikipedia first article's view it would not be a
complete mutex, anyway. I am fully content with being in this sort of
disagreement with one or more articles at Wikipedia.
>
> /Leigh
-Pavel
[toc] | [prev] | [next] | [standalone]
| From | "Chris M. Thomasson" <cristom@charter.net> |
|---|---|
| Date | 2011-05-04 11:49 -0700 |
| Message-ID | <hchwp.24123$vC5.17828@newsfe01.iad> |
| In reply to | #4565 |
"Leigh Johnston" <leigh@i42.co.uk> wrote in message news:aLSdnS3SA-XeaSDQnZ2dnUVZ8vKdnZ2d@giganews.com... [...] >> On 01/05/2011 22:26, Pavel wrote: [...] >>> Any system-dependent mutex has to behave like a double-side memory >>> barrier (it's a follow-up from the mutual exclusion requirement). >> >> I didn't realize this was guaranteed for all systems... >> > > The ellipsis was an indication that I find your claim that all mutex > implementations include memory barriers slightly dubious. :) From > Wikipedia: > > "These primitives (mutexes) are *usually* implemented with the memory > barriers required to provide the expected memory visibility semantics. In > such environments explicit use of memory barriers is not *generally* > necessary." > > I guess a mutex implementation that didn't include memory barriers on a > system where memory barriers are needed would not be very useful. FWIW, there are mutex implementations that can "skip" explicit memory barrier operations. You basically need to think in term of "Asymmetric Synchronization". Here is some more information on the subject: http://blogs.sun.com/dave/resource/Asymmetric-Dekker-Synchronization.txt http://www.trl.ibm.com/people/kawatiya/Kawachiya05phd.pdf (chapter 6)
[toc] | [prev] | [next] | [standalone]
| From | Pavel <pauldontspamtolk@removeyourself.dontspam.yahoo> |
|---|---|
| Date | 2011-05-06 00:16 -0400 |
| Message-ID | <4dc3761e$0$16714$c3e8da3$10cdda79@news.astraweb.com> |
| In reply to | #4691 |
Chris M. Thomasson wrote:
> "Leigh Johnston"<leigh@i42.co.uk> wrote in message
> news:aLSdnS3SA-XeaSDQnZ2dnUVZ8vKdnZ2d@giganews.com...
> [...]
>>> On 01/05/2011 22:26, Pavel wrote:
> [...]
>>>> Any system-dependent mutex has to behave like a double-side memory
>>>> barrier (it's a follow-up from the mutual exclusion requirement).
>>>
>>> I didn't realize this was guaranteed for all systems...
>>>
>>
>> The ellipsis was an indication that I find your claim that all mutex
>> implementations include memory barriers slightly dubious. :) From
>> Wikipedia:
>>
>> "These primitives (mutexes) are *usually* implemented with the memory
>> barriers required to provide the expected memory visibility semantics. In
>> such environments explicit use of memory barriers is not *generally*
>> necessary."
>>
>> I guess a mutex implementation that didn't include memory barriers on a
>> system where memory barriers are needed would not be very useful.
>
> FWIW, there are mutex implementations that can "skip" explicit memory
> barrier operations. You basically need to think in term of "Asymmetric
> Synchronization". Here is some more information on the subject:
>
> http://blogs.sun.com/dave/resource/Asymmetric-Dekker-Synchronization.txt
>
> http://www.trl.ibm.com/people/kawatiya/Kawachiya05phd.pdf
> (chapter 6)
Thanks Chris for interesting resources! The techniques described there do show
how to avoid executing memory barriers in both threads and still achieve mutual
exclusion. My statement above still holds: these mutices "behave like a
double-side memory barrier" (quoting myself) without actually executing them.
They ensure the other threads sees the changes in the correct order which
answers the original Leigh's question ("How does this prevent CPU reordering of
stores to the pInstance pointer and the object pInstance points to?").
All that damned C++ "as-if" legalese.. I have never imagined myself using such
language by accident .. but that exactly what happened.
-Pavel
[toc] | [prev] | [next] | [standalone]
| From | Joshua Maurice <joshuamaurice@gmail.com> |
|---|---|
| Date | 2011-05-02 15:43 -0700 |
| Message-ID | <60f7d6f6-4013-4fcc-bd44-2739837fc1e6@z15g2000prn.googlegroups.com> |
| In reply to | #4561 |
On May 1, 2:44 pm, Leigh Johnston <le...@i42.co.uk> wrote: > On 01/05/2011 22:26, Pavel wrote: > > Leigh Johnston wrote: > >> How does this prevent CPU reordering of stores to the pInstance pointer > >> and the object pInstance points to? > > Any system-dependent mutex has to behave like a double-side memory > > barrier (it's a follow-up from the mutual exclusion requirement). > > I didn't realize this was guaranteed for all systems... Anything that calls itself a mutex or lock in the C++0x or Java memory model, or any sane memory model for that matter, must guarantee that. That's word the terms "mutex", "lock", and "critical section" mean. It guarantees that properly synchronized code will execute according to the guarantees of the language, and if some hardware needs a "double- sided memory barrier", then it needs a "double-sided memory barrier".
[toc] | [prev] | [next] | [standalone]
| From | James Kanze <james.kanze@gmail.com> |
|---|---|
| Date | 2011-05-01 14:53 -0700 |
| Message-ID | <d753c386-2753-4cfc-b77a-3cf9712c9462@d27g2000vbz.googlegroups.com> |
| In reply to | #4559 |
On May 1, 10:26 pm, Pavel
<pauldontspamt...@removeyourself.dontspam.yahoo> wrote:
[...]
> This does not prevent from unnecessary overhead though
> (grabbing a mutex in a highly contentious case can be
> expensive and it is unnecessary when all contenders want is to
> read a pointer rather than change it -- which is the case
> after the Singleton has been initialized).
Contention becomes relevant when two conditions are met: many
threads want to acquire the mutex, and having acquired the
mutex, a thread holds it for a certain time. In the case of the
singleton, once the object has been created, the thread holds
the mutex the time it takes for a comparison with null. That
is, a very, very short time. I have yet to see a case where
there was actual contention for the mutex in a singleton once
the singleton has been created. And if you do end up in such a
rare case, as I pointed out, calling Singleton::instance() once
before threading starts is sufficient to ensure that the mutex
isn't needed at all. Double check locking is a solution in
search of a problem, and to top it off, a solution which doesn't
work (at least in standard C++).
--
James Kanze
[toc] | [prev] | [next] | [standalone]
| From | Pavel <pauldontspamtolk@removeyourself.dontspam.yahoo> |
|---|---|
| Date | 2011-05-01 19:23 -0400 |
| Message-ID | <4dbdeb7f$0$2423$c3e8da3$9b4ff22a@news.astraweb.com> |
| In reply to | #4563 |
James Kanze wrote: > On May 1, 10:26 pm, Pavel > <pauldontspamt...@removeyourself.dontspam.yahoo> wrote: > > [...] >> This does not prevent from unnecessary overhead though >> (grabbing a mutex in a highly contentious case can be >> expensive and it is unnecessary when all contenders want is to >> read a pointer rather than change it -- which is the case >> after the Singleton has been initialized). > > Contention becomes relevant when two conditions are met: many > threads want to acquire the mutex, and having acquired the > mutex, a thread holds it for a certain time. In the case of the > singleton, once the object has been created, the thread holds > the mutex the time it takes for a comparison with null. That > is, a very, very short time. I have yet to see a case where > there was actual contention for the mutex in a singleton once > the singleton has been created. I agree most of the times it does not happen. However, I have met situations when several threads start calling a short method on a single instance (like sending/reading a simple but often message to/from a singleton queue or similar) and, after they clash once, the situation called "lock convoy" emerges whereas the threads spent most of their time switching from runnable to blocked state. In some old implementations of mutices this condition was made worse by CPU cache traffic bursts occuring when the holding thread released a mutex -- all blocked threads were awoken to see which grabs it first; some of the current implementations have this aggravating issue solved (e.g. in linux 2.6 Kernel it's solved via futex-based implementation). Regardless, as soon as a lock convoy is formed, it's kind of self-supporting until all contending threads but one receive a relatively long message to process -- all at the same time -- for which you might have to wait for a while. > And if you do end up in such a > rare case, as I pointed out, calling Singleton::instance() once > before threading starts is sufficient to ensure that the mutex > isn't needed at all. Double check locking is a solution in > search of a problem, and to top it off, a solution which doesn't > work (at least in standard C++). I have never defended DCL. In fact, I have never defended a Singleton pattern. IMHO it is the most-overused, the least-understood, the vaguest-defined of all GoF patterns; and few people seem to have read the end of the "Consequences" sub-section of "Singleton" section of their book (actuating Consequences #4,5 there may invalidate the whole discussion on DCL for Singleton irrelevant). I can't recall when I used it last time -- most of the times you are much better without. When I have to use one written by someone else, I try to acquire it before starting any threads and cache, as per your advice or per the article referred to by OP. > > -- > James Kanze
[toc] | [prev] | [next] | [standalone]
| From | James Kanze <james.kanze@gmail.com> |
|---|---|
| Date | 2011-05-02 09:02 -0700 |
| Message-ID | <a9f74872-3179-4b72-b770-701579989273@q21g2000vbs.googlegroups.com> |
| In reply to | #4564 |
On May 2, 12:23 am, Pavel
<pauldontspamt...@removeyourself.dontspam.yahoo> wrote:
> James Kanze wrote:
[...]
> In fact, I have never defended a Singleton pattern. IMHO it
> is the most-overused, the least-understood, the
> vaguest-defined of all GoF patterns; and few people seem to
> have read the end of the "Consequences" sub-section of
> "Singleton" section of their book (actuating Consequences #4,5
> there may invalidate the whole discussion on DCL for Singleton
> irrelevant).
I don't think that the presentation in the GoF was actually that
vague. The problem is that as presented in the GoF, it is a
form of contract enforcement: the design says that there may
only be one instance, so we design the class to enforce the
design. Which per se is probably overkill: the semantics of
most singletons are such that no reasonable programmer would
create more than one instance anyway, so it's not worth the
extra effort to forbid it. In the case of C++, however, the
singleton idiom is most often used primarily to solve order of
initialization issues; once you've created the code necessary to
solve those, making it a singleton (enforcing only one instance)
is merely a question of where you put the "public:".
--
James Kanze
[toc] | [prev] | [next] | [standalone]
| From | Pavel <pauldontspamtolk@removeyourself.dontspam.yahoo> |
|---|---|
| Date | 2011-05-05 23:46 -0400 |
| Message-ID | <4dc36f1f$0$2358$c3e8da3$9b4ff22a@news.astraweb.com> |
| In reply to | #4574 |
James Kanze wrote: > On May 2, 12:23 am, Pavel > <pauldontspamt...@removeyourself.dontspam.yahoo> wrote: >> James Kanze wrote: > > [...] >> In fact, I have never defended a Singleton pattern. IMHO it >> is the most-overused, the least-understood, the >> vaguest-defined of all GoF patterns; and few people seem to >> have read the end of the "Consequences" sub-section of >> "Singleton" section of their book (actuating Consequences #4,5 >> there may invalidate the whole discussion on DCL for Singleton >> irrelevant). > > I don't think that the presentation in the GoF was actually that > vague. The problem is that as presented in the GoF, it is a > form of contract enforcement: the design says that there may > only be one instance, That's where the vagueness started (in definition of the problem, not the analysis and especially consequences which most people don't read to the end). "Only one" -- but in what context? One per a process? a thread? the whole world? a CPU? one computer? corporate network? a region of the corporate network? all of the above make sense and are being actively used. 50% of singletons that were defined in single-threaded programs as one-per-process (and, hence, per-thread), should be taken to TLS and made "one-per-thread" (and hence, "many-per-process) when converting to multi-threaded program. The other 50% should not (but of these 50%, some would gain from changing the requirement to "only one per machine" and becoming servers and yet others could perfectly become a pool with many-to-many relationship to the available threads or CPUs -- or cores or who knows what). The techniques for enforcing the "singleness" contracts above are meaningfully different. The other vagueness of "only one" is illustrated by the question "during which period of time"? Should it be the only one intance per life of a process or a thread? Or it should outlive some processes or threads? Or there should be exactly one (or no more than one?) instance "at at time" but it can be re-created several times during the life of a process (or a thread)? All of the above are useful in particular circumstances and enforcement techniques of differ significantly on the actual specific requirements (Andrei Alexandrescu illustrated the latter requirement with his "Phoenix singleton" composed of a set of different generic policies. Still he did not cover many of the above variances and I can't imagine a useful implementation that does. Even his singleton is more proof-of-concept than production-ready code despite relatively high quality (and of course it does not even try to address the performance issue of thread-safe singleton that DCL tried to solve. Phoenix singleton does not work with DCL -- or fixed-with-atomic-DCL -- too well)). so we design the class to enforce the > design. Which per se is probably overkill: the semantics of > most singletons are such that no reasonable programmer would > create more than one instance anyway, so it's not worth the > extra effort to forbid it. In the case of C++, however, the > singleton idiom is most often used primarily to solve order of > initialization issues; once you've created the code necessary to > solve those, making it a singleton (enforcing only one instance) > is merely a question of where you put the "public:". Yeah, that's another bunch of good questions (and don't even let me start on order-of-destruction issues). I prefer explicitly give one object to the constructor of the another when one depends on another or create them within a manager object that ensures the sequence. Then you end up with one manager for all interdependent "singletons" for a particular program. Make next step and create that manager in "main()" (or, if you are a library writer, make user create it in his "main()", or, if your user is a library writer, too, make him include your manager in his own manager that he has to make his user to create in main etc recursively) and there is no need for a singleton. BUT.. this only works if no one in the chain of library writers decided to put singleness inside his or her class (that is, s/he did not give his or her users a choice to create an object of class defined in his/her library at will). I am trying not to be such a nuisance; that's why I forgot when there was the last time I decided to encapsulate singleness in my class. > > -- > James Kanze -Pavel
[toc] | [prev] | [next] | [standalone]
| From | James Kanze <james.kanze@gmail.com> |
|---|---|
| Date | 2011-05-01 14:47 -0700 |
| Message-ID | <e4a00730-8155-4bfa-87c5-14d0ae34d848@q20g2000vbx.googlegroups.com> |
| In reply to | #4558 |
On May 1, 9:49 pm, Leigh Johnston <le...@i42.co.uk> wrote:
> On 30/04/2011 23:54, James Kanze wrote:
> > On Apr 26, 5:58 pm, jl_p...@hotmail.com wrote:
> >> Recently I've been reading up on "Double-Checked Locking" in
> >> C++ and how it's often implemented imperfectly.
> > You mean, how it is impossible to implement in standard C++
> > (03).
> >> The Article "C++ and the Perils of Double-Checked Locking" by
> >> Scott Meyers and Andrei Alexandrescu
> >> (http://www.aristeia.com/Papers/DDJ_Jul_Aug_2004_revised.pdf)
> >> provides a good overview of how it's usually done and why it's
> >> often inadequate.
> > The standard solution for implementing a singleton works just
> > fine:
> > Singleton* Singleton::instance()
> > {
> > ScopedLock lock(mutex);
> > if ( pInstance == NULL )
> > pInstance = new Singleton;
> > return pInstance;
> > }
> How does this prevent CPU reordering of stores to the
> pInstance pointer and the object pInstance points to?
Compiler magic:-).
Presumably, somewhere in the constructor of ScopedLock, you end
up calling pthread_mutex_lock, or something similar. And if the
compiler is Posix compliant, it knows that it cannot move code
accross a call to pthread_mutex_lock. (And of course, the code
in pthread_mutx_lock contains the necessary machine instructions
to ensure that the hardware respects the order. Posix requires
it.) (Replace Posix with Windows, and posix_mutex_lock with the
corresponding Windows function, if that's your platform.)
Of course, if the compiler doesn't support multithreading (or
you didn't specify the options it requires for it to support
multithreading), then there is no solution; you cant use
multithreading, period.
--
James Kanze
[toc] | [prev] | [standalone]
Page 2 of 2 — ← Prev page 1 [2]
Back to top | Article view | comp.lang.c++
csiph-web