Recently, while helping a client debug their application, we ran into a scenario where atexit() was being used with "static" objects. Interestingly enough, the position of the atexit() routine was affecting the order of the destructor calls for their static objects. Some applications may depend on a specific order of destructor calls. So if atexit() is not used properly, it can cause unexpected runtime behavior.
Let's consider the following example:
$cat test.cpp
1 #include <iostream>
2
3 using namespace std;
4
5 class Test {
6
7 public:
8
9 ~Test() {
10
11 cout << "In Test destructor" << endl;
12 }
13
14 static Test& Instance() {
15
16 static Test theInstance;
17 return theInstance;
18 }
19 };
20
21 void cleanup() {
22
23 cout << "In cleanup function" << endl;
24 }
25
26 int main(int argc, char *argv[]) {
27
28 #ifdef CASE1
29 atexit(cleanup);
30 Test::Instance();
31 #endif
32 #ifdef CASE2
33 Test::Instance();
34 atexit(cleanup);
35 #endif
36
37 return 0;
38 }
$
For CASE1, you will see the following output at runtime:
$./a.out
In Test destructor
In cleanup function
$
and for CASE2, you will see the following output at runtime:
$./a.out
In cleanup function
In Test destructor
$
As a user, one might always expect the cleanup function call to occur first ie, where the function registered with atexit() is processed before the static object is destroyed. However, this is not the case.
According to the C++ Standard, for an object with static storage duration constructed after a function is registered with atexit(), then following the call to exit, the registered function is not called until the execution of the object’s destructor has completed (ie CASE1).
If a function is registered with atexit() then following the call to exit, any objects with static storage duration initialized prior to the registration of that function shall not be destroyed until the registered function is called from the termination process and has completed (ie CASE2).
So, next time when using the atexit() function, be sure to keep this in mind to avoid potential runtime problems!