Topic: atexit and related problems


Author: Andrei Alexandrescu <andrewalex@hotmail.com>
Date: 1999/05/24
Raw View
Hi,

I've input the following code to MSVC 6 and CodeWarrior Pro 3:

struct A
{
    ~A()
    {
        static A b;
    }
};

static A a;

int main()
{
    return 0;
}

I've also moved the line that defines "a" inside the main function.
I've got the following behavior:

"a" defined outside main (as above):
MSVC - a destroyed, b not destroyed
CW - reproducible, dependable application crash

"a" defined inside main:
MSVC - a destroyed, b not destroyed
CW - a destroyed, b destroyed

This problem is closely related to atexit's behavior. The language
rules that describe the interaction between atexit and static objects
make it very natural to implement static object destruction with atexit
and very hard to implement it otherwise (3.6.3 Termination, paragraph
3). Thus all compiler vendors I know of implement destruction of static
objects by using atexit. That is, a call to each static object's
destructor is queued with atexit() exactly after the creation of the
object succeeded.

Steve Clamage has submitted an issue about atexit's behavior (see
http://anubis.dkuug.dk/jtc1/sc22/wg21/docs/lwg-issues.html#3). However,
the code above does not make explicit use of atexit. However, it does
simulate the "Atexit registration during atexit() call is not
described" problem described by Steve.

My questions are:
1. Is the code above valid?
2. If it is, does this code imply anything about atexit's behavior?
3. What can I do in order to get the correct effect on an erratic
compiler? Will I need to implement my own atexit stack and function?
4. If I do implement my own stuff, how can I get it to work correctly
given I have no special language/runtime support?
5. Can I implement it without using nifty counters?

Steve recommeds to leave the interaction between functions registered
with atexit and static objects inside those functions, undefined.
However, I am implementing Singletons. I found that a well-defined
atexit behavior is a must for implementing good Singletons. Otherwise,
I have to rely on the nifty counter and I don't like that.

I propose the following:
- Functions registered with atexit and static objects instantiated
during an atexit call should be processed (respectively destroyed)
immediately after that call returns, in the usual LIFO manner.

This would enable bulletproof Singleton implementations. One can
implement even prioritized Singletons, that is, Singletons for which
you define the order in which they are destroyed (even though they are
in different modules). The current atexit behavior prevents one from
implementing this.

The proposed atexit behavior is also easy to implement. Here's a high-
level atexit implementation. The point is that upon exit, the atexit
stack is not simply iterated from top to bottom (as most current
implementations do). It is popped from, as each call is to be performed.
I ignored exception handling for the sake of simplicity. Also please
excuse the typos I might have made.

// atexit.cpp
#include <stack>
using namespace std;

typedef void (* FuncPtr)();
// The runtime support will take care of this stack to be
// initialized before any other objects
static stack<FuncPtr> TheAtexitStack;

// also assume static objects' destruction is done
// with calls to atexit
int atexit(FuncPtr p)
{
    TheAtexitStack.push(p);
    return 1;
}

// This will be called after main() exits
void __cleanup()
{
    while (!TheAtexitStack.empty())
    {
        FuncPtr pCurrent = TheAtexitStack.top();
        TheAtexitStack.pop();
        pCurrent();
    }
}

Best,

Andrei


--== Sent via Deja.com http://www.deja.com/ ==--
---Share what you know. Learn what you don't.---


[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: clamage@eng.sun.com (Steve Clamage)
Date: 1999/05/24
Raw View
Andrei Alexandrescu <andrewalex@hotmail.com> writes:

>I've input the following code to MSVC 6 and CodeWarrior Pro 3:

>struct A
>{
>    ~A()
>    {
>        static A b;
>    }
>};

>static A a;

>int main()
>{
>    return 0;
>}

>I've also moved the line that defines "a" inside the main function.
>I've got the following behavior: [ unpredictable ]

>This problem is closely related to atexit's behavior. The language
>rules that describe the interaction between atexit and static objects
>make it very natural to implement static object destruction with atexit
>and very hard to implement it otherwise (3.6.3 Termination, paragraph
>3). Thus all compiler vendors I know of implement destruction of static
>objects by using atexit. That is, a call to each static object's
>destructor is queued with atexit() exactly after the creation of the
>object succeeded.

>Steve Clamage has submitted an issue about atexit's behavior (see
>http://anubis.dkuug.dk/jtc1/sc22/wg21/docs/lwg-issues.html#3). However,
>the code above does not make explicit use of atexit. However, it does
>simulate the "Atexit registration during atexit() call is not
>described" problem described by Steve.

>My questions are:
>1. Is the code above valid?

Yes.

>2. If it is, does this code imply anything about atexit's behavior?

No, because implementations are not required to use atexit for
controlling destruction of static objects. But it is the most
natrual way.

>3. What can I do in order to get the correct effect on an erratic
>compiler? Will I need to implement my own atexit stack and function?

Don't write code like that. :-)

>4. If I do implement my own stuff, how can I get it to work correctly
>given I have no special language/runtime support?

You can't, in general. Proper destruction order requires cooperation
between the code the compiler generates and the runtime library.
The compiler takes care of destruction of static objects, and you
can't replace or override its behavior.
>5. Can I implement it without using nifty counters?

>Steve recommeds to leave the interaction between functions registered
>with atexit and static objects inside those functions, undefined.

At the time I wrote that recommendation, the same defect existed
in the C standard. After a brief conversation with members of the
C committee, it appeared they leaned toward making the behavior
undefined, so that is what I also recommended. But the new FDIS
for C provides defined behavior: registration with atexit must
exhibit stack-like behavior. I now recommend that C++ adopt the
same rule.

>However, I am implementing Singletons. I found that a well-defined
>atexit behavior is a must for implementing good Singletons. Otherwise,
>I have to rely on the nifty counter and I don't like that.

There is no difficulty in requiring stack-like behavior for atexit.
The only question was ensuring agreement between the C and C++
standards. Although this particular item was not discussed at
the last C++ committee meeting, I believe it is safe to assume
that C++ will follow the new C rule. We can handle the change
in a Technical Corrigendum because the current standard not only
doesn't say what the behavior is, it is self-contradictory.
(A TC cannot modify the language or introduce new features. It
can only repair actual errors and ambiguities.)

--
Steve Clamage, stephen.clamage@sun.com
---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]