Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > comp.lang.c++ > #4373 > unrolled thread

Please disprove this Double-Checked Locking "fix"

Started byjl_post@hotmail.com
First post2011-04-26 09:58 -0700
Last post2011-05-01 14:47 -0700
Articles 13 on this page of 33 — 9 participants

Back to article view | Back to comp.lang.c++


Contents

  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]


#4558

FromLeigh Johnston <leigh@i42.co.uk>
Date2011-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]


#4559

FromPavel <pauldontspamtolk@removeyourself.dontspam.yahoo>
Date2011-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]


#4561

FromLeigh Johnston <leigh@i42.co.uk>
Date2011-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]


#4565

FromLeigh Johnston <leigh@i42.co.uk>
Date2011-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]


#4567

FromPavel <pauldontspamtolk@removeyourself.dontspam.yahoo>
Date2011-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]


#4691

From"Chris M. Thomasson" <cristom@charter.net>
Date2011-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]


#4754

FromPavel <pauldontspamtolk@removeyourself.dontspam.yahoo>
Date2011-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]


#4594

FromJoshua Maurice <joshuamaurice@gmail.com>
Date2011-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]


#4563

FromJames Kanze <james.kanze@gmail.com>
Date2011-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]


#4564

FromPavel <pauldontspamtolk@removeyourself.dontspam.yahoo>
Date2011-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]


#4574

FromJames Kanze <james.kanze@gmail.com>
Date2011-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]


#4753

FromPavel <pauldontspamtolk@removeyourself.dontspam.yahoo>
Date2011-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]


#4562

FromJames Kanze <james.kanze@gmail.com>
Date2011-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