[glib/wip/gcleanup: 14/78] gcleanup: Implementation of GCleanupList and associated macros
- From: Stefan Walter <stefw src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [glib/wip/gcleanup: 14/78] gcleanup: Implementation of GCleanupList and associated macros
- Date: Tue, 12 Nov 2013 05:28:18 +0000 (UTC)
commit befc453034fb2d163c102112182d1f4c72574323
Author: Stef Walter <stefw redhat com>
Date: Sun Mar 24 21:23:31 2013 -0400
gcleanup: Implementation of GCleanupList and associated macros
Add a new type GCleanupList that stores a list of things to "clean up"
when g_cleanup_list_clear() is called.
More importantly, define some macros (G_CLEANUP, etc) that
facilitate conditionally building a per-library/executable cleanup list
if G_DEBUG=cleanup is specified. The cleanup list is run at destructor
time.
-DG_CLEANUP_SCOPE defines the name of the cleanup list and enables the
feature for a given module.
Concept and initial work: Ryan Lortie <desrt desrt ca>
https://bugzilla.gnome.org/show_bug.cgi?id=627423
docs/reference/glib/glib-sections.txt | 15 +
glib/Makefile.am | 2 +
glib/gcleanup.c | 488 +++++++++++++++++++++++++++++++++
glib/gcleanup.h | 130 +++++++++
glib/glib-init.c | 4 +
glib/glib-init.h | 1 +
glib/glib.h | 1 +
7 files changed, 641 insertions(+), 0 deletions(-)
---
diff --git a/docs/reference/glib/glib-sections.txt b/docs/reference/glib/glib-sections.txt
index 9613a5f..0198855 100644
--- a/docs/reference/glib/glib-sections.txt
+++ b/docs/reference/glib/glib-sections.txt
@@ -3241,3 +3241,18 @@ g_hostname_is_ascii_encoded
<SUBSECTION>
g_hostname_is_ip_address
</SECTION>
+
+<SECTION>
+<FILE>gcleanup</FILE>
+<TITLE>Cleanup</FILE>
+G_CLEANUP_SCOPE
+G_CLEANUP_DEFINE
+G_CLEANUP
+G_CLEANUP_IN_PHASE
+G_CLEANUP_FUNC
+G_CLEANUP_FUNC_IN_PHASE
+g_cleanup_is_enabled
+g_cleanup_list_push
+g_cleanup_list_remove
+g_cleanup_list_clean
+</SECTION>
diff --git a/glib/Makefile.am b/glib/Makefile.am
index 7c737d8..9c3c19e 100644
--- a/glib/Makefile.am
+++ b/glib/Makefile.am
@@ -115,6 +115,7 @@ libglib_2_0_la_SOURCES = \
gcharset.c \
gcharsetprivate.h \
gchecksum.c \
+ gcleanup.c \
gconvert.c \
gdataset.c \
gdatasetprivate.h \
@@ -251,6 +252,7 @@ glibsubinclude_HEADERS = \
gbytes.h \
gcharset.h \
gchecksum.h \
+ gcleanup.h \
gconstructor.h \
gconvert.h \
gdataset.h \
diff --git a/glib/gcleanup.c b/glib/gcleanup.c
new file mode 100644
index 0000000..355452f
--- /dev/null
+++ b/glib/gcleanup.c
@@ -0,0 +1,488 @@
+/*
+ * Copyright © 2013 Canonical Limited
+ *
+ * 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 licence, 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.
+ *
+ * Author: Ryan Lortie <desrt desrt ca>
+ */
+
+#include "config.h"
+
+#include "gcleanup.h"
+
+#include "glib-init.h"
+#include "gatomic.h"
+#include "gbitlock.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+/**
+ * SECTION:gcleanup
+ * @short_description: Cleanup on Exit
+ *
+ * The cleanup facilities allow GLib based libraries to clean up their global
+ * variables on exit or unloading of the module. This is useful for verifying
+ * that no memory leaks are present, and works well in conjuction with tools
+ * like valgrind.
+ *
+ * To use cleanup, define %G_CLEANUP_SCOPE either in your make files or
+ * in a non-public header. This declares the GCleanupList that cleanup items
+ * will be added to.
+ *
+ * Create the cleanup list using the %G_CLEANUP_DEFINE macro in a source
+ * file. To push items for cleanup use %G_CLEANUP or %G_CLEANUP_FUNC.
+ *
+ * The <literal>G_DEBUG</literal> environment variable must contain the word
+ * '<literal>cleanup</literal>' for the cleanup to occur.
+ *
+ * The cleanup is ordered in phases. Cleanup items in lower numbered phases
+ * are run before those in higher numbered phases. Several phases are
+ * predefined, but you are free to define your own. Use %G_CLEANUP_IN_PHASE
+ * to push items for cleanup either before or after cleanup is run for the
+ * default phase.
+ *
+ * It is permissible to add or remove other cleanup items at any time, and
+ * from any thread.
+ *
+ * Note that cleanup items registered by a library will run before the items
+ * of the libraries it depends on. The phases are only respected within a
+ * single %G_CLEANUP_SCOPE.
+ */
+
+/**
+ * G_CLEANUP_SCOPE:
+ *
+ * Defines the cleanup scope.
+ *
+ * Applications or libraries should define this if they wish to make use
+ * of the cleanup facilities. If not defined, then cleanup functions will
+ * do nothing. Be careful not to define it in any public header files.
+ *
+ * For example, you might use this in its Makefile.am:
+ * |[
+ * INCLUDES = -DG_CLEANUP_SCOPE=my_app_cleanup
+ * ]|
+ *
+ * If %G_CLEANUP_SCOPE is not defined, then it will automatically be treated
+ * as %NULL, and cleanup facilities will not be compiled in.
+ *
+ * You can pass %G_CLEANUP_SCOPE as a #GCleanupList to g_cleanup_list_push()
+ * and g_cleanup_list_remove() if you need to.
+ */
+
+/**
+ * G_CLEANUP_PHASE_EARLY:
+ *
+ * Cleanup items that run before the main phase. This might be used for cleanup
+ * items that stop worker threads.
+ */
+
+/**
+ * G_CLEANUP_PHASE_DEFAULT:
+ *
+ * The main set of cleanup items.
+ */
+
+/**
+ * G_CLEANUP_PHASE_LATE:
+ *
+ * Cleanup items that run after the main phase. This is used to cleanup items
+ * that the main cleanup phase still depends on.
+ */
+
+/**
+ * G_CLEANUP_PHASE_GRAVEYARD:
+ *
+ * Special extremely late cleanup items. Rarely used outside of GLib. By
+ * convention, cleanup items running in this phase should not only use lower
+ * level facilitities, and not run other parts of the library's code.
+ */
+
+/*
+ * NOTE: most glib functions are off limits in this function without
+ * careful consideration. In particular logging functions,
+ * use various locks, which would cause issues during cleanup.
+ */
+
+/* As good a place as any to put this... */
+G_CLEANUP_DEFINE;
+
+typedef struct _GCleanupNode GCleanupNode;
+struct _GCleanupNode
+{
+ /* @func is accessed atomically. If NULL, then node is removed */
+ GCleanupFunc func;
+
+ gpointer data;
+ gint phase;
+ const gchar *func_name;
+ GCleanupNode *next;
+};
+
+typedef struct {
+ gint flags; /* Currently unused */
+ gint lock;
+ gint removed;
+ GCleanupNode *nodes;
+} GRealCleanup;
+
+static gboolean
+check_if_verbose (void)
+{
+ const gchar *env = getenv ("G_MESSAGES_DEBUG");
+ return (env && (strstr (env, "GLib-Cleanup") || strcmp (env, "all") == 0));
+}
+
+/**
+ * g_cleanup_is_enabled:
+ *
+ * Checks if the program should attempt to cleanup allocated memory at
+ * exit.
+ *
+ * This function will return true if the G_DEBUG variable is set to or
+ * includes 'cleanup'.
+ *
+ * See G_CLEANUP() and %G_CLEANUP_DEFINE for the recommended way to
+ * deal with memory cleanup.
+ *
+ * Returns: %TRUE if memory cleanup is enabled
+ *
+ * Since: 2.40
+ **/
+gboolean
+g_cleanup_is_enabled (void)
+{
+ return g_cleanup_enabled;
+}
+
+/**
+ * g_cleanup_list_push:
+ * @list: (allow-none): a #GCleanupList
+ * @phase: a phase to run this cleanup item in
+ * @cleanup_func: the cleanup function
+ * @user_data: (allow-none): data for the cleanup function
+ * @func_name: (allow-none): static string representing name of function
+ *
+ * Adds a cleanup item to @list.
+ *
+ * When g_cleanup_list_clean() is called on @list, @cleanup_func will be
+ * called with @user_data.
+ *
+ * Most typically, you will not use this function directly. See
+ * G_CLEANUP(), G_CLEANUP_FUNC().
+ *
+ * This function is threadsafe. Multiple threads can add to the same
+ * list at the same time.
+ *
+ * The returned pointer can be used with g_cleanup_list_remove()
+ * to later remove the item.
+ *
+ * Since: 2.40
+ *
+ * Returns: a tag which can be used for item removal
+ **/
+gpointer
+g_cleanup_list_push (GCleanupList *list,
+ gint phase,
+ GCleanupFunc cleanup_func,
+ gpointer user_data,
+ const gchar *func_name)
+{
+ GRealCleanup *cleanup;
+ GCleanupNode *node;
+ GCleanupNode **ptr;
+
+ if (!list || !g_cleanup_enabled)
+ return NULL;
+
+ cleanup = (GRealCleanup *)list;
+ node = NULL;
+
+ /*
+ * We use the bit locks, as they don't need cleanup themselves. In theory
+ * we could perform all the needed operations in a lock-less manner, but
+ * using a simple lock should be more efficient.
+ */
+
+ g_bit_lock (&cleanup->lock, 1);
+
+ /*
+ * Item removal is optimized for removal during cleanup. However in the case
+ * of repeated removal/push during the source of the process (ie: before
+ * cleanup has begun), we don't want the GCleanupList to become a memory
+ * leak.
+ *
+ * So we reuse removed nodes here.
+ */
+ if (cleanup->removed > 0)
+ {
+ for (ptr = &cleanup->nodes; (node = *ptr) != NULL; ptr = &(*ptr)->next)
+ {
+ /* If the node->func is NULL, steal the node */
+ if (g_atomic_pointer_get (&node->func) == NULL)
+ {
+ *ptr = node->next;
+ cleanup->removed--;
+ break;
+ }
+ }
+ }
+
+ /* Allocate a new one */
+ if (node == NULL)
+ node = malloc (sizeof (GCleanupNode));
+
+ if (node != NULL)
+ {
+ node->func = cleanup_func;
+ node->data = user_data;
+ node->func_name = func_name;
+ node->phase = phase;
+
+ node->next = cleanup->nodes;
+ cleanup->nodes = node;
+ }
+
+ if (check_if_verbose ())
+ {
+ fprintf (stderr, "GCleanup-DEBUG: g_cleanup_list_push: %s (%p) at %d\n",
+ func_name, user_data, phase);
+ }
+
+ g_bit_unlock (&cleanup->lock, 1);
+
+ return node;
+}
+
+/**
+ * g_cleanup_list_remove:
+ * @list: (allow-none): a #GCleanupList
+ * @item: (allow-none): the cleanup item
+ *
+ * Removes an @item in the list.
+ *
+ * It is not typically necessary to remove cleanup items, since cleanup is
+ * usually done on global or otherwise persistent data.
+ *
+ * This function reverses a previous call to g_cleanup_list_push(), and takes
+ * the item pointer returned by g_cleanup_list_push().
+ *
+ * This function is threadsafe. You can call this function one or zero times
+ * for an item that was added to the @list. However you should not call this
+ * function after g_cleanup_list_clean() returns.
+ *
+ * Since: 2.40
+**/
+void
+g_cleanup_list_remove (GCleanupList *list,
+ gpointer tag)
+{
+ GRealCleanup *cleanup;
+ GCleanupNode *node;
+
+ if (!tag || !list || !g_cleanup_enabled)
+ return;
+
+ /*
+ * We optimize the case where items are removed during cleanup, as this
+ * happens very often. However, see g_cleanup_list_add().
+ */
+
+ cleanup = (GRealCleanup *)list;
+ node = tag;
+
+ g_bit_lock (&cleanup->lock, 1);
+
+ /*
+ * Always access @func atomically. This allows us to not hold the lock
+ * while executing the callbacks in g_cleanup_list_clean().
+ */
+ cleanup->removed++;
+ g_atomic_pointer_set (&node->func, NULL);
+
+ if (check_if_verbose ())
+ {
+ fprintf (stderr, "GCleanup-DEBUG: g_cleanup_list_remove: %s (%p) at %d\n",
+ node->func_name, node->data, node->phase);
+ }
+
+ g_bit_unlock (&cleanup->lock, 1);
+}
+
+/**
+ * g_cleanup_list_clean:
+ * @list: a #GCleanupList
+ *
+ * Clears @list.
+ *
+ * This results in all of the previously-added functions being called.
+ *
+ * You usually do not need to call this directly. %G_CLEANUP_DEFINE will
+ * emit a destructor function to call this when your library or program
+ * is being unloaded.
+ *
+ * This function is threadsafe. Changes can occur while adds and
+ * changes are occuring in other threads.
+ *
+ * Since: 2.40
+ **/
+void
+g_cleanup_list_clean (GCleanupList *list)
+{
+ GRealCleanup *cleanup;
+ GCleanupNode *later;
+ GCleanupNode *nodes;
+ GCleanupNode *node;
+ GCleanupNode **last;
+ GCleanupFunc func;
+ gboolean verbose;
+ gint phase;
+ gint next;
+
+ if (!list || !g_cleanup_enabled)
+ return;
+
+ verbose = check_if_verbose ();
+ cleanup = (GRealCleanup *)list;
+
+ later = NULL;
+ nodes = NULL;
+ phase = 0;
+ next = 0;
+
+ do
+ {
+ later = nodes;
+
+ g_bit_lock (&cleanup->lock, 1);
+
+ nodes = cleanup->nodes;
+ cleanup->nodes = NULL;
+
+ g_bit_unlock (&cleanup->lock, 1);
+
+ /* The current phase */
+ phase = next;
+
+ /* No next phase yet */
+ next = G_MAXINT;
+
+ /* Find a lower phase, and find last node */
+ last = &nodes;
+ for (node = nodes; node; node = node->next)
+ {
+ if (node->phase < phase)
+ phase = node->phase;
+ last = &node->next;
+ }
+
+ /* Join the two lists */
+ *last = later;
+
+ /* Run that phase */
+ for (node = nodes; node; node = node->next)
+ {
+ func = g_atomic_pointer_get (&node->func);
+ if (!func)
+ continue;
+
+ /* Part of this phase */
+ if (phase == node->phase)
+ {
+ if (g_atomic_pointer_compare_and_exchange (&node->func, func, NULL))
+ {
+ if (verbose)
+ {
+ fprintf (stderr, "GCleanup-DEBUG: g_cleanup_list_clean: %s (%p) at %d\n",
+ node->func_name, node->data, phase);
+ }
+ (func) (node->data);
+ }
+ }
+
+ /* Find the next phase */
+ else if (node->phase > phase && node->phase < next)
+ {
+ next = node->phase;
+ }
+ }
+ }
+ while (next != G_MAXINT);
+
+ /* Free all the nodes */
+ while (nodes)
+ {
+ node = nodes;
+ nodes = node->next;
+ free (node);
+ }
+
+ if (verbose)
+ fprintf (stderr, "GCleanup-DEBUG: g_cleanup_list_clean: cleanup done\n");
+}
+
+/**
+ * G_CLEANUP_DEFINE:
+ *
+ * Sets up the GLib memory cleanup infrastructure for a shared library
+ * or executable program. This macro should be used once per
+ * shared library or executable.
+ *
+ * The macro defines a linked list to which cleanup functions will be
+ * added if memory cleanup has been enabled. It also defines a
+ * destructor function to free the items in this list on the current
+ * module being unloaded (usually after main() returns).
+ *
+ * Since: 2.40
+ **/
+
+/**
+ * G_CLEANUP:
+ * @data: the data to free
+ * @notify: the function used to free data
+ *
+ * Marks an item to be freed when performing memory cleanup.
+ *
+ * If memory cleanup is enabled then @function will be called on @data
+ * at the time that destructors are being run for the current module
+ * (ie: at program exit or module unload).
+ *
+ * In order for this to work, G_CLEANUP_DEFINE needs to be used exactly
+ * once somewhere in a source file in your module.
+ *
+ * If you want to call a function to cleanup several static variables
+ * then use G_CLEANUP_FUNC() instead.
+ *
+ * Since: 2.40
+ **/
+
+/**
+ * G_CLEANUP_FUNC:
+ * @cleanup_func: the cleanup function to call
+ *
+ * Adds a function to be called when performing memory cleanup.
+ *
+ * If memory cleanup is enabled then @function will be called at the
+ * time that destructors are being run for the current module (ie: at
+ * program exit or module unload).
+ *
+ * In order for this to work, G_CLEANUP_DEFINE needs to be used exactly
+ * once somewhere in a source file in your module.
+ *
+ * Since: 2.40
+ **/
diff --git a/glib/gcleanup.h b/glib/gcleanup.h
new file mode 100644
index 0000000..26c4c22
--- /dev/null
+++ b/glib/gcleanup.h
@@ -0,0 +1,130 @@
+/*
+ * Copyright © 2013 Canonical Limited
+ * Copyright © 2013 Red Hat Inc.
+ *
+ * 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 licence, 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.
+ *
+ * Author: Ryan Lortie <desrt desrt ca>
+ * Stef Walter <stefw redhat com>
+ */
+
+#ifndef __G_CLEANUP_H__
+#define __G_CLEANUP_H__
+
+#if !defined (__GLIB_H_INSIDE__) && !defined (GLIB_COMPILATION)
+#error "Only <glib.h> can be included directly."
+#endif
+
+#include <glib/gconstructor.h>
+#include <glib/gtypes.h>
+
+G_BEGIN_DECLS
+
+enum {
+ G_CLEANUP_PHASE_EARLY = -50,
+ G_CLEANUP_PHASE_DEFAULT = 0,
+ G_CLEANUP_PHASE_LATE = 50,
+ G_CLEANUP_PHASE_GRAVEYARD = 100,
+};
+
+typedef struct
+{
+ /*< private >*/
+ gint value;
+ gpointer priv[4];
+} GCleanupList;
+
+typedef void (* GCleanupFunc) (gpointer user_data);
+
+GLIB_AVAILABLE_IN_2_40
+gboolean g_cleanup_is_enabled (void);
+GLIB_AVAILABLE_IN_2_40
+gpointer g_cleanup_list_push (GCleanupList *list,
+ gint phase,
+ GCleanupFunc cleanup_func,
+ gpointer user_data,
+ const gchar *func_name);
+GLIB_AVAILABLE_IN_2_40
+void g_cleanup_list_remove (GCleanupList *list,
+ gpointer item);
+GLIB_AVAILABLE_IN_2_40
+void g_cleanup_list_clean (GCleanupList *list);
+
+#if defined(G_HAS_CONSTRUCTORS) && !defined(G_DEFINE_CONSTRUCTOR_NEEDS_PRAGMA) && defined(G_CLEANUP_SCOPE)
+
+/*
+ * G_CLEANUP_SCOPE acts as a pointer with a constant address usable in initializers.
+ */
+
+extern GCleanupList G_CLEANUP_SCOPE[1];
+
+#define G_CLEANUP_DEFINE \
+ GCleanupList G_CLEANUP_SCOPE[1] = { { 0, { 0, }, } }; \
+ G_DEFINE_DESTRUCTOR (G_PASTE (G_CLEANUP_SCOPE, _perform)) \
+ static void G_PASTE (G_CLEANUP_SCOPE, _perform) (void) { \
+ g_cleanup_list_clean (G_CLEANUP_SCOPE); \
+ }
+#define G_CLEANUP(data, func) \
+ G_STMT_START { \
+ if (0) (func) ((data)); \
+ g_cleanup_list_push (G_CLEANUP_SCOPE, G_CLEANUP_PHASE_DEFAULT, \
+ (void*) (func), (data), G_STRINGIFY (func)); \
+ } G_STMT_END
+#define G_CLEANUP_IN_PHASE(data, func, phase) \
+ G_STMT_START { \
+ if (0) (func) ((data)); \
+ g_cleanup_list_push (G_CLEANUP_SCOPE, phase, \
+ (void*) (func), (data), G_STRINGIFY (func)); \
+ } G_STMT_END
+#define G_CLEANUP_FUNC(func) \
+ G_STMT_START { \
+ if (0) (func) (); \
+ g_cleanup_list_push (G_CLEANUP_SCOPE, G_CLEANUP_PHASE_DEFAULT, \
+ (void*) (func), NULL, G_STRINGIFY (func)); \
+ } G_STMT_END
+#define G_CLEANUP_FUNC_IN_PHASE(func, phase) \
+ G_STMT_START { \
+ if (0) (func) (); \
+ g_cleanup_list_push (G_CLEANUP_SCOPE, phase, \
+ (void*) (func), NULL, G_STRINGIFY (func)); \
+ } G_STMT_END
+
+#else
+
+#define G_CLEANUP_SCOPE NULL
+#define G_CLEANUP_DEFINE
+#define G_CLEANUP(data, func) \
+ G_STMT_START { \
+ if (0) (func) (data); \
+ } G_STMT_END
+#define G_CLEANUP_IN_PHASE(data, func, phase) \
+ G_STMT_START { \
+ if (0) (func) (data); \
+ } G_STMT_END
+#define G_CLEANUP_FUNC(func) \
+ G_STMT_START { \
+ if (0) (func) (); \
+ } G_STMT_END
+#define G_CLEANUP_FUNC_IN_PHASE(func, phase) \
+ G_STMT_START { \
+ if (0) (func) (); \
+ } G_STMT_END
+
+#endif
+
+G_END_DECLS
+
+#endif /* __G_CLEANUP_H__ */
diff --git a/glib/glib-init.c b/glib/glib-init.c
index ceb9395..70dfc54 100644
--- a/glib/glib-init.c
+++ b/glib/glib-init.c
@@ -43,6 +43,7 @@ gboolean g_mem_gc_friendly = TRUE;
#else
gboolean g_mem_gc_friendly = FALSE;
#endif
+gboolean g_cleanup_enabled = 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;
@@ -203,6 +204,8 @@ g_debug_init (void)
{
const GDebugKey keys[] = {
{ "gc-friendly", 1 },
+ { "cleanup", 2 },
+ /* warning: G_LOG_LEVEL_ERROR is 4, so you'd better not use that one next... */
{"fatal-warnings", G_LOG_LEVEL_WARNING | G_LOG_LEVEL_CRITICAL },
{"fatal-criticals", G_LOG_LEVEL_CRITICAL }
};
@@ -213,6 +216,7 @@ g_debug_init (void)
g_log_always_fatal |= flags & G_LOG_LEVEL_MASK;
g_mem_gc_friendly = flags & 1;
+ g_cleanup_enabled = (flags & 2) != 0;
}
static void
diff --git a/glib/glib-init.h b/glib/glib-init.h
index 5b150a4..c279215 100644
--- a/glib/glib-init.h
+++ b/glib/glib-init.h
@@ -26,6 +26,7 @@
extern GLogLevelFlags g_log_always_fatal;
extern GLogLevelFlags g_log_msg_prefix;
+extern gboolean g_cleanup_enabled;
#ifdef G_OS_WIN32
#include <windows.h>
diff --git a/glib/glib.h b/glib/glib.h
index c117761..e72034a 100644
--- a/glib/glib.h
+++ b/glib/glib.h
@@ -40,6 +40,7 @@
#include <glib/gbytes.h>
#include <glib/gcharset.h>
#include <glib/gchecksum.h>
+#include <glib/gcleanup.h>
#include <glib/gconstructor.h>
#include <glib/gconvert.h>
#include <glib/gdataset.h>
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]