It's hard to pinpoint the exact problem without the actual code, but maybe you can find it yourself after reading this:
from http://www.gershnik.com/tips/cpp.asp (link now dead below)
atexit () and dynamic / shared libraries
The standard C and C ++ libraries include a sometimes useful function: atexit (). It allows the caller to register a callback that will be called when the application exits (usually). In C ++, it is also integrated with a mechanism that calls global object destructors, so things created before this atexit () call is destroyed before the callback and vice versa. All of this should be well known, and it works fine until the DLLs or shared libraries go into the image.
The problem, of course, is that dynamic libraries have their own lifetime, which in the general case can end before the main application. If the code in the DLL registers one of its functions as an atexit () callback, this callback should be called before the DLL is unloaded. Otherwise, the main application will crash or something worse. (To make unpleasant crashes during exit, it is usually difficult to debug, as many debuggers have problems with dying processes).
This problem is much better known in the context of C ++ global object destructors (which, as mentioned above, are atexit () siblings). Obviously, any C ++ implementation on a platform that supports dynamic libraries should solve this problem, and the unanimous solution was to call global destructors either when unloading the shared library or when the application exits, whichever comes first.
So far, so good, except that some implementations have forgotten to extend the same mechanism to plain old atexit (). Since the C ++ standard does not say anything about dynamic libraries, such implementations are technically “correct”, but this does not help a poor programmer who, for one reason or another, must call atexit () to pass a callback that is in the DLL.
On platforms, I know the situation is this. MSVC on Windows, GCC on Linux and Solaris, and SunPro on Solaris have the “correct” atexit (), which works the same as global destructors. However, GCC on FreeBSD at the time of this writing is “broken,” which always logs callbacks that must be executed in the application, rather than exiting the shared library. However, as promised, global destructors work fine even on FreeBSD.
What should you do in portable code? One solution, of course, is to completely eliminate atexit (). If you need its functionality, it is easy to replace it with C ++ destructors as follows.
//Code with atexit() void callback() { //do something } ... atexit(callback); ... //Equivalent code without atexit() class callback { public: ~callback() { //do something } static void register(); private: callback() {} //not implemented callback(const callback &); void operator=(const callback &); }; void callback::register() { static callback the_instance; } ... callback::register(); ...
This works due to the large typing and non-intuitive interface. It is important to note that there is no loss of functionality compared to the version of atexit (). The callback destructor cannot throw exceptions, but it also performs the functions called atexit. The callback :: register () function may not be thread safe on this platform, but it is also atexit () (the C ++ standard is currently inactive for threads, so should atexit () be implemented in a thread-safe way before implementation)
What if you want to avoid entering the text above? Usually there is a way, and it relies on a simple trick. Instead of calling broken atexit (), we need to do everything the C ++ compiler does to register global destructors. With GCC and other compilers that implement the so-called Itanium ABI (widely used for platforms without Itanium), the magic spell is called __cxa_atexit. Here's how to use it. First put the code below in some utility header
#if defined(_WIN32) || defined(LINUX) || defined(SOLARIS) #include <stdlib.h> #define SAFE_ATEXIT_ARG inline void safe_atexit(void (*p)(SAFE_ATEXIT_ARG)) { atexit(p); } #elif defined(FREEBSD) extern "C" int __cxa_atexit(void (*func) (void *), void * arg, void * dso_handle); extern "C" void * __dso_handle; #define SAFE_ATEXIT_ARG void * inline void safe_atexit(void (*p)(SAFE_ATEXIT_ARG)) { __cxa_atexit(p, 0, __dso_handle); } #endif And then use it as follows void callback(SAFE_ATEXIT_ARG) { //do something } ... safe_atexit(callback); ...
The __cxa_atexit method works as follows. It registers the callback in one global list in the same way as outside the DLL that atexit () knows. However, he also associates the other two parameters with it. The second parameter is just nice to have a thing. This allows the callback to convey some context (for example, some object), and therefore one callback can be reused for multiple cleanups. The third parameter is the one we really need. It is simply a cookie that identifies the shared library that should be associated with the callback. When any shared library is unloaded, its cleanup code is moved to the atexit callback list and calls (and deletes) any callbacks that have a cookie that matches the one associated with the unloaded library. What should be the value of cookies? This is not the start address of the DLL, and not its dlopen () handle, as you might expect. Instead, the handle is stored in the special __dso_handle global variable supported by the C ++ runtime.
The safe_atexit function must be inline. Thus, it selects everything that __dso_handle is used by the calling module, which is exactly what we need.
Should this approach be used instead of the detailed and more portable above? Probably not, although who knows what your requirements are. However, even if you never use it, it helps to realize how everything works, so it is included here.