Topic: why would I use std::set_terminate?


Author: restor <akrzemi1@gmail.com>
Date: Fri, 4 Jun 2010 17:43:29 CST
Raw View
Hi,
I know how std::terminate and std::set_terminate work (at least I
think I do). Aborting the program is a good default. But could anyone
tell me what other implementation of a terminate handler there could
be, that would be useful in practice?
I can think of the following example:

 void startProgram();

 int main() {
   std::set_terminate( startProgram );
   startProgram();
   return 0;
 } // Throw at will, I need not catch anything

But I doubt this was the intent of the designers of exception
mechanism. Did the designers had any particular implementation of
termination function in mind?

A second question; do all global objects are guaranteed to work when
std::terminate is called? I.e. is it guaranteed that the destructors
of the globals have not been called before std::terminate ends?
Are global objects destroyed after the program termination with
std::terminate, or not? Or is it dependent on what function I register
for termination (exit or abort)?

Regards,
&rzej
Regards,
&rzej

--
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use
mailto:std-c++@netlab.cs.rpi.edu<std-c%2B%2B@netlab.cs.rpi.edu>
]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]





Author: "Martin B." <0xCDCDCDCD@gmx.at>
Date: Mon, 7 Jun 2010 19:07:53 CST
Raw View
restor wrote:

> Hi,
> I know how std::terminate and std::set_terminate work (at least I
> think I do). Aborting the program is a good default. But could anyone
> tell me what other implementation of a terminate handler there could
> be, that would be useful in practice?
>

Hmm ... well the first thing I can think of is writing a log-entry before
calling terminate/abort. Writing a user dump also comes to mind, although
that is highly implementation specific of course.

br,
Martin

--
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use
mailto:std-c++@netlab.cs.rpi.edu<std-c%2B%2B@netlab.cs.rpi.edu>
]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]





Author: restor <akrzemi1@gmail.com>
Date: Tue, 8 Jun 2010 13:45:35 CST
Raw View
> > [...] Aborting the program is a good default. But could anyone
> > tell me what other implementation of a terminate handler there could
> > be, that would be useful in practice?
>
> Hmm ... well the first thing I can think of is writing a log-entry before
> calling terminate/abort. Writing a user dump also comes to mind, although
> that is highly implementation specific of course.

Hi,
after thinking about it for a while, I believe I found the answer to
my questions. I would still like someone, preferably one of the co-
authors of the feature, to verify my thoughts.
std::terminate is called in many different situations, and although
the most often quoted are:
 1. We threw an exception, the stack was unwound, but no matching
handler was found.
 2. Someone threw an uncaught  exception while the stack was being
unwound.
But terminate can also be called in situations, where an exception is
thrown, and no exception handler can be installed by definition:
either while initializing global objects (or static storage objects)
or while destroying them, or while calling functions registered with
std::atexit (which is almost the same as calling global destructors).

Whatever terminate does, the action should be valid in every
situation. Thus: we cannot rely on any global (or static) object
because it may not have been yet created, or may have already been
destroyed. In particular, it may be the constructor or the destructor
of the very object we want to use that caused the call to terminate by
throwing the exception.

So every example, in every manual I have seen so far, that reads like:

 void myTerminateHandler() {
     std::cerr << "terminate has beeen called" << std::endl;
 }

Is wrong, because they assume that std::cerr object's life time has
already started and has not yet finished -- and this may not be the
case, and there is no way to check it. The same with any logging that
relies on some global or static logger object -- we cannot log
anything.

I am not sure about the user dump. When std::terminate is called it is
not guaranteed or expected that what we see on the program stack is
what caused the termination. E.g. if an exception is thrown we unwind
the stack, untill we realize there is no handler (not mandatory, but
valid behavior). At the point of calling terminate we only know that
we are in the terminate handler, not sure if we know that we found no
exception handler, but for sure there is no information about what
exception and where was thrown.

The only think that I could think of, that can be safely called, is
some limited cleanup functions. Calling destructors of global object
is usually not that important because the OS does some cleanup anyway.
But there are some global OS resources that must be freed in order not
to spoil other programs or the OS. These cleanup functions must be
prepaired to be called twice, because terminate may be called after
they have already been called during the normal program cleanup.

I intend to teach that in my company, and I would really appreciate if
someone could tell if what I say makes sense.

Regards,
&rzej

--
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use
mailto:std-c++@netlab.cs.rpi.edu<std-c%2B%2B@netlab.cs.rpi.edu>
]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]





Author: =3D?ISO-8859-1?Q?Daniel_Kr=3DFCgler?=3D <daniel.kruegler@googlemail.c=.om>
Date: Tue, 8 Jun 2010 16:50:32 CST
Raw View
On 8 Jun., 21:45, restor <akrze...@gmail.com> wrote:
[..]
> Whatever terminate does, the action should be valid in every
> situation. Thus: we cannot rely on any global (or static) object
> because it may not have been yet created, or may have already been
> destroyed. In particular, it may be the constructor or the destructor
> of the very object we want to use that caused the call to terminate by
> throwing the exception.
>
> So every example, in every manual I have seen so far, that reads like:
>
>  void myTerminateHandler() {
>      std::cerr << "terminate has beeen called" << std::endl;
>  }
>
> Is wrong, because they assume that std::cerr object's life time has
> already started and has not yet finished -- and this may not be the
> case, and there is no way to check it. The same with any logging that
> relies on some global or static logger object -- we cannot log
> anything.

While your above description of general objects of static storage
duration is correct, you cannot apply these rules to the 8 iostream
objects. I know that the standard wording is fuzzy, but the informal
footnote 300 ([iostream.objects]/2),

"Constructors and destructors for static objects can access these
objects to read input from stdin or write output to stdout or stderr"

is a clear advice to an implementation to make that somehow
happen. Yes, they need to use black magic to realize that, but
I'm not aware of any iostream implementation that is of
non-theoretical interest, which doesn't get this right.

Note also that the discussion in defect report

http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-defects.html#369

explains a bit the idea and relates it to ios_base::Init.

Also note, that these objects are basically not destroyed, see
also the changes done to clarify that as of

http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-defects.html#574

and

http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-defects.html#1123

HTH & Greetings from Bremen,

Daniel Kr=FCgler


--
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use
mailto:std-c++@netlab.cs.rpi.edu<std-c%2B%2B@netlab.cs.rpi.edu>
]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]





Author: restor <akrzemi1@gmail.com>
Date: Wed, 9 Jun 2010 11:44:13 CST
Raw View
> > Whatever terminate does, the action should be valid in every
> > situation. Thus: we cannot rely on any global (or static) object
> > because it may not have been yet created, or may have already been
> > destroyed. In particular, it may be the constructor or the destructor
> > of the very object we want to use that caused the call to terminate by
> > throwing the exception.
>
> While your above description of general objects of static storage
> duration is correct, you cannot apply these rules to the 8 iostream
> objects. I know that the standard wording is fuzzy, but the informal
> footnote 300 ([iostream.objects]/2),
>
> "Constructors and destructors for static objects can access these
> objects to read input from stdin or write output to stdout or stderr"

Thanks for pointing this out.
I am not very familiar with iostream details, from the links you
provided I only understood that the implicit creation of
ios_base::Init objects guarantees that the 8 stream objects are
accessible from any ctor/dtor of every global or static object I
create.
Can I somehow re-use the ios_base::Init "trick" to make a similar
guarantee for my logger class? Or in order words, If I want to create
a logging mechanism that is available in every ctor/dtor of every
static/global object is it possible to do so by somehow employing the
ios_base::Init?

Regards,
&rzej



--
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use
mailto:std-c++@netlab.cs.rpi.edu<std-c%2B%2B@netlab.cs.rpi.edu>
]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]





Author: =3D?ISO-8859-1?Q?Daniel_Kr=3DFCgler?=3D <daniel.kruegler@googlemail.c=.om>
Date: Thu, 10 Jun 2010 14:09:51 CST
Raw View
On 9 Jun., 19:44, restor <akrze...@gmail.com> wrote:
> > > Whatever terminate does, the action should be valid in every
> > > situation. Thus: we cannot rely on any global (or static) object
> > > because it may not have been yet created, or may have already been
> > > destroyed. In particular, it may be the constructor or the destructor
> > > of the very object we want to use that caused the call to terminate b=
y
> > > throwing the exception.
>
> > While your above description of general objects of static storage
> > duration is correct, you cannot apply these rules to the 8 iostream
> > objects. I know that the standard wording is fuzzy, but the informal
> > footnote 300 ([iostream.objects]/2),
>
> > "Constructors and destructors for static objects can access these
> > objects to read input from stdin or write output to stdout or stderr"
>
> Thanks for pointing this out.
> I am not very familiar with iostream details, from the links you
> provided I only understood that the implicit creation of
> ios_base::Init objects guarantees that the 8 stream objects are
> accessible from any ctor/dtor of every global or static object I
> create.
> Can I somehow re-use the ios_base::Init "trick" to make a similar
> guarantee for my logger class? Or in order words, If I want to create
> a logging mechanism that is available in every ctor/dtor of every
> static/global object is it possible to do so by somehow employing the
> ios_base::Init?

It is not possible to realize exactly the same as what an
implementation has to do to make the iostream objects
"work". The ios_base::Init idiom catches many situations,
but not all. At least it is a technique that comes near to
the ideal. See e.g.

http://stackoverflow.com/questions/2216029/common-pattern-for-library-initi=
alization-and-shutdown

for some simplified variants of that (but none of them is
100% bullet-proof).

HTH & Greetings from Bremen,

Daniel Kr=FCgler


--
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use
mailto:std-c++@netlab.cs.rpi.edu<std-c%2B%2B@netlab.cs.rpi.edu>
]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]





Author: "Martin B." <0xCDCDCDCD@gmx.at>
Date: Thu, 10 Jun 2010 14:09:23 CST
Raw View
restor wrote:

> [...] Aborting the program is a good default. But could anyone
>>> tell me what other implementation of a terminate handler there could
>>> be, that would be useful in practice?
>>>
>> Hmm ... well the first thing I can think of is writing a log-entry before
>> calling terminate/abort. Writing a user dump also comes to mind, although
>> that is highly implementation specific of course.
>>
>
> Hi,
> after thinking about it for a while, I believe I found the answer to
> my questions. I would still like someone, preferably one of the co-
> authors of the feature, to verify my thoughts.
> std::terminate is called in many different situations, (....)
>
> Whatever terminate does, the action should be valid in every
> situation. Thus: we cannot rely on any global (or static) object
> because it may not have been yet created, or may have already been
> destroyed. (...)
> (...) relies on some global or static logger object -- we cannot log
> anything.
>
>
Ah. But you have to install your custom termination handler with
set_terminate, so you can setup everything you need to do logging *before*
calling set_terminate. Therefore you *can* rely on correctly initialized
objects in your termination handler.

I am not sure about the user dump. When std::terminate is called it is
> not guaranteed or expected that what we see on the program stack is
> what caused the termination. E.g. if an exception is thrown we unwind
> the stack, untill we realize there is no handler (not mandatory, but
> valid behavior). At the point of calling terminate we only know that
> we are in the terminate handler, not sure if we know that we found no
> exception handler, but for sure there is no information about what
> exception and where was thrown.
>

Any dump generation is implementation specific anyway. So whenever you *do*
write a dump you know more or less pretty well what to expect.
(Note: On Windows+MSVC set_terminate is basically useless as it has to be
called per thread. There it is better to catch SIGABRT and rely on the
default behaviour of terminate() that calls abort())


> The only think that I could think of, that can be safely called, is
> (....)
> I intend to teach that in my company, and I would really appreciate if
> someone could tell if what I say makes sense.
>
>
I think no general statement with regard to the usefulness of set_terminate
can be made. Logging (after setup of the appropriate logging class) should
not really pose a problem I'd say.

cheers,
Martin

--
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use
mailto:std-c++@netlab.cs.rpi.edu<std-c%2B%2B@netlab.cs.rpi.edu>
]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]





Author: restor <akrzemi1@gmail.com>
Date: Fri, 11 Jun 2010 16:35:35 CST
Raw View
> Ah. But you have to install your custom termination handler with
> set_terminate, so you can setup everything you need to do logging *before*
> calling set_terminate. Therefore you *can* rely on correctly initialized
> objects in your termination handler.

This is a very good point, and it invalidates my concern about the log
being used in terminate before it has been constructed. Still, I
wonder what happens if std::terminate is called after the logger has
been destroyed.
It can happen, that function main finished, logger destructor exited
via exception and we got into terminate. Or the logger destructor
ended successfully, but some subsequent function (registered with
std::atexit) threw an exception.
Destructors should not throw exceptions, I know, but if they never
did, there would be (almost) no need for std::terminate to exist.
Perhaps there is a way to make sure that the lifetime of my logger
never ends: create it in the free store and never delete. This would
cause a memory leak, but who cares about memory leaks when the program
is closing. Or apply the "singleton revival" as Andrei Alexandrescu
described in his book. I wonder how many logging libraries do that.

Regards,
&rzej

--
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use
mailto:std-c++@netlab.cs.rpi.edu<std-c%2B%2B@netlab.cs.rpi.edu>
]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]





Author: =?ISO-8859-1?Q?Daniel_Kr=FCgler?= <daniel.kruegler@googlemail.com>
Date: Sun, 13 Jun 2010 15:50:28 CST
Raw View
On 5 Jun., 01:43, restor <akrze...@gmail.com> wrote:
> I know how std::terminate and std::set_terminate work (at least I
> think I do). Aborting the program is a good default. But could anyone
> tell me what other implementation of a terminate handler there could
> be, that would be useful in practice?

In addition to logging and aborting, a reboot of the system
(with non-standardized means) is an often occurring idiom
in the embedded world.

HTH & Greetings from Bremen,

Daniel Kr   gler


--
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@netlab.cs.rpi.edu]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]