Topic: Destruction of singletons


Author: James Kanze US/ESC 60/3/141 #40763 <kanze@lts.sel.alcatel.de>
Date: 1996/02/26
Raw View
In article <4go9au$l8f@engnews1.Eng.Sun.COM> Jamshid Afshar
<jamshid@IO.COM> writes:

|> In article <4fvng1$jit@netlab.cs.rpi.edu>,
|> Bob Archer  <bob@hottub.demon.co.uk> wrote:
|> >Singleton * Singleton::Instance() {
|> >  if (_instance == 0)
|> >      _instance = new Singleton;
|> >  return _instance;
|> >}
|> >
|> >[Jamshid] suggested that using a static local variable might be better:
|> >
|> >Singleton * Singleton::Instance() {
|> >  static Singleton s;
|> >  return &s;
|> >}
|> >
|> >James Kanze then replied:
|> > Although I tend to prefer this idiom too, it is important to realize
|> > that there are some cases where it is preferable *not* to destruct the
|> > variable.  (If there is only one, this shouldn't constitute a memory
|> > leak.)  [...]
|> >
|> >Why does the fact that only one variable is involved mean that this is
|> >not a memory leak?

|> James has responded that although technically it is a memory leak,
|> it's not important unless you're using a poor operating system that
|> does not recover all memory when a process finishes.  I agree I
|> wouldn't want to use such an operating system (Win3.1?), but I think
|> it is sloppy programming to not ensure all the objects you create get
|> destroyed and all the memory you allocate gets deleted.  Of course
|> there are probably cases where this is not possible or practical, but
|> leaked memory and undestroyed objects cause hassles when using
|> debugging tools like Purify which report all memory leaks when the
|> program finishes.  You'll get enough leak reports from sloppy 3rd
|> party libraries -- you shouldn't be adding a bunch "ignore this leak,
|> I 'know' it's okay" configuration statements for the code you write.
|> Also, you may sometime soon want to remove the "singleton" restriction
|> on the class -- IMO get it right the first time and make sure its
|> destructor works properly.

First of all, I will agree that not destructing the object is not the
most elegant solution around, and in fact, now that I am aware of
relatively simple programming techniques (returning the address of a
local static) to ensure this, I use them.

Concerning the two practical objections, however:

1. As long as there is an active pointer to the memory (a static
pointer), Purify will not classify it as a leak.

2. This technique is only used for *static* data member pointers.  If
the class is no longer used as a singleton, then it is no problem
putting the delete in the constructor.  The problem is where to put
the delete otherwise.  How can you know that everyone has finished
using the object.

In general, I would stress that the prefered solution is to not use
the singleton object in any destructors, and use a local static to
initialize the pointer.  If you do have to use the object in
destructors, however, I would still prefer not destructing it to
trying to manage the `order of destruction' problem (given that most
compiler implementors still consider the order of destruction of
static locals undefined).

|> >I take the point about the Singleton object possibly being used in the
|> >destructors of a static object. Is there any way to ensure that the
|> >singleton will be destroyed but only after everything that might wish
|> >to use it has been destroyed ? (I guess that this is just the usual
|> >problem with the order of global construction / destruction ).

|> Actually, the construction and destruction of "local objects of static
|> storage duration" is not the same as objects outside of functions.  In
|> fact, it seems local statics are more poorly specified.  According to
|> the April '95 Draft, while they are guaranteed to not be constructed
|> until (and if) the function is called, exactly when the destructor is
|> called is unspecified.

I actually believe that it is, and in fact, has been well defined from
the ARM.  The exact language in the sections concerning the
initialization and destruction of statics is significant: in the
sections concerning initializations, the reference is consistently to
`non-local' statics, whereas in the section concerning destruction,
the words `non-local' are significantly absent.  IMHO, the extreme
care concerning the use of `non-local' in the initialization section
makes it clear that its absense in the destruction section was not
simply oversight, and that *all* statics were meant.

The perennial test suite contains a case which in fact does test this.
Regretfully, the compiler implementors I'm familiar with have all
denied that this was what was meant, and claimed that there was no
guarantee concerning local statics.

In any case, the Jan. 96 draft makes the question moot, since it
explicitly adds the words in the destructor section clarifying that
static means both local and non-local.

|> At least globals are sensibly guaranteed to be
|> destroyed in the reverse order they were constructed.  What should
|> have been required IMO is that local statics be destroyed in the
|> reverse order they were (fully) constructed.  For example, if the
|> constructor of a local static "g_list" calls a function containing
|> another local static "g_log", g_list should be destroyed first since
|> its construction was finished last.  This is desirable behavior since
|> g_list's destructor may also use g_log.

I agree.  The remaining weak point in the standard is that it doesn't
really specify what is meant by the ordering, entering the
constructor, or leaving it.

    [...]
|> Is there any reason the committee chose not to have local statics do
|> this kind of ordering automatically?  Of course, it won't solve every
|> problem related to "global" construction/destruction ordering, but it
|> seems a lot better than leaving it unspecified and doesn't seem like
|> it would have placed any burden whatsoever on implementors.

It does place a burden on the implementors, since in fact, the order
of destruction will depend upon the order the functions are called.
But the committe, and Bjarne before them, voted in favor of the
programmer, not the implementor.

--
James Kanze         Tel.: (+33) 88 14 49 00        email: kanze@gabi-soft.fr
GABI Software, Sarl., 8 rue des Francs-Bourgeois, F-67000 Strasbourg, France
Conseils,    tudes et r   alisations en logiciel orient    objet --
                -- A la recherche d'une activit    dans une region francophone
---
[ comp.std.c++ is moderated.  To submit articles: Try just posting with your
                newsreader.  If that fails, use mailto:std-c++@ncar.ucar.edu
  comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
  Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
  Comments? mailto:std-c++-request@ncar.ucar.edu
]





Author: jamshid@io.com (Jamshid Afshar)
Date: 1996/02/26
Raw View
In article <9602261828.AA28873@lts.sel.alcatel.de>,
James Kanze US/ESC 60/3/141 #40763  <kanze@lts.sel.alcatel.de> wrote:
>In article <4go9au$l8f@engnews1.Eng.Sun.COM> Jamshid Afshar
><jamshid@IO.COM> writes:
>
>1. As long as there is an active pointer to the memory (a static
>pointer), Purify will not classify it as a leak.

You're right.  I thought there was some kind of report of all unfreed
memory at program exit.

>In general, I would stress that the prefered solution is to not use
>the singleton object in any destructors, and use a local static to
>initialize the pointer.  If you do have to use the object in
>destructors, however, I would still prefer not destructing it to
>trying to manage the `order of destruction' problem (given that most
>compiler implementors still consider the order of destruction of
>static locals undefined).

I agree I would rather have the compiler manage the destruction,
especially since it's so easy for them to do it (just generate a
"destroy" function and register it with atexit() when the constructor
returns).

>|> Actually, the construction and destruction of "local objects of static
>|> storage duration" is not the same as objects outside of functions.  In
>|> fact, it seems local statics are more poorly specified.  According to
>|> the April '95 Draft, while they are guaranteed to not be constructed
>|> until (and if) the function is called, exactly when the destructor is
>|> called is unspecified.
>
>I actually believe that it is, and in fact, has been well defined from
>the ARM.

Okay, I see now that I was misreading section 6.7 Declaration
Statement in the ARM and April 95 Draft.  I took the sentence "Exactly
when is unspecified" to mean the destructor order is *completely*
unspecified.  I guess the sentence is only referring to the previous
sentence (ie, "Whether the destructor is called before or as part of
the atexit() functions is unspecified.").

   5 The destructor for a local object with static storage duration will be
     executed if and only if the variable was constructed.  The  destructor
     is  called  either  immediately  before or as part of the calls of the
     atexit()  functions  (_lib.support.start.term_).   Exactly   when   is
     unspecified.

>In any case, the Jan. 96 draft makes the question moot, since it
>explicitly adds the words in the destructor section clarifying that
>static means both local and non-local.

I assume you're referring to 3.6.3 Termination (analogous to ARM 3.4
Start and Termination).  Btw, has the wording in 6.7 been changed or
improved?  There seems to be some conflict with it and 3.6.3 wrt the
order of static destructors and atexit() calls.

>|> [...] What should
>|> have been required IMO is that local statics be destroyed in the
>|> reverse order they were (fully) constructed.
>
>I agree.  The remaining weak point in the standard is that it doesn't
>really specify what is meant by the ordering, entering the
>constructor, or leaving it.

If the Standard does not require local statics to be destroyed in the
reverse order they were *fully* constructed, local statics are useless
to the many programmers who have singletons that use another singleton
during its construction and destruction (eg, all singletons may report
their construction and destruction to a "logfile" singleton).
Programmers will instead have to dynamically create the singletons and
either never delete them or use the "atexit(&destroy)" technique I
mentioned.  The "atexit(&destroy)" technique isn't difficult (you can
even wrap it up in a template), but it seems silly for this to be 1)
unspecified and 2) specified in a manner that makes local statics so
much less useful.

Jamshid Afshar
jamshid@io.com
---
[ comp.std.c++ is moderated.  To submit articles: Try just posting with your
                newsreader.  If that fails, use mailto:std-c++@ncar.ucar.edu
  comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
  Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
  Comments? mailto:std-c++-request@ncar.ucar.edu
]





Author: James Kanze US/ESC 60/3/141 #40763 <kanze@lts.sel.alcatel.de>
Date: 1996/02/27
Raw View
In article <4gtmg0$t59@pentagon.io.com> jamshid@io.com (Jamshid
Afshar) writes:

|> In article <9602261828.AA28873@lts.sel.alcatel.de>,
|> James Kanze US/ESC 60/3/141 #40763  <kanze@lts.sel.alcatel.de> wrote:
|> >In article <4go9au$l8f@engnews1.Eng.Sun.COM> Jamshid Afshar
|> ><jamshid@IO.COM> writes:
|> >
|> >1. As long as there is an active pointer to the memory (a static
|> >pointer), Purify will not classify it as a leak.

|> You're right.  I thought there was some kind of report of all unfreed
|> memory at program exit.

I think you can get a report of all unfreed memory, too, although it
isn't classified as a leak or a potential leak.

|> >In general, I would stress that the prefered solution is to not use
|> >the singleton object in any destructors, and use a local static to
|> >initialize the pointer.  If you do have to use the object in
|> >destructors, however, I would still prefer not destructing it to
|> >trying to manage the `order of destruction' problem (given that most
|> >compiler implementors still consider the order of destruction of
|> >static locals undefined).

|> I agree I would rather have the compiler manage the destruction,
|> especially since it's so easy for them to do it (just generate a
|> "destroy" function and register it with atexit() when the constructor
|> returns).

This would be one solution.

All of the solutions involve dynamic ordering; i.e.: some sort of
registration when the object is constructed.  At present, all of the
compilers I know of use static ordering for the destructors, with just
a simple flag to indicate whether the local static was constructed or
not.

|> >|> Actually, the construction and destruction of "local objects of static
|> >|> storage duration" is not the same as objects outside of functions.  In
|> >|> fact, it seems local statics are more poorly specified.  According to
|> >|> the April '95 Draft, while they are guaranteed to not be constructed
|> >|> until (and if) the function is called, exactly when the destructor is
|> >|> called is unspecified.
|> >
|> >I actually believe that it is, and in fact, has been well defined from
|> >the ARM.

|> Okay, I see now that I was misreading section 6.7 Declaration
|> Statement in the ARM and April 95 Draft.  I took the sentence "Exactly
|> when is unspecified" to mean the destructor order is *completely*
|> unspecified.  I guess the sentence is only referring to the previous
|> sentence (ie, "Whether the destructor is called before or as part of
|> the atexit() functions is unspecified.").

|>    5 The destructor for a local object with static storage duration will be
|>      executed if and only if the variable was constructed.  The  destructor
|>      is  called  either  immediately  before or as part of the calls of the
|>      atexit()  functions  (_lib.support.start.term_).   Exactly   when   is
|>      unspecified.

|> >In any case, the Jan. 96 draft makes the question moot, since it
|> >explicitly adds the words in the destructor section clarifying that
|> >static means both local and non-local.

|> I assume you're referring to 3.6.3 Termination (analogous to ARM 3.4
|> Start and Termination).  Btw, has the wording in 6.7 been changed or
|> improved?  There seems to be some conflict with it and 3.6.3 wrt the
|> order of static destructors and atexit() calls.

Correct.  I've now got it on line, and in fact, it is better than I
remembered, since it also definitly states that the order is
determined by the completion of the constructors.  (For some reason, I
remembered this as being vague.  I was wrong.)

    Destructors for initialized objects of static storage duration
    (declared at block scope or at namespace scope) are called when
    returning from <B>main</B> and when calling <B>exit</B>.  These
    objects are destroyed in the reverse order of the completion of
    their constructors.  For an object of array or class type, all
    subobjects of that object are destroyed before any local object
    with static storage duration initialized during the construction
    the subobjects is destroyed.

Note the words in the first parentheses; this makes it quite clear
that the ordering holds over both local (block scope) and non-local
(namespace scope) variables.

|> >|> [...] What should
|> >|> have been required IMO is that local statics be destroyed in the
|> >|> reverse order they were (fully) constructed.
|> >
|> >I agree.  The remaining weak point in the standard is that it doesn't
|> >really specify what is meant by the ordering, entering the
|> >constructor, or leaving it.

|> If the Standard does not require local statics to be destroyed in the
|> reverse order they were *fully* constructed, local statics are useless
    [...]

Agreed.  This was my mistake: the current (Jan. 96) draft does
guarantee exactly what you (and I) want.

Now if I could only convince them to require static initialization (of
non-local statics) to take place before main:-).

--
James Kanze         Tel.: (+33) 88 14 49 00        email: kanze@gabi-soft.fr
GABI Software, Sarl., 8 rue des Francs-Bourgeois, F-67000 Strasbourg, France
Conseils,    tudes et r   alisations en logiciel orient    objet --
                -- A la recherche d'une activit    dans une region francophone
---
[ comp.std.c++ is moderated.  To submit articles: Try just posting with your
                newsreader.  If that fails, use mailto:std-c++@ncar.ucar.edu
  comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
  Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
  Comments? mailto:std-c++-request@ncar.ucar.edu
]





Author: Jamshid Afshar <jamshid@IO.COM>
Date: 1996/02/25
Raw View
In article <4fvng1$jit@netlab.cs.rpi.edu>,
Bob Archer  <bob@hottub.demon.co.uk> wrote:
>Singleton * Singleton::Instance() {
>  if (_instance == 0)
>      _instance = new Singleton;
>  return _instance;
>}
>
>[Jamshid] suggested that using a static local variable might be better:
>
>Singleton * Singleton::Instance() {
>  static Singleton s;
>  return &s;
>}
>
>James Kanze then replied:
> Although I tend to prefer this idiom too, it is important to realize
> that there are some cases where it is preferable *not* to destruct the
> variable.  (If there is only one, this shouldn't constitute a memory
> leak.)  [...]
>
>Why does the fact that only one variable is involved mean that this is
>not a memory leak?

James has responded that although technically it is a memory leak,
it's not important unless you're using a poor operating system that
does not recover all memory when a process finishes.  I agree I
wouldn't want to use such an operating system (Win3.1?), but I think
it is sloppy programming to not ensure all the objects you create get
destroyed and all the memory you allocate gets deleted.  Of course
there are probably cases where this is not possible or practical, but
leaked memory and undestroyed objects cause hassles when using
debugging tools like Purify which report all memory leaks when the
program finishes.  You'll get enough leak reports from sloppy 3rd
party libraries -- you shouldn't be adding a bunch "ignore this leak,
I 'know' it's okay" configuration statements for the code you write.
Also, you may sometime soon want to remove the "singleton" restriction
on the class -- IMO get it right the first time and make sure its
destructor works properly.

>I take the point about the Singleton object possibly being used in the
>destructors of a static object. Is there any way to ensure that the
>singleton will be destroyed but only after everything that might wish
>to use it has been destroyed ? (I guess that this is just the usual
>problem with the order of global construction / destruction ).

Actually, the construction and destruction of "local objects of static
storage duration" is not the same as objects outside of functions.  In
fact, it seems local statics are more poorly specified.  According to
the April '95 Draft, while they are guaranteed to not be constructed
until (and if) the function is called, exactly when the destructor is
called is unspecified.  At least globals are sensibly guaranteed to be
destroyed in the reverse order they were constructed.  What should
have been required IMO is that local statics be destroyed in the
reverse order they were (fully) constructed.  For example, if the
constructor of a local static "g_list" calls a function containing
another local static "g_log", g_list should be destroyed first since
its construction was finished last.  This is desirable behavior since
g_list's destructor may also use g_log.

With the committee leaving destruction order of local statics
unspecified, you'll have to instead dynamically allocate the singleton
and use atexit() to delete it if you have singletons referring to each
other in their constructor or destructor code.  Note: atexit()
functions are called in the reverse order they were registered.

 class LogFile {
 public:
    LogFile& theLog() {
       if (!p) {
          p = new LogFile;
          atexit(&destroy);
       }
       return *p;
    }
 private:
    static LogFile* p;
    void destroy() { delete p; }
 };

Is there any reason the committee chose not to have local statics do
this kind of ordering automatically?  Of course, it won't solve every
problem related to "global" construction/destruction ordering, but it
seems a lot better than leaving it unspecified and doesn't seem like
it would have placed any burden whatsoever on implementors.

Jamshid Afshar
jamshid@io.com
[ To submit articles: Try just posting with your newsreader.
        If that fails, use mailto:std-c++@ncar.ucar.edu
  FAQ:    http://reality.sgi.com/employees/austern_mti/std-c++/faq.html
  Policy: http://reality.sgi.com/employees/austern_mti/std-c++/policy.html
  Comments? mailto:std-c++-request@ncar.ucar.edu
]