[glib/wip/free: 1/6] Add G_DEBUG=cleanup



commit ba4362b118e97e8f14dbff629b07a14db810fc71
Author: Dan Winship <danw gnome org>
Date:   Mon Aug 27 17:59:46 2012 -0400

    Add G_DEBUG=cleanup
    
    If set, glib will do its best to free its static memory on exit.
    
    Based on a patch from Ole André Vadla Ravnås.

 gio/Makefile.am          |    2 +
 gio/gcancellable.c       |   11 +++
 gio/gdbusconnection.c    |   10 ++
 gio/gdbuserror.c         |   13 +++
 gio/gdbusprivate.c       |   22 +++++
 gio/gdbusprivate.h       |    6 ++
 gio/gio.c                |   37 ++++++++
 gio/gio.h                |    6 ++
 gio/giomodule.c          |   50 +++++++++--
 gio/gioprivate.h         |   38 ++++++++
 gio/gioscheduler.c       |   11 +++
 gio/glocalfile.c         |   11 +++
 gio/gproxyresolver.c     |   11 +++
 gio/gresolver.c          |   11 +++
 gio/gsocketconnection.c  |   11 +++
 glib-2.0.supp            |   41 +++++++++
 glib/gcharset.c          |   35 ++++++--
 glib/gconvert.c          |   52 ++++++++++--
 glib/gdataset.c          |   27 ++++++-
 glib/gdate.c             |   13 +++
 glib/gerror.c            |   15 +++-
 glib/glib-init.c         |   46 ++++++++++
 glib/glib-init.h         |   15 +++-
 glib/glib.symbols        |    1 +
 glib/gmain.c             |   28 ++++++-
 glib/gmem.h              |    1 +
 glib/grand.c             |    6 ++
 glib/gslice.c            |   37 ++++++++-
 glib/gtestutils.c        |   28 ++++++
 glib/gthread.c           |    6 ++
 glib/gthreadpool.c       |   22 +++++
 glib/gtimezone.c         |    6 ++
 glib/gutils.c            |   54 +++++++++++-
 glib/gvariant.c          |    2 +
 glib/tests/error.c       |    2 -
 glib/tests/gwakeuptest.c |   15 +++-
 glib/tests/utils.c       |    1 -
 gobject/gatomicarray.c   |   29 ++++++-
 gobject/gatomicarray.h   |    2 +
 gobject/gobject.c        |   37 +++++++-
 gobject/gobject.symbols  |    1 +
 gobject/gparam.c         |   34 +++++++
 gobject/gparam.h         |    1 +
 gobject/gparamspecs.c    |    7 ++
 gobject/gsignal.c        |   30 +++++++
 gobject/gtype-private.h  |    3 +
 gobject/gtype.c          |  214 ++++++++++++++++++++++++++++++++++++++++++++++
 gobject/gvalue.c         |    7 ++
 gthread/gthread.def      |    1 +
 49 files changed, 1019 insertions(+), 50 deletions(-)
---
diff --git a/gio/Makefile.am b/gio/Makefile.am
index 20bcd81..703696c 100644
--- a/gio/Makefile.am
+++ b/gio/Makefile.am
@@ -333,6 +333,8 @@ SUBDIRS += tests
 endif
 
 libgio_2_0_la_SOURCES =                \
+       gio.c                   \
+       gioprivate.h            \
        gappinfo.c              \
        gasynchelper.c          \
        gasynchelper.h          \
diff --git a/gio/gcancellable.c b/gio/gcancellable.c
index 0ba3a01..a1b153b 100644
--- a/gio/gcancellable.c
+++ b/gio/gcancellable.c
@@ -25,6 +25,7 @@
 #include <gioerror.h>
 #include "glib-private.h"
 #include "gcancellable.h"
+#include "gioprivate.h"
 #include "glibintl.h"
 
 
@@ -756,3 +757,13 @@ g_cancellable_source_new (GCancellable *cancellable)
 
   return source;
 }
+
+void
+_g_cancellable_deinit (void)
+{
+  if (cancellable_cond != NULL)
+    {
+      g_cond_free (cancellable_cond);
+      cancellable_cond = NULL;
+    }
+}
diff --git a/gio/gdbusconnection.c b/gio/gdbusconnection.c
index afc4ed4..a9cc83c 100644
--- a/gio/gdbusconnection.c
+++ b/gio/gdbusconnection.c
@@ -603,6 +603,16 @@ check_unclosed (GDBusConnection     *connection,
 
 static GHashTable *alive_connections = NULL;
 
+void
+_g_dbus_connection_deinit (void)
+{
+  if (alive_connections != NULL)
+    {
+      g_hash_table_unref (alive_connections);
+      alive_connections = NULL;
+    }
+}
+
 static void
 g_dbus_connection_dispose (GObject *object)
 {
diff --git a/gio/gdbuserror.c b/gio/gdbuserror.c
index f743226..b41d6b9 100644
--- a/gio/gdbuserror.c
+++ b/gio/gdbuserror.c
@@ -328,6 +328,19 @@ static GHashTable *quark_code_pair_to_re = NULL;
 /* maps from gchar* -> RegisteredError* */
 static GHashTable *dbus_error_name_to_re = NULL;
 
+void
+_g_dbus_error_deinit (void)
+{
+  if (quark_code_pair_to_re != NULL)
+    {
+      g_hash_table_unref (quark_code_pair_to_re);
+      quark_code_pair_to_re = NULL;
+
+      g_hash_table_unref (dbus_error_name_to_re);
+      dbus_error_name_to_re = NULL;
+    }
+}
+
 /**
  * g_dbus_error_register_error:
  * @error_domain: A #GQuark for a error domain.
diff --git a/gio/gdbusprivate.c b/gio/gdbusprivate.c
index a3cf9d4..886a98c 100644
--- a/gio/gdbusprivate.c
+++ b/gio/gdbusprivate.c
@@ -282,6 +282,14 @@ gdbus_shared_thread_func (gpointer user_data)
   return NULL;
 }
 
+static gboolean
+quit_main_loop (gpointer user_data)
+{
+  GMainLoop *loop = user_data;
+  g_main_loop_quit (loop);
+  return FALSE;
+}
+
 /* ---------------------------------------------------------------------------------------------------- */
 
 static SharedThreadData *
@@ -1949,6 +1957,20 @@ _g_dbus_initialize (void)
     }
 }
 
+void
+_g_dbus_deinitialize (void)
+{
+  if (shared_thread_data)
+    {
+      g_assert_cmpint (shared_thread_data->num_users, ==, 1); /* if not, there's a leak */
+      _g_dbus_shared_thread_unref ();
+    }
+
+  _g_dbus_connection_deinit ();
+
+  _g_dbus_error_deinit ();
+}
+
 /* ---------------------------------------------------------------------------------------------------- */
 
 GVariantType *
diff --git a/gio/gdbusprivate.h b/gio/gdbusprivate.h
index 1ac7754..912c8f9 100644
--- a/gio/gdbusprivate.h
+++ b/gio/gdbusprivate.h
@@ -84,6 +84,7 @@ void         _g_dbus_worker_close        (GDBusWorker         *worker,
 /* ---------------------------------------------------------------------------------------------------- */
 
 void _g_dbus_initialize (void);
+void _g_dbus_deinitialize (void);
 gboolean _g_dbus_debug_authentication (void);
 gboolean _g_dbus_debug_transport (void);
 gboolean _g_dbus_debug_message (void);
@@ -109,6 +110,11 @@ gchar *_g_dbus_hexdump (const gchar *data, gsize len, guint indent);
 
 /* ---------------------------------------------------------------------------------------------------- */
 
+void _g_dbus_connection_deinit (void);
+void _g_dbus_error_deinit (void);
+
+/* ---------------------------------------------------------------------------------------------------- */
+
 #ifdef G_OS_WIN32
 gchar *_g_dbus_win32_get_user_sid (void);
 #endif
diff --git a/gio/gio.c b/gio/gio.c
new file mode 100644
index 0000000..c4eff03
--- /dev/null
+++ b/gio/gio.c
@@ -0,0 +1,37 @@
+/* GIO - GLib Input, Output and Streaming Library
+ *
+ * Copyright (C) 2010 Ole André Vadla Ravnås <oleavr gmail com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "gio.h"
+
+#include "gdbusprivate.h"
+#include "gioprivate.h"
+
+void
+g_io_deinit (void)
+{
+  _g_dbus_deinitialize ();
+  _g_local_file_deinit ();
+  _g_socket_connection_factory_deinit ();
+  _g_io_module_deinit ();
+  _g_cancellable_deinit ();
+  _g_io_scheduler_deinit ();
+  _g_proxy_resolver_deinit ();
+  _g_resolver_deinit ();
+}
diff --git a/gio/gio.h b/gio/gio.h
index 3fb914d..9013a82 100644
--- a/gio/gio.h
+++ b/gio/gio.h
@@ -158,5 +158,11 @@
 
 #undef __GIO_GIO_H_INSIDE__
 
+G_BEGIN_DECLS
+
+void g_io_deinit (void);
+
+G_END_DECLS
+
 #endif /* __G_IO_H__ */
 
diff --git a/gio/giomodule.c b/gio/giomodule.c
index 37a9e70..37a64e3 100644
--- a/gio/giomodule.c
+++ b/gio/giomodule.c
@@ -803,16 +803,6 @@ _g_io_win32_get_module (void)
 
 #endif
 
-#undef GIO_MODULE_DIR
-
-/* GIO_MODULE_DIR is used only in code called just once,
- * so no problem leaking this
- */
-#define GIO_MODULE_DIR \
-  g_build_filename (g_win32_get_package_installation_directory_of_module (gio_dll), \
-                   "lib/gio/modules", \
-                   NULL)
-
 #endif
 
 void
@@ -870,6 +860,24 @@ _g_io_modules_ensure_extension_points_registered (void)
   G_UNLOCK (registered_extensions);
 }
 
+static gchar *
+g_io_get_module_dir (void)
+{
+  gchar *module_dir;
+
+#if defined (G_PLATFORM_WIN32)
+  gchar *install_dir;
+
+  install_dir = g_win32_get_package_installation_directory_of_module (gio_dll);
+  module_dir = g_build_filename (install_dir, "lib/gio/modules", NULL);
+  g_free (install_dir);
+#else
+  module_dir = g_strdup (GIO_MODULE_DIR);
+#endif
+
+  return module_dir;
+}
+
 void
 _g_io_modules_ensure_loaded (void)
 {
@@ -948,10 +956,32 @@ _g_io_modules_ensure_loaded (void)
   G_UNLOCK (loaded_dirs);
 }
 
+void
+_g_io_module_deinit (void)
+{
+  if (extension_points != NULL)
+    {
+      g_hash_table_unref (extension_points);
+      extension_points = NULL;
+    }
+}
+
 static void
 g_io_extension_point_free (GIOExtensionPoint *ep)
 {
+  GList *walk;
+
   g_free (ep->name);
+
+  for (walk = ep->extensions; walk != NULL; walk = walk->next)
+    {
+      GIOExtension *extension = walk->data;
+
+      g_free (extension->name);
+      g_slice_free (GIOExtension, extension);
+    }
+  g_list_free (ep->extensions);
+
   g_free (ep);
 }
 
diff --git a/gio/gioprivate.h b/gio/gioprivate.h
new file mode 100644
index 0000000..490568c
--- /dev/null
+++ b/gio/gioprivate.h
@@ -0,0 +1,38 @@
+/* GIO - GLib Input, Output and Streaming Library
+ *
+ * Copyright (C) 2010 Ole André Vadla Ravnås <oleavr gmail com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __G_IO_PRIVATE_H__
+#define __G_IO_PRIVATE_H__
+
+#include <gio/giotypes.h>
+
+G_BEGIN_DECLS
+
+G_GNUC_INTERNAL void _g_cancellable_deinit (void);
+G_GNUC_INTERNAL void _g_io_module_deinit (void);
+G_GNUC_INTERNAL void _g_io_scheduler_deinit (void);
+G_GNUC_INTERNAL void _g_local_file_deinit (void);
+G_GNUC_INTERNAL void _g_proxy_resolver_deinit (void);
+G_GNUC_INTERNAL void _g_resolver_deinit (void);
+G_GNUC_INTERNAL void _g_socket_connection_factory_deinit (void);
+
+G_END_DECLS
+
+#endif
diff --git a/gio/gioscheduler.c b/gio/gioscheduler.c
index f63996e..c90934e 100644
--- a/gio/gioscheduler.c
+++ b/gio/gioscheduler.c
@@ -23,6 +23,7 @@
 #include "config.h"
 
 #include "gioscheduler.h"
+#include "gioprivate.h"
 #include "gcancellable.h"
 
 
@@ -389,3 +390,13 @@ g_io_scheduler_job_send_to_mainloop_async (GIOSchedulerJob *job,
   g_source_attach (source, job->context);
   g_source_unref (source);
 }
+
+void
+_g_io_scheduler_deinit (void)
+{
+  if (job_thread_pool)
+    {
+      g_thread_pool_free (job_thread_pool, FALSE, FALSE);
+      job_thread_pool = NULL;
+    }
+}
diff --git a/gio/glocalfile.c b/gio/glocalfile.c
index 0c54727..faa0654 100644
--- a/gio/glocalfile.c
+++ b/gio/glocalfile.c
@@ -62,6 +62,7 @@
 #include "gmountprivate.h"
 #include "gunixmounts.h"
 #include "gioerror.h"
+#include "gioprivate.h"
 #include <glib/gstdio.h>
 #include "glibintl.h"
 
@@ -180,6 +181,16 @@ g_local_file_class_init (GLocalFileClass *klass)
   local_writable_attributes = list;
 }
 
+void
+_g_local_file_deinit (void)
+{
+  if (local_writable_attributes != NULL)
+    {
+      g_file_attribute_info_list_unref (local_writable_attributes);
+      local_writable_attributes = NULL;
+    }
+}
+
 static void
 g_local_file_init (GLocalFile *local)
 {
diff --git a/gio/gproxyresolver.c b/gio/gproxyresolver.c
index db2e475..d4ca7b3 100644
--- a/gio/gproxyresolver.c
+++ b/gio/gproxyresolver.c
@@ -31,6 +31,7 @@
 #include "gcancellable.h"
 #include "giomodule.h"
 #include "giomodule-priv.h"
+#include "gioprivate.h"
 #include "gsimpleasyncresult.h"
 
 /**
@@ -195,3 +196,13 @@ g_proxy_resolver_lookup_finish (GProxyResolver     *resolver,
 
   return (* iface->lookup_finish) (resolver, result, error);
 }
+
+void
+_g_proxy_resolver_deinit (void)
+{
+  if (default_proxy_resolver)
+  {
+    g_object_unref (default_proxy_resolver);
+    default_proxy_resolver = NULL;
+  }
+}
diff --git a/gio/gresolver.c b/gio/gresolver.c
index 1785543..0eaba57 100644
--- a/gio/gresolver.c
+++ b/gio/gresolver.c
@@ -25,6 +25,7 @@
 #include "glibintl.h"
 
 #include "gresolver.h"
+#include "gioprivate.h"
 #include "gnetworkingprivate.h"
 #include "gasyncresult.h"
 #include "ginetaddress.h"
@@ -1423,3 +1424,13 @@ _g_resolver_records_from_DnsQuery (const gchar  *rrname,
 }
 
 #endif
+
+void
+_g_resolver_deinit (void)
+{
+  if (default_resolver)
+    {
+      g_object_unref (default_resolver);
+      default_resolver = NULL;
+    }
+}
diff --git a/gio/gsocketconnection.c b/gio/gsocketconnection.c
index a881a47..f9bf5c8 100644
--- a/gio/gsocketconnection.c
+++ b/gio/gsocketconnection.c
@@ -29,6 +29,7 @@
 
 #include "gsocketconnection.h"
 
+#include "gioprivate.h"
 #include "gsocketoutputstream.h"
 #include "gsocketinputstream.h"
 #include <gio/giostream.h>
@@ -555,6 +556,16 @@ connection_factory_equal (gconstpointer _a,
 static GHashTable *connection_factories = NULL;
 G_LOCK_DEFINE_STATIC(connection_factories);
 
+void
+_g_socket_connection_factory_deinit (void)
+{
+  if (connection_factories != NULL)
+    {
+      g_hash_table_unref (connection_factories);
+      connection_factories = NULL;
+    }
+}
+
 /**
  * g_socket_connection_factory_register_type:
  * @g_type: a #GType, inheriting from %G_TYPE_SOCKET_CONNECTION
diff --git a/glib-2.0.supp b/glib-2.0.supp
new file mode 100644
index 0000000..23df02a
--- /dev/null
+++ b/glib-2.0.supp
@@ -0,0 +1,41 @@
+# valgrind suppressions
+
+{
+   static GCond
+   Memcheck:Leak
+   fun:malloc
+   fun:g_cond_impl_new
+   fun:g_cond_get_impl
+}
+
+{
+   static GMutex
+   Memcheck:Leak
+   fun:malloc
+   fun:g_mutex_impl_new
+   fun:g_mutex_get_impl
+}
+
+{
+   static GPrivate
+   Memcheck:Leak
+   fun:malloc
+   fun:g_private_impl_new
+   fun:g_private_get_impl
+}
+
+{
+   static GRecMutex
+   Memcheck:Leak
+   fun:g_slice_new
+   fun:g_rec_mutex_impl_new
+   fun:g_rec_mutex_get_impl
+}
+
+{
+   static GRWLock
+   Memcheck:Leak
+   fun:malloc
+   fun:g_rw_lock_impl_new
+   fun:g_rw_lock_get_impl
+}
diff --git a/glib/gcharset.c b/glib/gcharset.c
index 4f52ab4..9326773 100644
--- a/glib/gcharset.c
+++ b/glib/gcharset.c
@@ -38,18 +38,19 @@
 #include <stdio.h>
 
 G_LOCK_DEFINE_STATIC (aliases);
+static GHashTable *alias_hash = NULL;
 
 static GHashTable *
 get_alias_hash (void)
 {
-  static GHashTable *alias_hash = NULL;
   const char *aliases;
 
   G_LOCK (aliases);
 
   if (!alias_hash)
     {
-      alias_hash = g_hash_table_new (g_str_hash, g_str_equal);
+      alias_hash = g_hash_table_new_full (g_str_hash, g_str_equal,
+                                          NULL, g_free);
 
       aliases = _g_locale_get_charset_aliases ();
       while (*aliases != '\0')
@@ -152,6 +153,8 @@ charset_cache_free (gpointer data)
   g_free (cache);
 }
 
+static GPrivate charset_cache_private = G_PRIVATE_INIT (charset_cache_free);
+
 /**
  * g_get_charset:
  * @charset: return location for character set name
@@ -179,14 +182,13 @@ charset_cache_free (gpointer data)
 gboolean
 g_get_charset (const char **charset)
 {
-  static GPrivate cache_private = G_PRIVATE_INIT (charset_cache_free);
-  GCharsetCache *cache = g_private_get (&cache_private);
+  GCharsetCache *cache = g_private_get (&charset_cache_private);
   const gchar *raw;
 
   if (!cache)
     {
       cache = g_new0 (GCharsetCache, 1);
-      g_private_set (&cache_private, cache);
+      g_private_set (&charset_cache_private, cache);
     }
 
   G_LOCK (aliases);
@@ -240,7 +242,10 @@ read_aliases (gchar *file)
   char buf[256];
 
   if (!alias_table)
-    alias_table = g_hash_table_new (g_str_hash, g_str_equal);
+    {
+      alias_table = g_hash_table_new_full (g_str_hash, g_str_equal,
+                                           g_free, g_free);
+    }
   fp = fopen (file,"r");
   if (!fp)
     return;
@@ -530,6 +535,8 @@ language_names_cache_free (gpointer data)
   g_free (cache);
 }
 
+static GPrivate langnames_cache_private = G_PRIVATE_INIT (language_names_cache_free);
+
 /**
  * g_get_language_names:
  *
@@ -553,14 +560,13 @@ language_names_cache_free (gpointer data)
 const gchar * const *
 g_get_language_names (void)
 {
-  static GPrivate cache_private = G_PRIVATE_INIT (language_names_cache_free);
-  GLanguageNamesCache *cache = g_private_get (&cache_private);
+  GLanguageNamesCache *cache = g_private_get (&langnames_cache_private);
   const gchar *value;
 
   if (!cache)
     {
       cache = g_new0 (GLanguageNamesCache, 1);
-      g_private_set (&cache_private, cache);
+      g_private_set (&langnames_cache_private, cache);
     }
 
   value = guess_category_value ("LC_MESSAGES");
@@ -590,3 +596,14 @@ g_get_language_names (void)
 
   return (const gchar * const *) cache->language_names;
 }
+
+void
+g_charset_cleanup (void)
+{
+  g_clear_pointer (&alias_hash, g_hash_table_unref);
+#ifndef G_OS_WIN32
+  g_clear_pointer (&alias_table, g_hash_table_unref);
+#endif
+  g_private_replace (&charset_cache_private, NULL);
+  g_private_replace (&langnames_cache_private, NULL);
+}
diff --git a/glib/gconvert.c b/glib/gconvert.c
index dded367..256eff1 100644
--- a/glib/gconvert.c
+++ b/glib/gconvert.c
@@ -349,6 +349,10 @@ struct _iconv_cache_bucket {
   GIConv cd;
 };
 
+static void iconv_cache_bucket_expire (GList *node,
+                                       struct _iconv_cache_bucket *bucket);
+
+static gboolean iconv_cache_initialized = FALSE;
 static GList *iconv_cache_list;
 static GHashTable *iconv_cache;
 static GHashTable *iconv_open_hash;
@@ -359,18 +363,42 @@ G_LOCK_DEFINE_STATIC (iconv_cache_lock);
 static void
 iconv_cache_init (void)
 {
-  static gboolean initialized = FALSE;
-  
-  if (initialized)
+  if (iconv_cache_initialized)
     return;
   
   iconv_cache_list = NULL;
   iconv_cache = g_hash_table_new (g_str_hash, g_str_equal);
   iconv_open_hash = g_hash_table_new (g_direct_hash, g_direct_equal);
   
-  initialized = TRUE;
+  iconv_cache_initialized = TRUE;
 }
 
+static void
+iconv_cache_deinit (void)
+{
+  struct _iconv_cache_bucket *bucket;
+  GList *node, *next;
+
+  if (!iconv_cache_initialized)
+    return;
+
+  node = iconv_cache_list;
+  while (node)
+    {
+      next = node->next;
+
+      bucket = node->data;
+      iconv_cache_bucket_expire (node, bucket);
+
+      node = next;
+    }
+  g_assert (iconv_cache_list == NULL);
+
+  g_clear_pointer (&iconv_cache, g_hash_table_unref);
+  g_clear_pointer (&iconv_open_cache, g_hash_table_unref);
+
+  iconv_cache_initialized = FALSE;
+}
 
 /*
  * iconv_cache_bucket_new:
@@ -1281,6 +1309,8 @@ filename_charset_cache_free (gpointer data)
   g_free (cache);
 }
 
+static GPrivate filename_cache_private = G_PRIVATE_INIT (filename_charset_cache_free);
+
 /**
  * g_get_filename_charsets:
  * @charsets: return location for the %NULL-terminated list of encoding names
@@ -1318,14 +1348,13 @@ filename_charset_cache_free (gpointer data)
 gboolean
 g_get_filename_charsets (const gchar ***filename_charsets)
 {
-  static GPrivate cache_private = G_PRIVATE_INIT (filename_charset_cache_free);
-  GFilenameCharsetCache *cache = g_private_get (&cache_private);
+  GFilenameCharsetCache *cache = g_private_get (&filename_cache_private);
   const gchar *charset;
 
   if (!cache)
     {
       cache = g_new0 (GFilenameCharsetCache, 1);
-      g_private_set (&cache_private, cache);
+      g_private_set (&filename_cache_private, cache);
     }
 
   g_get_charset (&charset);
@@ -2248,3 +2277,12 @@ g_filename_display_name (const gchar *filename)
 
   return display_name;
 }
+
+void
+g_convert_cleanup (void)
+{
+#ifdef NEED_ICONV_CACHE
+  iconv_cache_deinit ();
+#endif
+  g_private_replace (&filename_cache_private, NULL);
+}
diff --git a/glib/gdataset.c b/glib/gdataset.c
index 80f5094..83200c2 100644
--- a/glib/gdataset.c
+++ b/glib/gdataset.c
@@ -1070,6 +1070,13 @@ g_data_initialize (void)
   g_dataset_cached = NULL;
 }
 
+void
+g_dataset_cleanup (void)
+{
+  g_clear_pointer (&g_dataset_location_ht, g_hash_table_unref);
+  g_dataset_cached = NULL;
+}
+
 /**
  * SECTION:quarks
  * @title: Quarks
@@ -1139,6 +1146,7 @@ g_quark_try_string (const gchar *string)
 #define QUARK_STRING_BLOCK_SIZE (4096 - sizeof (gsize))
 static char *quark_block = NULL;
 static int quark_block_offset = 0;
+static GSList *quark_blocks = NULL;
 
 /* HOLDS: g_quark_global_lock */
 static char *
@@ -1152,13 +1160,20 @@ quark_strdup(const gchar *string)
   /* For strings longer than half the block size, fall back
      to strdup so that we fill our blocks at least 50%. */
   if (len > QUARK_STRING_BLOCK_SIZE / 2)
-    return g_strdup (string);
+    {
+      copy = g_strdup (string);
+      if (G_UNLIKELY (g_mem_do_cleanup))
+        quark_blocks = g_slist_prepend (quark_blocks, copy);
+      return copy;
+    }
 
   if (quark_block == NULL ||
       QUARK_STRING_BLOCK_SIZE - quark_block_offset < len)
     {
       quark_block = g_malloc (QUARK_STRING_BLOCK_SIZE);
       quark_block_offset = 0;
+      if (G_UNLIKELY (g_mem_do_cleanup))
+        quark_blocks = g_slist_prepend (quark_blocks, quark_block);
     }
 
   copy = quark_block + quark_block_offset;
@@ -1303,6 +1318,16 @@ g_quark_new (gchar *string)
   return quark;
 }
 
+void
+g_quark_cleanup (void)
+{
+  g_clear_pointer (&g_quark_ht, g_hash_table_unref);
+  g_clear_pointer (&g_quarks, g_free);
+
+  g_slist_free_full (quark_blocks, g_free);
+  quark_blocks = NULL;
+}
+
 /**
  * g_intern_string:
  * @string: (allow-none): a string
diff --git a/glib/gdate.c b/glib/gdate.c
index d9b25f3..a734a4b 100644
--- a/glib/gdate.c
+++ b/glib/gdate.c
@@ -2549,3 +2549,16 @@ g_date_strftime (gchar       *s,
   return retval;
 #endif
 }
+
+void
+g_date_cleanup (void)
+{
+  int i;
+
+  for (i = 1; i <= 12; i++)
+    {
+      g_clear_pointer (&short_month_names[i], g_free);
+      g_clear_pointer (&long_month_names[i], g_free);
+    }
+  g_clear_pointer (&current_locale, g_free);
+}
diff --git a/glib/gerror.c b/glib/gerror.c
index e6ce3c0..65dd416 100644
--- a/glib/gerror.c
+++ b/glib/gerror.c
@@ -563,7 +563,10 @@ g_set_error (GError      **err,
   if (*err == NULL)
     *err = new;
   else
-    g_warning (ERROR_OVERWRITTEN_WARNING, new->message); 
+    {
+      g_warning (ERROR_OVERWRITTEN_WARNING, new->message);
+      g_error_free (new);
+    }
 }
 
 /**
@@ -596,7 +599,10 @@ g_set_error_literal (GError      **err,
   if (*err == NULL)
     *err = new;
   else
-    g_warning (ERROR_OVERWRITTEN_WARNING, new->message); 
+    {
+      g_warning (ERROR_OVERWRITTEN_WARNING, new->message);
+      g_error_free (new);
+    }
 }
 
 /**
@@ -622,7 +628,10 @@ g_propagate_error (GError **dest,
   else
     {
       if (*dest != NULL)
-        g_warning (ERROR_OVERWRITTEN_WARNING, src->message);
+        {
+          g_warning (ERROR_OVERWRITTEN_WARNING, src->message);
+          g_error_free (src);
+        }
       else
         *dest = src;
     }
diff --git a/glib/glib-init.c b/glib/glib-init.c
index f4edd5c..cb8a0dd 100644
--- a/glib/glib-init.c
+++ b/glib/glib-init.c
@@ -42,6 +42,17 @@ gboolean g_mem_gc_friendly = TRUE;
 #else
 gboolean g_mem_gc_friendly = FALSE;
 #endif
+
+/**
+ * g_mem_do_cleanup:
+ *
+ * This variable is %TRUE if the <envar>G_DEBUG</envar> environment
+ * variable includes the key <literal>cleanup</literal>. This will
+ * cause glib to free as much data as possible before exiting, to help
+ * with finding real memory leaks.
+ */
+gboolean g_mem_do_cleanup = FALSE;
+
 GLogLevelFlags g_log_msg_prefix = G_LOG_LEVEL_ERROR | G_LOG_LEVEL_WARNING |
                                   G_LOG_LEVEL_CRITICAL | G_LOG_LEVEL_DEBUG;
 GLogLevelFlags g_log_always_fatal = G_LOG_FATAL_MASK;
@@ -202,6 +213,7 @@ g_debug_init (void)
 {
   const GDebugKey keys[] = {
     { "gc-friendly", 1 },
+    { "cleanup", 2 },
     {"fatal-warnings",  G_LOG_LEVEL_WARNING | G_LOG_LEVEL_CRITICAL },
     {"fatal-criticals", G_LOG_LEVEL_CRITICAL }
   };
@@ -212,6 +224,7 @@ g_debug_init (void)
   g_log_always_fatal |= flags & G_LOG_LEVEL_MASK;
 
   g_mem_gc_friendly = flags & 1;
+  g_mem_do_cleanup = flags & 2;
 }
 
 static void
@@ -221,6 +234,25 @@ glib_init (void)
   g_debug_init ();
 }
 
+static void
+glib_cleanup (void)
+{
+  g_test_cleanup ();
+
+  g_charset_cleanup ();
+  g_convert_cleanup ();
+  g_dataset_cleanup ();
+  g_date_cleanup ();
+  g_main_cleanup ();
+  g_quark_cleanup ();
+  g_rand_cleanup ();
+  g_time_zone_cleanup ();
+  g_utils_cleanup ();
+
+  g_threading_cleanup ();
+  g_slice_cleanup ();
+}
+
 #if defined (G_OS_WIN32)
 
 HMODULE glib_dll;
@@ -241,6 +273,8 @@ DllMain (HINSTANCE hinstDLL,
 
     case DLL_THREAD_DETACH:
       g_thread_win32_thread_detach ();
+      if (G_UNLIKELY (g_mem_do_cleanup))
+        glib_cleanup ();
       break;
 
     default:
@@ -264,6 +298,18 @@ glib_init_ctor (void)
   glib_init ();
 }
 
+#ifdef G_DEFINE_CONSTRUCTOR_NEEDS_PRAGMA
+#pragma G_DEFINE_CONSTRUCTOR_PRAGMA_ARGS(glib_init_dtor)
+#endif
+G_DEFINE_DESTRUCTOR(glib_init_dtor)
+
+static void
+glib_init_dtor (void)
+{
+  if (G_UNLIKELY (g_mem_do_cleanup))
+    glib_cleanup ();
+}
+
 #else
 # error Your platform/compiler is missing constructor support
 #endif
diff --git a/glib/glib-init.h b/glib/glib-init.h
index bf1ecd0..b471f87 100644
--- a/glib/glib-init.h
+++ b/glib/glib-init.h
@@ -26,7 +26,6 @@
 
 G_GNUC_INTERNAL extern GLogLevelFlags g_log_always_fatal;
 G_GNUC_INTERNAL extern GLogLevelFlags g_log_msg_prefix;
-GLIB_VAR gboolean g_mem_gc_friendly;
 
 #ifdef G_OS_WIN32
 #include <windows.h>
@@ -37,4 +36,18 @@ G_GNUC_INTERNAL void g_clock_win32_init (void);
 G_GNUC_INTERNAL extern HMODULE glib_dll;
 #endif
 
+/* For G_DEBUG=cleanup */
+G_GNUC_INTERNAL void g_charset_cleanup (void);
+G_GNUC_INTERNAL void g_convert_cleanup (void);
+G_GNUC_INTERNAL void g_dataset_cleanup (void);
+G_GNUC_INTERNAL void g_date_cleanup (void);
+G_GNUC_INTERNAL void g_main_cleanup (void);
+G_GNUC_INTERNAL void g_quark_cleanup (void);
+G_GNUC_INTERNAL void g_rand_cleanup (void);
+G_GNUC_INTERNAL void g_slice_cleanup (void);
+G_GNUC_INTERNAL void g_test_cleanup (void);
+G_GNUC_INTERNAL void g_threading_cleanup (void);
+G_GNUC_INTERNAL void g_time_zone_cleanup (void);
+G_GNUC_INTERNAL void g_utils_cleanup (void);
+
 #endif /* __GLIB_INIT_H__ */
diff --git a/glib/glib.symbols b/glib/glib.symbols
index ae2ad73..f1b5890 100644
--- a/glib/glib.symbols
+++ b/glib/glib.symbols
@@ -1642,6 +1642,7 @@ glib_mem_profiler_table
 glib_micro_version
 glib_minor_version
 glib_on_error_halt
+g_mem_do_cleanup
 g_mem_gc_friendly
 g_cond_broadcast
 g_cond_clear
diff --git a/glib/gmain.c b/glib/gmain.c
index c0c4581..de943ff 100644
--- a/glib/gmain.c
+++ b/glib/gmain.c
@@ -2359,10 +2359,11 @@ g_main_dispatch_free (gpointer dispatch)
 
 /* Running the main loop */
 
+static GPrivate depth_private = G_PRIVATE_INIT (g_main_dispatch_free);
+
 static GMainDispatch *
 get_dispatch (void)
 {
-  static GPrivate depth_private = G_PRIVATE_INIT (g_main_dispatch_free);
   GMainDispatch *dispatch;
 
   dispatch = g_private_get (&depth_private);
@@ -5020,10 +5021,13 @@ g_main_context_invoke_full (GMainContext   *context,
     }
 }
 
+static GThread *glib_worker_thread;
+static volatile gboolean glib_worker_running;
+
 static gpointer
 glib_worker_main (gpointer data)
 {
-  while (TRUE)
+  while (glib_worker_running)
     {
       g_main_context_iteration (glib_worker_context, TRUE);
 
@@ -5052,7 +5056,8 @@ g_get_worker_context (void)
       pthread_sigmask (SIG_SETMASK, &all, &prev_mask);
 #endif
       glib_worker_context = g_main_context_new ();
-      g_thread_new ("gmain", glib_worker_main, NULL);
+      glib_worker_running = TRUE;
+      glib_worker_thread = g_thread_new ("gmain", glib_worker_main, NULL);
 #ifdef G_OS_UNIX
       pthread_sigmask (SIG_SETMASK, &prev_mask, NULL);
 #endif
@@ -5061,3 +5066,20 @@ g_get_worker_context (void)
 
   return glib_worker_context;
 }
+
+void
+g_main_cleanup (void)
+{
+  if (glib_worker_running)
+    {
+      glib_worker_running = FALSE;
+      g_main_context_wakeup (glib_worker_context);
+      g_thread_join (glib_worker_thread);
+
+      g_clear_pointer (&glib_worker_context, g_main_context_unref);
+      glib_worker_thread = NULL;
+    }
+
+  g_clear_pointer (&default_main_context, g_main_context_unref);
+  g_private_replace (&depth_private, NULL);
+}
diff --git a/glib/gmem.h b/glib/gmem.h
index c50f46e..f7eecc1 100644
--- a/glib/gmem.h
+++ b/glib/gmem.h
@@ -274,6 +274,7 @@ void         g_mem_set_vtable (GMemVTable   *vtable);
 gboolean g_mem_is_system_malloc (void);
 
 GLIB_VAR gboolean g_mem_gc_friendly;
+GLIB_VAR gboolean g_mem_do_cleanup;
 
 /* Memory profiler and checker, has to be enabled via g_mem_set_vtable()
  */
diff --git a/glib/grand.c b/glib/grand.c
index 4d6a0a6..f193253 100644
--- a/glib/grand.c
+++ b/glib/grand.c
@@ -153,6 +153,12 @@ get_random_version (void)
   return random_version;
 }
 
+void
+g_rand_cleanup (void)
+{
+  g_clear_pointer (&global_random, g_rand_free);
+}
+
 struct _GRand
 {
   guint32 mt[N]; /* the array for the state vector  */
diff --git a/glib/gslice.c b/glib/gslice.c
index b70724d..a102b67 100644
--- a/glib/gslice.c
+++ b/glib/gslice.c
@@ -494,13 +494,14 @@ g_mutex_lock_a (GMutex *mutex,
     }
 }
 
+static GMutex init_mutex;
+
 static inline ThreadMemory*
 thread_memory_from_self (void)
 {
   ThreadMemory *tmem = g_private_get (&private_thread_memory);
   if (G_UNLIKELY (!tmem))
     {
-      static GMutex init_mutex;
       guint n_magazines;
 
       g_mutex_lock (&init_mutex);
@@ -1368,6 +1369,8 @@ slab_allocator_free_chunk (gsize    chunk_size,
  */
 
 #if !(HAVE_COMPLIANT_POSIX_MEMALIGN || HAVE_MEMALIGN || HAVE_VALLOC)
+static gpointer    *compat_allocations = NULL;
+static guint        compat_n_allocations = 0;
 static GTrashStack *compat_valloc_trash = NULL;
 #endif
 
@@ -1396,6 +1399,12 @@ allocator_memalign (gsize alignment,
       const guint n_pages = 16;
       guint8 *mem = malloc (n_pages * sys_page_size);
       err = errno;
+
+      compat_n_allocations++;
+      compat_allocations = realloc (compat_allocations,
+          compat_n_allocations * sizeof (gpointer));
+      compat_allocations[compat_n_allocations - 1] = mem;
+
       if (mem)
         {
           gint i = n_pages;
@@ -1716,3 +1725,29 @@ g_slice_debug_tree_statistics (void)
    */
 }
 #endif /* G_ENABLE_DEBUG */
+
+void
+g_slice_cleanup (void)
+{
+  g_private_replace (&private_thread_memory, NULL);
+
+#if HAVE_COMPLIANT_POSIX_MEMALIGN || HAVE_MEMALIGN || HAVE_VALLOC
+  /* no need to clean up slabs, they go away when the last chunk is freed */
+#else
+  int i;
+
+  for (i = 0; i < compat_n_allocations; i++)
+    free (compat_allocations[i]);
+  free (compat_allocations);
+  compat_allocations = NULL;
+  compat_n_allocations = 0;
+  compat_valloc_trash = NULL;
+#endif
+
+  g_clear_pointer (&allocator->contention_counters, g_free);
+  g_clear_pointer (&allocator->magazines, g_free);
+  g_clear_pointer (&allocator->slab_stack, g_free);
+
+  g_mutex_clear (&allocator->magazine_mutex);
+  g_mutex_clear (&allocator->slab_mutex);
+}
diff --git a/glib/gtestutils.c b/glib/gtestutils.c
index e4b4331..d6f6a7c 100644
--- a/glib/gtestutils.c
+++ b/glib/gtestutils.c
@@ -481,6 +481,7 @@ struct DestroyEntry
 
 /* --- prototypes --- */
 static void     test_run_seed                   (const gchar *rseed);
+static void     g_test_destroy_suite            (GTestSuite *suite);
 static void     test_trap_clear                 (void);
 static guint8*  g_test_log_dump                 (GTestLogMsg *msg,
                                                  guint       *len);
@@ -958,6 +959,15 @@ g_test_init (int    *argc,
   g_test_log (G_TEST_LOG_START_BINARY, g_get_prgname(), test_run_seedstr, 0, NULL);
 }
 
+void
+g_test_cleanup (void)
+{
+  g_clear_pointer (&test_run_rand, g_rand_free);
+  g_clear_pointer (&test_user_timer, g_timer_destroy);
+  g_clear_pointer (&test_suite_root, g_test_destroy_suite);
+  g_clear_pointer (&test_uri_base, g_free);
+}
+
 static void
 test_run_seed (const gchar *rseed)
 {
@@ -1361,6 +1371,13 @@ g_test_create_case (const char       *test_name,
   return tc;
 }
 
+static void
+g_test_destroy_case (GTestCase *tc)
+{
+  g_free (tc->name);
+  g_slice_free (GTestCase, tc);
+}
+
 /**
  * GTestFixtureFunc:
  * @fixture: the test fixture
@@ -1563,6 +1580,17 @@ g_test_create_suite (const char *suite_name)
   return ts;
 }
 
+static void
+g_test_destroy_suite (GTestSuite *suite)
+{
+  g_free (suite->name);
+
+  g_slist_free_full (suite->suites, (GDestroyNotify)g_test_destroy_suite);
+  g_slist_free_full (suite->cases, (GDestroyNotify)g_test_destroy_case);
+
+  g_slice_free (GTestSuite, suite);
+}
+
 /**
  * g_test_suite_add:
  * @suite: a #GTestSuite
diff --git a/glib/gthread.c b/glib/gthread.c
index c1d06ec..651388d 100644
--- a/glib/gthread.c
+++ b/glib/gthread.c
@@ -1010,5 +1010,11 @@ g_thread_self (void)
   return (GThread*) thread;
 }
 
+void
+g_threading_cleanup (void)
+{
+  g_private_replace (&g_thread_specific_private, NULL);
+}
+
 /* Epilogue {{{1 */
 /* vim: set foldmethod=marker: */
diff --git a/glib/gthreadpool.c b/glib/gthreadpool.c
index 78684ab..0836ff8 100644
--- a/glib/gthreadpool.c
+++ b/glib/gthreadpool.c
@@ -107,6 +107,8 @@ struct _GRealThreadPool
 static const gpointer wakeup_thread_marker = (gpointer) &g_thread_pool_new;
 static gint wakeup_thread_serial = 0;
 
+static gint alive_threads = 0;
+
 /* Here all unused threads are waiting  */
 static GAsyncQueue *unused_thread_queue = NULL;
 static gint unused_threads = 0;
@@ -378,6 +380,8 @@ g_thread_pool_thread_proxy (gpointer data)
         }
     }
 
+  g_atomic_int_add (&alive_threads, -1);
+
   return NULL;
 }
 
@@ -1015,3 +1019,21 @@ g_thread_pool_get_max_idle_time (void)
 {
   return g_atomic_int_get (&max_idle_time);
 }
+
+void
+g_thread_pool_cleanup (void)
+{
+  /*
+   * FIXME: Making sure that no threads are left behind requires
+   *        significant changes to the current implementation,
+   *        so for now we'll signal and always wait 10 ms,
+   *        allowing any threads that were already in the process
+   *        of shutting down to execute their last instructions... ICK!
+   */
+  g_thread_pool_set_max_unused_threads (0);
+  while (alive_threads)
+    g_thread_yield ();
+  g_usleep (10000);
+
+  g_clear_pointer (&unused_thread_queue, g_async_queue_unref);
+}
diff --git a/glib/gtimezone.c b/glib/gtimezone.c
index e513f3b..5b49d76 100644
--- a/glib/gtimezone.c
+++ b/glib/gtimezone.c
@@ -813,5 +813,11 @@ g_time_zone_is_dst (GTimeZone *tz,
   return interval_isdst (tz, interval);
 }
 
+void
+g_time_zone_cleanup (void)
+{
+  g_clear_pointer (&time_zones, g_hash_table_unref);
+}
+
 /* Epilogue {{{1 */
 /* vim:set foldmethod=marker: */
diff --git a/glib/gutils.c b/glib/gutils.c
index 49862ac..95bc83d 100644
--- a/glib/gutils.c
+++ b/glib/gutils.c
@@ -600,6 +600,10 @@ static  gchar  **g_system_config_dirs = NULL;
 
 static  gchar  **g_user_special_dirs = NULL;
 
+#ifndef G_OS_WIN32
+static  gchar   *g_user_runtime_dir;
+#endif
+
 /* fifteen minutes of fame for everybody */
 #define G_USER_DIRS_EXPIRE      15 * 60
 
@@ -935,6 +939,40 @@ g_get_any_init_locked (void)
   G_UNLOCK (g_utils_global);
 }
 
+static void
+g_get_any_cleanup (void)
+{
+  g_clear_pointer (&g_tmp_dir, g_free);
+  g_clear_pointer (&g_user_name, g_free);
+  g_clear_pointer (&g_real_name, g_free);
+  g_clear_pointer (&g_home_dir, g_free);
+  g_clear_pointer (&g_host_name, g_free);
+
+#ifdef G_OS_WIN32
+  g_clear_pointer (&g_tmp_dir_cp, g_free);
+  g_clear_pointer (&g_user_name_cp, g_free);
+  g_clear_pointer (&g_real_name_cp, g_free);
+  g_clear_pointer (&g_home_dir_cp, g_free);
+#else
+  g_clear_pointer (&g_user_runtime_dir, g_free);
+#endif
+
+  g_clear_pointer (&g_user_data_dir, g_free);
+  g_clear_pointer (&g_system_data_dirs, g_strfreev);
+  g_clear_pointer (&g_user_cache_dir, g_free);
+  g_clear_pointer (&g_user_config_dir, g_free);
+  g_clear_pointer (&g_system_config_dirs, g_strfreev);
+
+  if (g_user_special_dirs != NULL)
+    {
+      gint i;
+
+      for (i = 0; i < G_USER_N_DIRECTORIES; i++)
+        g_free (g_user_special_dirs[i]);
+
+      g_clear_pointer (&g_user_special_dirs, g_free);
+    }
+}
 
 /**
  * g_get_user_name:
@@ -1391,18 +1429,17 @@ const gchar *
 g_get_user_runtime_dir (void)
 {
 #ifndef G_OS_WIN32
-  static const gchar *runtime_dir;
   static gsize initialised;
 
   if (g_once_init_enter (&initialised))
     {
-      runtime_dir = g_strdup (getenv ("XDG_RUNTIME_DIR"));
+      g_user_runtime_dir = g_strdup (getenv ("XDG_RUNTIME_DIR"));
       
       g_once_init_leave (&initialised, 1);
     }
 
-  if (runtime_dir)
-    return runtime_dir;
+  if (g_user_runtime_dir)
+    return g_user_runtime_dir;
 
   /* Both fallback for UNIX and the default
    * in Windows: use the user cache directory.
@@ -1803,6 +1840,15 @@ g_get_user_special_dir (GUserDirectory directory)
   return g_user_special_dirs[directory];
 }
 
+void
+g_utils_cleanup (void)
+{
+  g_clear_pointer (&g_prgname, g_free);
+  g_clear_pointer (&g_application_name, g_free);
+
+  g_get_any_cleanup ();
+}
+
 #ifdef G_OS_WIN32
 
 #undef g_get_system_data_dirs
diff --git a/glib/gvariant.c b/glib/gvariant.c
index 163782d..65eeebb 100644
--- a/glib/gvariant.c
+++ b/glib/gvariant.c
@@ -3931,6 +3931,8 @@ valid_format_string (const gchar *format_string,
                   "`%s' but the given value has a type of `%s'",
                   fragment, typestr, g_variant_get_type_string (value));
 
+      g_free (typestr);
+      g_free (fragment);
       g_variant_type_free (type);
 
       return FALSE;
diff --git a/glib/tests/error.c b/glib/tests/error.c
index 81b4055..2b46bdf 100644
--- a/glib/tests/error.c
+++ b/glib/tests/error.c
@@ -28,9 +28,7 @@ test_overwrite (void)
   g_test_assert_expected_messages ();
 
   g_assert_error (dest, G_MARKUP_ERROR, G_MARKUP_ERROR_EMPTY);
-  g_assert_error (src, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE);
   g_error_free (dest);
-  g_error_free (src);
 
 }
 
diff --git a/glib/tests/gwakeuptest.c b/glib/tests/gwakeuptest.c
index eaa10ca..9a24a22 100644
--- a/glib/tests/gwakeuptest.c
+++ b/glib/tests/gwakeuptest.c
@@ -210,8 +210,16 @@ test_threaded (void)
 {
   gint i;
 
-  /* make sure we don't block forever */
-  alarm (60);
+  if (!g_mem_do_cleanup)
+    {
+      /* Make sure we don't block forever. But if g_mem_do_cleanup is
+       * set, we're probably running under valgrind, in which case things
+       * may run arbitrarily more slowly, so just skip this part; presumably
+       * the developer has already made sure the test passes outside of
+       * valgrind anyway...
+       */
+      alarm (60);
+    }
 
   /* simple mainloop test based on GWakeup.
    *
@@ -251,7 +259,8 @@ test_threaded (void)
   g_wakeup_free (last_token_wakeup);
 
   /* cancel alarm */
-  alarm (0);
+  if (!g_mem_do_cleanup)
+    alarm (0);
 }
 
 int
diff --git a/glib/tests/utils.c b/glib/tests/utils.c
index 9d79584..c94441c 100644
--- a/glib/tests/utils.c
+++ b/glib/tests/utils.c
@@ -424,7 +424,6 @@ test_xdg_dirs (void)
 
   g_assert_cmpstr (s, ==, xdg);
 
-  g_strfreev ((gchar **)dirs);
   g_free (s);
 }
 
diff --git a/gobject/gatomicarray.c b/gobject/gatomicarray.c
index d621e94..d341cab 100644
--- a/gobject/gatomicarray.c
+++ b/gobject/gatomicarray.c
@@ -45,6 +45,9 @@
 
 G_LOCK_DEFINE_STATIC (array);
 
+#define G_ATOMIC_ARRAY_REAL_SIZE_FROM(s) \
+    (sizeof (gsize) + MAX (s, sizeof (FreeListNode)))
+
 typedef struct _FreeListNode FreeListNode;
 struct _FreeListNode {
   FreeListNode *next;
@@ -75,7 +78,7 @@ freelist_alloc (gsize size, gboolean reuse)
        }
     }
 
-  real_size = sizeof (gsize) + MAX (size, sizeof (FreeListNode));
+  real_size = G_ATOMIC_ARRAY_REAL_SIZE_FROM (size);
   mem = g_slice_alloc (real_size);
   mem = ((char *) mem) + sizeof (gsize);
   G_ATOMIC_ARRAY_DATA_SIZE (mem) = size;
@@ -99,6 +102,30 @@ _g_atomic_array_init (GAtomicArray *array)
   array->data = NULL;
 }
 
+void
+_g_atomic_array_free (GAtomicArray *array)
+{
+  if (array->data != NULL)
+    freelist_free (array->data);
+}
+
+void
+_g_atomic_array_deinit (void)
+{
+  FreeListNode *cur, *next;
+
+  for (cur = freelist; cur; cur = next)
+    {
+      gsize size, real_size;
+
+      next = cur->next;
+
+      size = G_ATOMIC_ARRAY_DATA_SIZE (cur);
+      real_size = G_ATOMIC_ARRAY_REAL_SIZE_FROM (size);
+      g_slice_free1 (real_size, ((char *) cur) - sizeof (gsize));
+    }
+}
+
 /* Get a copy of the data (if non-NULL) that
  * can be changed and then re-applied with
  * g_atomic_array_update().
diff --git a/gobject/gatomicarray.h b/gobject/gatomicarray.h
index 4d4b3d5..8a8e8b8 100644
--- a/gobject/gatomicarray.h
+++ b/gobject/gatomicarray.h
@@ -35,6 +35,8 @@ struct _GAtomicArray {
 };
 
 void     _g_atomic_array_init   (GAtomicArray *array);
+void     _g_atomic_array_free   (GAtomicArray *array);
+void     _g_atomic_array_deinit (void);
 gpointer _g_atomic_array_copy   (GAtomicArray *array,
                                 gsize         header_size,
                                 gsize         additional_element_size);
diff --git a/gobject/gobject.c b/gobject/gobject.c
index 9c4ca63..e47d0f9 100644
--- a/gobject/gobject.c
+++ b/gobject/gobject.c
@@ -31,6 +31,7 @@
 #include "gvaluecollector.h"
 #include "gsignal.h"
 #include "gparamspecs.h"
+#include "gtype-private.h"
 #include "gvaluetypes.h"
 #include "gobject_trace.h"
 #include "gconstructor.h"
@@ -342,6 +343,9 @@ debug_objects_atexit (void)
 {
   IF_DEBUG (OBJECTS)
     {
+      if (debug_objects_ht == NULL)
+        return; /* deinitialized, do nothing */
+
       G_LOCK (debug_objects);
       g_message ("stale GObjects: %u", debug_objects_count);
       g_hash_table_foreach (debug_objects_ht, debug_objects_foreach, NULL);
@@ -402,6 +406,24 @@ _g_object_type_init (void)
 #endif /* G_ENABLE_DEBUG */
 }
 
+void
+g_object_type_deinit (void)
+{
+  if (pspec_pool != NULL)
+    {
+      g_param_spec_pool_destroy (pspec_pool);
+      pspec_pool = NULL;
+    }
+
+#ifdef G_ENABLE_DEBUG
+  if (debug_objects_ht != NULL)
+    {
+      g_hash_table_unref (debug_objects_ht);
+      debug_objects_ht = NULL;
+    }
+#endif /* G_ENABLE_DEBUG */
+}
+
 static void
 g_object_base_class_init (GObjectClass *class)
 {
@@ -421,14 +443,21 @@ g_object_base_class_init (GObjectClass *class)
 
 static void
 g_object_base_class_finalize (GObjectClass *class)
-{
-  GList *list, *node;
-  
+{  
   _g_signals_destroy (G_OBJECT_CLASS_TYPE (class));
 
   g_slist_free (class->construct_properties);
   class->construct_properties = NULL;
-  list = g_param_spec_pool_list_owned (pspec_pool, G_OBJECT_CLASS_TYPE (class));
+
+  _g_object_release_resources_owned_by (G_OBJECT_CLASS_TYPE (class));
+}
+
+void
+_g_object_release_resources_owned_by (GType type)
+{
+  GList *list, *node;
+
+  list = g_param_spec_pool_list_owned (pspec_pool, type);
   for (node = list; node; node = node->next)
     {
       GParamSpec *pspec = node->data;
diff --git a/gobject/gobject.symbols b/gobject/gobject.symbols
index 136045b..9ad5abd 100644
--- a/gobject/gobject.symbols
+++ b/gobject/gobject.symbols
@@ -345,6 +345,7 @@ g_type_get_plugin
 g_type_get_qdata
 g_type_init
 g_type_init_with_debug_flags
+g_type_deinit
 g_type_instance_get_private
 g_type_interface_add_prerequisite
 g_type_interface_get_plugin
diff --git a/gobject/gparam.c b/gobject/gparam.c
index b5c3024..9e9a8af 100644
--- a/gobject/gparam.c
+++ b/gobject/gparam.c
@@ -80,6 +80,8 @@ static gchar* value_param_lcopy_value         (const GValue   *value,
                                                 GTypeCValue    *collect_values,
                                                 guint           collect_flags);
 
+G_LOCK_DEFINE_STATIC (g_param_spec_class_info);
+static GSList *g_param_spec_class_info = NULL;
 
 /* --- functions --- */
 void
@@ -128,6 +130,14 @@ _g_param_type_init (void)
   g_value_register_transform_func (G_TYPE_PARAM, G_TYPE_PARAM, value_param_transform_value);
 }
 
+void
+g_param_type_deinit (void)
+{
+  g_slist_foreach (g_param_spec_class_info, (GFunc) g_free, NULL);
+  g_slist_free (g_param_spec_class_info);
+  g_param_spec_class_info = NULL;
+}
+
 static void
 g_param_spec_class_base_init (GParamSpecClass *class)
 {
@@ -901,6 +911,20 @@ g_param_spec_pool_new (gboolean type_prefixing)
   return pool;
 }
 
+void
+g_param_spec_pool_destroy (GParamSpecPool *pool)
+{
+  GHashTableIter iter;
+  gpointer key;
+
+  g_hash_table_iter_init (&iter, pool->hash_table);
+  while (g_hash_table_iter_next (&iter, &key, NULL))
+    g_param_spec_unref (key);
+  g_hash_table_unref (pool->hash_table);
+
+  g_free (pool);
+}
+
 /**
  * g_param_spec_pool_insert:
  * @pool: a #GParamSpecPool.
@@ -1341,6 +1365,12 @@ param_spec_generic_class_init (gpointer g_class,
   if (info->value_validate)
     class->value_validate = info->value_validate;      /* optional */
   class->values_cmp = info->values_cmp;
+
+  G_LOCK (g_param_spec_class_info);
+  g_param_spec_class_info = g_slist_remove (g_param_spec_class_info,
+      class_data);
+  G_UNLOCK (g_param_spec_class_info);
+
   g_free (class_data);
 }
 
@@ -1408,6 +1438,10 @@ g_param_type_register_static (const gchar              *name,
   cinfo->values_cmp = pspec_info->values_cmp ? pspec_info->values_cmp : default_values_cmp;
   info.class_data = cinfo;
 
+  G_LOCK (g_param_spec_class_info);
+  g_param_spec_class_info = g_slist_prepend (g_param_spec_class_info, cinfo);
+  G_UNLOCK (g_param_spec_class_info);
+
   return g_type_register_static (G_TYPE_PARAM, name, &info, 0);
 }
 
diff --git a/gobject/gparam.h b/gobject/gparam.h
index 84b3c44..c1cce2e 100644
--- a/gobject/gparam.h
+++ b/gobject/gparam.h
@@ -376,6 +376,7 @@ gpointer    g_param_spec_internal           (GType          param_type,
                                                 const gchar   *blurb,
                                                 GParamFlags    flags);
 GParamSpecPool* g_param_spec_pool_new          (gboolean       type_prefixing);
+void            g_param_spec_pool_destroy       (GParamSpecPool *pool);
 void           g_param_spec_pool_insert        (GParamSpecPool *pool,
                                                 GParamSpec     *pspec,
                                                 GType           owner_type);
diff --git a/gobject/gparamspecs.c b/gobject/gparamspecs.c
index 0043cac..f44c808 100644
--- a/gobject/gparamspecs.c
+++ b/gobject/gparamspecs.c
@@ -1592,6 +1592,13 @@ _g_param_spec_types_init (void)
   g_assert (spec_types == spec_types_bound);
 }
 
+void
+g_param_spec_types_deinit (void)
+{
+  g_free (g_param_spec_types);
+  g_param_spec_types = NULL;
+}
+
 /* --- GParamSpec initialization --- */
 
 /**
diff --git a/gobject/gsignal.c b/gobject/gsignal.c
index 6913979..6741f22 100644
--- a/gobject/gsignal.c
+++ b/gobject/gsignal.c
@@ -832,6 +832,36 @@ _g_signal_init (void)
 }
 
 void
+g_signal_deinit (void)
+{
+  guint i;
+
+  SIGNAL_LOCK (); /* signal_destroy_R releases this lock, so we must hold it */
+
+  for (i = 1; i < g_n_signal_nodes; i++)
+    {
+      SignalNode *node = g_signal_nodes[i];
+
+      if (!node->destroyed)
+       signal_destroy_R (node);
+      g_free (node);
+    }
+
+  SIGNAL_UNLOCK ();
+
+  g_hash_table_unref (g_handler_list_bsa_ht);
+  g_handler_list_bsa_ht = NULL;
+
+  g_bsearch_array_free (g_signal_key_bsa, &g_signal_key_bconfig);
+  g_signal_key_bsa = NULL;
+
+  g_n_signal_nodes = 0;
+
+  g_free (g_signal_nodes);
+  g_signal_nodes = NULL;
+}
+
+void
 _g_signals_destroy (GType itype)
 {
   guint i;
diff --git a/gobject/gtype-private.h b/gobject/gtype-private.h
index 04a252b..fac8fec 100644
--- a/gobject/gtype-private.h
+++ b/gobject/gtype-private.h
@@ -73,6 +73,9 @@ void        _g_closure_invoke_va (GClosure       *closure,
                                  GType          *param_types);
 
 
+/* for gtype.c */
+void _g_object_release_resources_owned_by (GType type);
+
 G_END_DECLS
 
 #endif /* __G_TYPE_PRIVATE_H__ */
diff --git a/gobject/gtype.c b/gobject/gtype.c
index 075e2bc..1e00363 100644
--- a/gobject/gtype.c
+++ b/gobject/gtype.c
@@ -4386,6 +4386,220 @@ g_type_init (void)
   g_type_init_with_debug_flags (0);
 }
 
+static void
+type_iface_vtable_finalize (TypeNode       *iface,
+                           TypeNode       *node,
+                           GTypeInterface *vtable)
+{
+  IFaceEntry *entry = type_lookup_iface_entry_L (node, iface);
+  IFaceHolder *iholder;
+
+  iholder = type_iface_retrieve_holder_info_Wm (iface, NODE_TYPE (node), FALSE);
+  if (!iholder)
+    return;
+
+  g_assert (entry && entry->vtable == vtable && iholder->info);
+
+  g_assert (entry->init_state == INITIALIZED);
+  entry->init_state = UNINITIALIZED;
+
+  if (iholder->info->interface_finalize)
+    iholder->info->interface_finalize (vtable, iholder->info->interface_data);
+  if (iface->data->iface.vtable_finalize_base)
+    iface->data->iface.vtable_finalize_base (vtable);
+}
+
+static void
+type_data_finalize_class_ifaces (TypeNode *node)
+{
+  IFaceEntries *entries;
+  guint i;
+
+  entries = CLASSED_NODE_IFACES_ENTRIES_LOCKED (node);
+  for (i = 0; entries != NULL && i < IFACE_ENTRIES_N_ENTRIES (entries); i++)
+    {
+      IFaceEntry *entry = &entries->entry[i];
+      if (entry->vtable)
+       {
+          g_assert (entry->init_state == INITIALIZED);
+
+          type_iface_vtable_finalize (lookup_type_node_I (entry->iface_type), node, entry->vtable);
+
+          entry->init_state = UNINITIALIZED;
+       }
+    }
+}
+
+static void
+type_data_finalize_class (TypeNode  *node,
+                         ClassData *cdata)
+{
+  GTypeClass *class = cdata->class;
+  TypeNode *bnode;
+
+  if (cdata->class_finalize)
+    cdata->class_finalize (class, (gpointer) cdata->class_data);
+
+  /* call all base class destruction functions in descending order
+   */
+  if (cdata->class_finalize_base)
+    cdata->class_finalize_base (class);
+  for (bnode = lookup_type_node_I (NODE_PARENT_TYPE (node)); bnode; bnode = lookup_type_node_I 
(NODE_PARENT_TYPE (bnode)))
+    if (bnode->data->class.class_finalize_base)
+      bnode->data->class.class_finalize_base (class);
+}
+
+static void
+type_data_finalize (TypeNode *node)
+{
+  GType ptype = NODE_PARENT_TYPE (node);
+  TypeData *tdata;
+
+  tdata = node->data;
+  if (node->is_classed && tdata->class.class)
+    {
+      if (CLASSED_NODE_IFACES_ENTRIES_LOCKED (node) != NULL)
+       type_data_finalize_class_ifaces (node);
+      type_data_finalize_class (node, &tdata->class);
+    }
+  else if (NODE_IS_IFACE (node) && tdata->iface.dflt_vtable)
+    {
+      if (tdata->iface.dflt_finalize)
+        tdata->iface.dflt_finalize (tdata->iface.dflt_vtable, (gpointer) tdata->iface.dflt_data);
+      if (tdata->iface.vtable_finalize_base)
+        tdata->iface.vtable_finalize_base (tdata->iface.dflt_vtable);
+
+      /* FIXME: This is awful...
+        *
+        * Interfaces might do g_object_interface_install_property() in their
+        * base_init, which means that the GTypeInterface owns this GParamSpec.
+        * However, the GParamSpecPool is owned by GObject... and the interface'
+        * GParamSpecs need to be released when it goes away.
+        */
+      _g_object_release_resources_owned_by (NODE_TYPE (node));
+    }
+}
+
+void
+g_type_deinit (void)
+{
+  GHashTableIter iter;
+  gpointer value;
+  GHashTable * vtables;
+
+  g_hash_table_iter_init (&iter, static_type_nodes_ht);
+  while (g_hash_table_iter_next (&iter, NULL, &value))
+    {
+      GType gtype = (GType) GPOINTER_TO_SIZE (value);
+      TypeNode *node;
+
+      node = lookup_type_node_I (gtype);
+      if (node->is_classed)
+        type_data_finalize (node);
+    }
+
+  g_hash_table_iter_init (&iter, static_type_nodes_ht);
+  while (g_hash_table_iter_next (&iter, NULL, &value))
+    {
+      GType gtype = (GType) GPOINTER_TO_SIZE (value);
+      TypeNode *node;
+
+      node = lookup_type_node_I (gtype);
+      if (NODE_IS_IFACE (node))
+        type_data_finalize (node);
+    }
+
+  g_signal_deinit ();
+
+  g_param_spec_types_deinit ();
+
+  g_object_type_deinit ();
+
+  g_param_type_deinit ();
+
+  g_value_c_deinit ();
+
+  static_n_class_cache_funcs = 0;
+  g_free (static_class_cache_funcs);
+  static_class_cache_funcs = NULL;
+
+  static_n_iface_check_funcs = 0;
+  g_free (static_iface_check_funcs);
+  static_iface_check_funcs = NULL;
+
+  g_hash_table_iter_init (&iter, static_type_nodes_ht);
+  vtables = g_hash_table_new (NULL, NULL);
+  while (g_hash_table_iter_next (&iter, NULL, &value))
+    {
+      GType gtype = (GType) GPOINTER_TO_SIZE (value);
+      TypeNode *node;
+
+      node = lookup_type_node_I (gtype);
+
+      g_free (node->children);
+
+      if (node->is_classed)
+        {
+          IFaceEntries *entries;
+
+          entries = CLASSED_NODE_IFACES_ENTRIES_LOCKED (node);
+          if (entries)
+           {
+              guint i;
+
+             for (i = 0; i != IFACE_ENTRIES_N_ENTRIES (entries); i++)
+               g_hash_table_insert (vtables, entries->entry[i].vtable, NULL);
+            }
+
+          _g_atomic_array_free (CLASSED_NODE_IFACES_ENTRIES (node));
+
+          if (node->data != NULL)
+            g_free (node->data->class.class);
+        }
+
+      if (NODE_IS_IFACE (node))
+        {
+          IFaceHolder *iholder, *next;
+
+          _g_atomic_array_free (&node->_prot.offsets);
+
+          iholder = iface_node_get_holders_L (node);
+          while (iholder)
+            {
+              next = iholder->next;
+
+              g_free (iholder->info);
+              g_free (iholder);
+
+              iholder = next;
+            }
+
+          if (node->data != NULL)
+            g_free (node->data->iface.dflt_vtable);
+
+          g_free (iface_node_get_dependants_array_L (node));
+        }
+
+      g_free (node->data);
+
+      if (node->global_gdata != NULL)
+        g_free (node->global_gdata->qdatas);
+      g_free (node->global_gdata);
+
+      g_free (node->prerequisites);
+
+      if (G_TYPE_IS_FUNDAMENTAL (gtype))
+        node = G_STRUCT_MEMBER_P (node, -SIZEOF_FUNDAMENTAL_INFO);
+      g_free (node);
+    }
+  g_hash_table_foreach (vtables, (GHFunc) g_free, NULL);
+  g_hash_table_unref (vtables);
+  g_hash_table_unref (static_type_nodes_ht);
+  static_type_nodes_ht = NULL;
+
+  _g_atomic_array_deinit ();
+}
+
 /**
  * g_type_class_add_private:
  * @g_class: class structure for an instantiatable type
diff --git a/gobject/gvalue.c b/gobject/gvalue.c
index fcadab9..95a6c8c 100644
--- a/gobject/gvalue.c
+++ b/gobject/gvalue.c
@@ -145,6 +145,13 @@ _g_value_c_init (void)
   transform_array = g_bsearch_array_create (&transform_bconfig);
 }
 
+void
+g_value_c_deinit (void)
+{
+  g_bsearch_array_free (transform_array, &transform_bconfig);
+  transform_array = NULL;
+}
+
 static inline void             /* keep this function in sync with gvaluecollector.h and gboxed.c */
 value_meminit (GValue *value,
               GType   value_type)
diff --git a/gthread/gthread.def b/gthread/gthread.def
index 200b043..1832937 100644
--- a/gthread/gthread.def
+++ b/gthread/gthread.def
@@ -1,3 +1,4 @@
 EXPORTS
        g_thread_init
        g_thread_init_with_errorcheck_mutexes
+       g_thread_deinit


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