[glib/wip/free: 1/6] Add G_DEBUG=cleanup
- From: Dan Winship <danw src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [glib/wip/free: 1/6] Add G_DEBUG=cleanup
- Date: Thu, 31 Oct 2013 15:33:41 +0000 (UTC)
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 (¤t_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]