Re: glib\guitls.h, atexit and the windows DDK



Rick Jones writes:
 > the first complaint out of the 
 > compiler (among many :) is that in line 239 of gutils.h atexit is being 
 > redefined with different type modifiers (error C2373)

Hmm, does it help to make the declaration
 int __cdecl atexit(void (__cdecl *)(void));
instead?

 > Notepad is useless for looking at the include file (pointers on that 
 > score most welcome)

Notepad doesn't handle the Unixish line-endings of just one newline
(linefeed) character. Wordpad handles them fine, but presumably you
want some real text editor instead. (Or an IDE, even.)

 > #ifdef G_OS_WIN32
 > /* It's a bad idea to wrap atexit() on Windows. If the GLib DLL calls
 >   * atexit(), the function will be called when the GLib DLL is detached
 >   * from the program, which is not what the caller wants. The caller
 >   * wants the function to be called when it *itself* exits (or is
 >   * detached, in case the caller, too, is a DLL).
 >   */
 > int atexit (void (*)(void));
 > #define g_atexit(func) atexit(func)
 > #endif
 > 
 > am I just missing something really simple? I would have thought that 
 > would be an extern int right? 

"an" extern int? atexit() is a function.

The point here is that there is a function in GLib called g_atexit(),
that just calls atexit() with the same parameter. This is problematic
because the way atexit() calls from DLLs work: the registered function
is called when the DLL that called atexit() is detached
(unloaded). (This usually is when the program is finishing, after
main() has returned or while exit() is being called, but in the case
of DLLs loaded and unloaded programmatically at run-time, it might be
in the middle of the program's execution.)

Now, if g_atexit() was called from the application's .exe, and the
function registered also is located in the .exe, this wouln't really
matter. The function would be called when the GLib DLL is detached,
when the program is finishing, and as the function is in the .exe, it
would be certain to be present.

However, if the call to g_atexit() is in a DLL itself, and the
function being registered is in that DLL, we have a potential
problem. Let's call the DLL that calls g_atexit() FOO.DLL. Consider
what happens if FOO.DLL is detached before the GLib DLL is
detached. When the GLib DLL is detached, the atexit() function that
GLib registered which actually was located in FOO.DLL will be
called. Of course, that function pointer is then pointing into empty
space and you will get a crash.

Thus it's not really a good idea to use g_atexit() on Windows, it's
much better to call atexit() directly. That's why g_atexit() is just a
#define on Windows.

Please note that the use of atexit() in the presence of shared
libraries is not problem-free on Unix, either.

It is very hard to exatly specify what atexit() should do on modern
platforms with dynamically loaded modules. The specification for
atexit() vagualy says that the function is called "when the program
exits". But what if you call atexit() registering a function located
in a dynamically loaded module? If that module then is unloaded while
the program is still running, should the atexit() registration be
silently dropped? Should the function be called when the module it's
located in is unloaded?  Or when the module that called atexit() is
unloaded? Should a module unload be refused if there are atexit
functions registered that are located in it?

The possibilities are many, and the only thing one can be certain of
is that no two Unix implementations handle all the details in the same
way ;) This is really a can of worms. That some way of using atexit()
happens to work on one Unix platform (or on Windows) is no guarantee
that it would work as intended on some other platform.

The doc comment for g_atexit() is copied below. The wording could
still be improved.

/**
 * g_atexit:
 * @func: the function to call on normal program termination.
 * 
 * Specifies a function to be called at normal program termination.
 *
 * Since GLib 2.8.2, on Windows g_atexit() actually is a preprocessor
 * macro that maps to a call to the atexit() function in the C
 * library. This means that in case the code that calls g_atexit(),
 * i.e. atexit(), is in a DLL, the function will be called when the
 * DLL is detached from the program. This typically makes more sense
 * than that the function is called when the GLib DLL is detached,
 * which happened earlier when g_atexit() was a function in the GLib
 * DLL.
 *
 * The behaviour of atexit() in the context of dynamically loaded
 * modules is not formally specified and varies wildly.
 *
 * On POSIX systems, calling g_atexit() (or atexit()) in a dynamically
 * loaded module which is unloaded before the program terminates might
 * well cause a crash at program exit.
 *
 * Some POSIX systems implement atexit() like Windows, and have each
 * dynamically loaded module maintain an own atexit chain that is
 * called when the module is unloaded.
 *
 * On other POSIX systems, before a dynamically loaded module is
 * unloaded, the registered atexit functions (if any) residing in that
 * module are called, regardless where the code that registered them
 * resided. This is presumably the most robust approach.
 *
 * As can be seen from the above, for portability it's best to avoid
 * calling g_atexit() (or atexit()) except in the main executable of a
 * program.
 */

--tml




[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]