Wrapping atexit() is a bad idea on Windows, where the EXE and each DLL
authorTor Lillqvist <tml@novell.com>
Wed, 31 Aug 2005 21:02:47 +0000 (21:02 +0000)
committerTor Lillqvist <tml@src.gnome.org>
Wed, 31 Aug 2005 21:02:47 +0000 (21:02 +0000)
2005-08-31  Tor Lillqvist  <tml@novell.com>

* glib/gutils.h: Wrapping atexit() is a bad idea on Windows, where
the EXE and each DLL have their own atexit function chains.

#define g_atexit as atexit instead. This means it has a
better chance of doing what the caller wants. For instance,
gtkhtml calls g_atexit() registering a function in gtkhtml
itself. This caused a crash when g_atexit() was implemented as a
function in the GLib DLL. The gtkhtml DLL was already unloaded by
the time the GLib DLL got unloaded.

* glib/gutils.c: #undef the #define mentioned above, to also get a
real g_atexit() into the DLL for backward compatibility. Document
the Windows behaviour of g_atexit(), and document the varying ways
atexit() can behave in the context of dynamically loaded modules
on Unix.

ChangeLog
ChangeLog.pre-2-10
ChangeLog.pre-2-12
glib/gutils.c
glib/gutils.h

index f5c482a51c7245f31bccbff1b2c6723d9ddb3a04..c6269aaed3048e72c9cc0b232854fd31b555fe26 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,21 @@
+2005-08-31  Tor Lillqvist  <tml@novell.com>
+
+       * glib/gutils.h: Wrapping atexit() is a bad idea on Windows, where
+       the EXE and each DLL have their own atexit function chains.
+
+       #define g_atexit as atexit instead. This means it has a
+       better chance of doing what the caller wants. For instance,
+       gtkhtml calls g_atexit() registering a function in gtkhtml
+       itself. This caused a crash when g_atexit() was implemented as a
+       function in the GLib DLL. The gtkhtml DLL was already unloaded by
+       the time the GLib DLL got unloaded.
+
+       * glib/gutils.c: #undef the #define mentioned above, to also get a
+       real g_atexit() into the DLL for backward compatibility. Document
+       the Windows behaviour of g_atexit(), and document the varying ways
+       atexit() can behave in the context of dynamically loaded modules
+       on Unix.
+
 2005-08-31  Matthias Clasen  <mclasen@redhat.com>
 
        * glib/glib.symbols: 
index f5c482a51c7245f31bccbff1b2c6723d9ddb3a04..c6269aaed3048e72c9cc0b232854fd31b555fe26 100644 (file)
@@ -1,3 +1,21 @@
+2005-08-31  Tor Lillqvist  <tml@novell.com>
+
+       * glib/gutils.h: Wrapping atexit() is a bad idea on Windows, where
+       the EXE and each DLL have their own atexit function chains.
+
+       #define g_atexit as atexit instead. This means it has a
+       better chance of doing what the caller wants. For instance,
+       gtkhtml calls g_atexit() registering a function in gtkhtml
+       itself. This caused a crash when g_atexit() was implemented as a
+       function in the GLib DLL. The gtkhtml DLL was already unloaded by
+       the time the GLib DLL got unloaded.
+
+       * glib/gutils.c: #undef the #define mentioned above, to also get a
+       real g_atexit() into the DLL for backward compatibility. Document
+       the Windows behaviour of g_atexit(), and document the varying ways
+       atexit() can behave in the context of dynamically loaded modules
+       on Unix.
+
 2005-08-31  Matthias Clasen  <mclasen@redhat.com>
 
        * glib/glib.symbols: 
index f5c482a51c7245f31bccbff1b2c6723d9ddb3a04..c6269aaed3048e72c9cc0b232854fd31b555fe26 100644 (file)
@@ -1,3 +1,21 @@
+2005-08-31  Tor Lillqvist  <tml@novell.com>
+
+       * glib/gutils.h: Wrapping atexit() is a bad idea on Windows, where
+       the EXE and each DLL have their own atexit function chains.
+
+       #define g_atexit as atexit instead. This means it has a
+       better chance of doing what the caller wants. For instance,
+       gtkhtml calls g_atexit() registering a function in gtkhtml
+       itself. This caused a crash when g_atexit() was implemented as a
+       function in the GLib DLL. The gtkhtml DLL was already unloaded by
+       the time the GLib DLL got unloaded.
+
+       * glib/gutils.c: #undef the #define mentioned above, to also get a
+       real g_atexit() into the DLL for backward compatibility. Document
+       the Windows behaviour of g_atexit(), and document the varying ways
+       atexit() can behave in the context of dynamically loaded modules
+       on Unix.
+
 2005-08-31  Matthias Clasen  <mclasen@redhat.com>
 
        * glib/glib.symbols: 
index 8f7adc10bbf90ed12f1517098925677a33365f6a..ced20103ed72c32bc2dcad2de3ce45487a9e0f20 100644 (file)
@@ -208,11 +208,44 @@ g_memmove (gpointer      dest,
 }
 #endif /* !HAVE_MEMMOVE && !HAVE_WORKING_BCOPY */
 
+#ifdef G_OS_WIN32
+#undef g_atexit
+#endif
+
 /**
  * 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.
  */
 void
 g_atexit (GVoidFunc func)
index 3cb781f0bb630ad6ed02595a33f64332f5effcd3..5350b9c1d1571dbecbeb62426be9e8ec0f361924 100644 (file)
@@ -229,6 +229,17 @@ typedef    void            (*GVoidFunc)            (void);
  */
 void   g_atexit                (GVoidFunc    func);
 
+#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
+
 /* Look for an executable in PATH, following execvp() rules */
 gchar*  g_find_program_in_path  (const gchar *program);