mirror of
https://gitlab.gnome.org/GNOME/glib.git
synced 2025-10-03 04:09:20 +02:00
gcleanup: Implementation of GCleanupScope and associated macros
Add a new type GCleanupScope that stores a list of things to "clean up" when g_cleanup_clean() 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
This commit is contained in:
@@ -3241,3 +3241,18 @@ g_hostname_is_ascii_encoded
|
|||||||
<SUBSECTION>
|
<SUBSECTION>
|
||||||
g_hostname_is_ip_address
|
g_hostname_is_ip_address
|
||||||
</SECTION>
|
</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>
|
||||||
|
@@ -115,6 +115,7 @@ libglib_2_0_la_SOURCES = \
|
|||||||
gcharset.c \
|
gcharset.c \
|
||||||
gcharsetprivate.h \
|
gcharsetprivate.h \
|
||||||
gchecksum.c \
|
gchecksum.c \
|
||||||
|
gcleanup.c \
|
||||||
gconvert.c \
|
gconvert.c \
|
||||||
gdataset.c \
|
gdataset.c \
|
||||||
gdatasetprivate.h \
|
gdatasetprivate.h \
|
||||||
@@ -251,6 +252,7 @@ glibsubinclude_HEADERS = \
|
|||||||
gbytes.h \
|
gbytes.h \
|
||||||
gcharset.h \
|
gcharset.h \
|
||||||
gchecksum.h \
|
gchecksum.h \
|
||||||
|
gcleanup.h \
|
||||||
gconstructor.h \
|
gconstructor.h \
|
||||||
gconvert.h \
|
gconvert.h \
|
||||||
gdataset.h \
|
gdataset.h \
|
||||||
|
620
glib/gcleanup.c
Normal file
620
glib/gcleanup.c
Normal file
@@ -0,0 +1,620 @@
|
|||||||
|
/*
|
||||||
|
* 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 "glib-private.h"
|
||||||
|
|
||||||
|
#include "gatomic.h"
|
||||||
|
#include "gbitlock.h"
|
||||||
|
#include "ghash.h"
|
||||||
|
#include "gmacros.h"
|
||||||
|
#include "gtestutils.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 #GCleanupScope that cleanup items
|
||||||
|
* will be added to.
|
||||||
|
*
|
||||||
|
* Create the cleanup scope 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 between the integers
|
||||||
|
* -1000 and 1000.
|
||||||
|
*
|
||||||
|
* 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 #GCleanupScope 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;
|
||||||
|
|
||||||
|
static gint marked = 0;
|
||||||
|
|
||||||
|
/* GCleanupNode flags */
|
||||||
|
enum {
|
||||||
|
DEREF_CLEAR_POINTER = 1 << 16,
|
||||||
|
PHASE_MASK = 0xFFFF
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct _GCleanupNode GCleanupNode;
|
||||||
|
struct _GCleanupNode
|
||||||
|
{
|
||||||
|
/* Lower 16 bits is phase, higher bits are flags */
|
||||||
|
guint phase_and_flags;
|
||||||
|
|
||||||
|
/* @func is accessed atomically. If NULL, then node is removed */
|
||||||
|
GCleanupFunc func;
|
||||||
|
gpointer data;
|
||||||
|
|
||||||
|
/* May drop this if we drop debugging */
|
||||||
|
const gchar *func_name;
|
||||||
|
|
||||||
|
GCleanupNode *next;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
gint flags;
|
||||||
|
GCleanupNode *nodes;
|
||||||
|
gint lock;
|
||||||
|
gint swept;
|
||||||
|
} 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gpointer
|
||||||
|
cleanup_scope_push (GRealCleanup *cleanup,
|
||||||
|
gint phase,
|
||||||
|
guint flags,
|
||||||
|
GCleanupFunc cleanup_func,
|
||||||
|
gpointer user_data)
|
||||||
|
{
|
||||||
|
GCleanupNode *node;
|
||||||
|
GCleanupNode **ptr;
|
||||||
|
|
||||||
|
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 GCleanupScope to become a memory
|
||||||
|
* leak.
|
||||||
|
*
|
||||||
|
* So we reuse removed nodes here.
|
||||||
|
*/
|
||||||
|
marked = g_atomic_int_get (&marked);
|
||||||
|
if (marked != cleanup->swept)
|
||||||
|
{
|
||||||
|
for (ptr = &cleanup->nodes; (node = *ptr) != NULL; ptr = &(*ptr)->next)
|
||||||
|
{
|
||||||
|
/* If the node->func is NULL, steal the node */
|
||||||
|
if (g_atomic_pointer_compare_and_exchange (&node->func, NULL, cleanup_func))
|
||||||
|
{
|
||||||
|
g_atomic_int_add (&marked, -1);
|
||||||
|
*ptr = node->next;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* A full pass found nothing, don't try again */
|
||||||
|
if (node == NULL)
|
||||||
|
cleanup->swept = marked;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Allocate a new one */
|
||||||
|
if (node == NULL)
|
||||||
|
node = calloc (1, sizeof (GCleanupNode));
|
||||||
|
|
||||||
|
if (node != NULL)
|
||||||
|
{
|
||||||
|
node->func = cleanup_func;
|
||||||
|
node->data = user_data;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We use the first 16 bits of GCleanupNode->phase_and_flags as the phase.
|
||||||
|
* However we want callers to be able to specify zero as default phase,
|
||||||
|
* negative as early, positive as late. So convert to a unsigned phase
|
||||||
|
* here, and combine with flags.
|
||||||
|
*/
|
||||||
|
|
||||||
|
g_assert ((flags & PHASE_MASK) == 0);
|
||||||
|
phase = CLAMP (phase, -1024, 1024) + G_MAXINT16;
|
||||||
|
node->phase_and_flags = (guint) phase | flags;
|
||||||
|
|
||||||
|
node->next = cleanup->nodes;
|
||||||
|
cleanup->nodes = node;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_bit_unlock (&cleanup->lock, 1);
|
||||||
|
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
g_cleanup_annotate (gpointer cleanup_item,
|
||||||
|
const gchar *func_name)
|
||||||
|
{
|
||||||
|
GCleanupNode *node;
|
||||||
|
|
||||||
|
if (cleanup_item == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
node = cleanup_item;
|
||||||
|
node->func_name = func_name;
|
||||||
|
|
||||||
|
if (check_if_verbose ())
|
||||||
|
{
|
||||||
|
fprintf (stderr, "GLib-Cleanup-DEBUG: pushed: %s (%p) at %u\n",
|
||||||
|
func_name, node->data, node->phase_and_flags & PHASE_MASK);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** xxxx
|
||||||
|
* g_cleanup_list_push:
|
||||||
|
* @scope: (allow-none): a #GCleanupScope
|
||||||
|
* @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 @scope.
|
||||||
|
*
|
||||||
|
* When g_cleanup_list_clean() is called on @scope, @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
|
||||||
|
* scope 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
|
||||||
|
* xxxxx
|
||||||
|
**/
|
||||||
|
gpointer
|
||||||
|
g_cleanup_push (GCleanupScope *cleanup,
|
||||||
|
gint phase,
|
||||||
|
GCleanupFunc cleanup_func,
|
||||||
|
gpointer user_data)
|
||||||
|
{
|
||||||
|
GRealCleanup *real = (GRealCleanup *)cleanup;
|
||||||
|
|
||||||
|
if (cleanup && (real->flags & G_CLEANUP_SCOPE_FORCE || g_cleanup_enabled))
|
||||||
|
return cleanup_scope_push (real, phase, 0, cleanup_func, user_data);
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
gpointer
|
||||||
|
g_cleanup_push_pointer (GCleanupScope *cleanup,
|
||||||
|
gint phase,
|
||||||
|
GCleanupFunc cleanup_func,
|
||||||
|
gpointer *pointer_to_data)
|
||||||
|
{
|
||||||
|
GRealCleanup *real = (GRealCleanup *)cleanup;
|
||||||
|
|
||||||
|
if (cleanup && (real->flags & G_CLEANUP_SCOPE_FORCE || g_cleanup_enabled))
|
||||||
|
{
|
||||||
|
return cleanup_scope_push (real, phase, DEREF_CLEAR_POINTER,
|
||||||
|
cleanup_func, pointer_to_data);
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
dummy_callback (gpointer user_data)
|
||||||
|
{
|
||||||
|
g_assert_not_reached ();
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
g_cleanup_push_source (GCleanupScope *cleanup,
|
||||||
|
gint phase,
|
||||||
|
GSource *source)
|
||||||
|
{
|
||||||
|
GRealCleanup *real = (GRealCleanup *)cleanup;
|
||||||
|
gpointer cleanup_item;
|
||||||
|
GSource *child;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* So we want to get a callback when a source is destroyed. The only way
|
||||||
|
* I've found to do that is by registering a child source. By setting NULL
|
||||||
|
* for all the functions, they return FALSE, and don't dispatch().
|
||||||
|
*/
|
||||||
|
|
||||||
|
static GSourceFuncs funcs = { NULL, };
|
||||||
|
|
||||||
|
if (cleanup && (real->flags & G_CLEANUP_SCOPE_FORCE || g_cleanup_enabled))
|
||||||
|
{
|
||||||
|
cleanup_item = cleanup_scope_push (real, phase, 0, (GCleanupFunc)g_source_destroy, source);
|
||||||
|
if (cleanup_item)
|
||||||
|
{
|
||||||
|
child = g_source_new (&funcs, sizeof (GSource));
|
||||||
|
g_source_set_callback (child, dummy_callback, cleanup_item, g_cleanup_remove);
|
||||||
|
g_source_add_child_source (source, child);
|
||||||
|
g_source_unref (child);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** xxxx
|
||||||
|
* g_cleanup_steal:
|
||||||
|
* @scope: (allow-none): a #GCleanupScope
|
||||||
|
* @item: (allow-none): the cleanup item
|
||||||
|
*
|
||||||
|
* Removes an @item in the scope.
|
||||||
|
*
|
||||||
|
* 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 @scope. However you should not call this
|
||||||
|
* function after g_cleanup_list_clean() returns.
|
||||||
|
*
|
||||||
|
* Since: 2.40
|
||||||
|
* xxxxx
|
||||||
|
**/
|
||||||
|
void
|
||||||
|
g_cleanup_remove (gpointer cleanup_item)
|
||||||
|
{
|
||||||
|
g_cleanup_steal (cleanup_item, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
GCleanupFunc
|
||||||
|
g_cleanup_steal (gpointer cleanup_item,
|
||||||
|
gpointer *stolen_data)
|
||||||
|
{
|
||||||
|
GCleanupNode *node;
|
||||||
|
GCleanupFunc func;
|
||||||
|
gpointer data;
|
||||||
|
|
||||||
|
if (!cleanup_item)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We optimize the case where items are removed during cleanup, as this
|
||||||
|
* happens very often. However, see g_cleanup_list_add().
|
||||||
|
*/
|
||||||
|
|
||||||
|
node = cleanup_item;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Always access @func atomically. This allows us to not hold the lock
|
||||||
|
* while executing the callbacks in g_cleanup_list_clean().
|
||||||
|
*/
|
||||||
|
do
|
||||||
|
{
|
||||||
|
data = node->data;
|
||||||
|
func = g_atomic_pointer_get (&node->func);
|
||||||
|
if (!func)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
while (!g_atomic_pointer_compare_and_exchange (&node->func, func, NULL));
|
||||||
|
|
||||||
|
if (!func)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
/* Help g_cleanup_push find this removed item */
|
||||||
|
g_atomic_int_add (&marked, 1);
|
||||||
|
|
||||||
|
if (check_if_verbose ())
|
||||||
|
{
|
||||||
|
fprintf (stderr, "GLib-Cleanup-DEBUG: remove: %s (%p) at %d\n",
|
||||||
|
node->func_name, data, node->phase_and_flags & PHASE_MASK);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stolen_data)
|
||||||
|
*stolen_data = data;
|
||||||
|
return func;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** xxxx
|
||||||
|
* g_cleanup_list_clean:
|
||||||
|
* @scope: a #GCleanupScope
|
||||||
|
*
|
||||||
|
* Clears @scope.
|
||||||
|
*
|
||||||
|
* 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
|
||||||
|
* xxx
|
||||||
|
**/
|
||||||
|
void
|
||||||
|
g_cleanup_clean (GCleanupScope *scope)
|
||||||
|
{
|
||||||
|
GRealCleanup *cleanup;
|
||||||
|
GCleanupNode *later;
|
||||||
|
GCleanupNode *nodes;
|
||||||
|
GCleanupNode *node;
|
||||||
|
GCleanupNode **last;
|
||||||
|
GCleanupFunc func;
|
||||||
|
gboolean verbose;
|
||||||
|
gint phase, node_phase;
|
||||||
|
gint next;
|
||||||
|
|
||||||
|
if (!scope)
|
||||||
|
return;
|
||||||
|
|
||||||
|
verbose = check_if_verbose ();
|
||||||
|
cleanup = (GRealCleanup *)scope;
|
||||||
|
|
||||||
|
later = NULL;
|
||||||
|
nodes = NULL;
|
||||||
|
phase = 0;
|
||||||
|
next = G_MAXUINT16;
|
||||||
|
|
||||||
|
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_MAXUINT16;
|
||||||
|
|
||||||
|
/* Find a lower phase, and find last node */
|
||||||
|
last = &nodes;
|
||||||
|
for (node = nodes; node; node = node->next)
|
||||||
|
{
|
||||||
|
node_phase = node->phase_and_flags & PHASE_MASK;
|
||||||
|
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;
|
||||||
|
|
||||||
|
node_phase = node->phase_and_flags & PHASE_MASK;
|
||||||
|
|
||||||
|
/* Part of this phase */
|
||||||
|
if (phase == node_phase)
|
||||||
|
{
|
||||||
|
if (g_atomic_pointer_compare_and_exchange (&node->func, func, NULL))
|
||||||
|
{
|
||||||
|
if (verbose)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "GLib-Cleanup-DEBUG: clean: %s (%p) at %d\n",
|
||||||
|
node->func_name, node->data, phase);
|
||||||
|
}
|
||||||
|
if (node->phase_and_flags & DEREF_CLEAR_POINTER)
|
||||||
|
g_clear_pointer ((gpointer *)node->data, func);
|
||||||
|
else
|
||||||
|
(func) (node->data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Find the next phase */
|
||||||
|
else if (node_phase > phase && node_phase < next)
|
||||||
|
{
|
||||||
|
next = node_phase;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
while (next != G_MAXUINT16);
|
||||||
|
|
||||||
|
/* Free all the nodes */
|
||||||
|
while (nodes)
|
||||||
|
{
|
||||||
|
node = nodes;
|
||||||
|
nodes = node->next;
|
||||||
|
free (node);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (verbose)
|
||||||
|
fprintf (stderr, "GLib-Cleanup-DEBUG: 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 scope 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 scope 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
|
||||||
|
**/
|
153
glib/gcleanup.h
Normal file
153
glib/gcleanup.h
Normal file
@@ -0,0 +1,153 @@
|
|||||||
|
/*
|
||||||
|
* 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/gmain.h>
|
||||||
|
#include <glib/gquark.h>
|
||||||
|
#include <glib/gtypes.h>
|
||||||
|
|
||||||
|
G_BEGIN_DECLS
|
||||||
|
|
||||||
|
/* Don't use codes higher than 1000 or lower than -1000 */
|
||||||
|
enum {
|
||||||
|
G_CLEANUP_PHASE_EARLY = -50,
|
||||||
|
G_CLEANUP_PHASE_DEFAULT = 0,
|
||||||
|
G_CLEANUP_PHASE_LATE = 50,
|
||||||
|
G_CLEANUP_PHASE_GRAVEYARD = 100,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum {
|
||||||
|
G_CLEANUP_SCOPE_FORCE = 1 << 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
/*< private >*/
|
||||||
|
gint value;
|
||||||
|
gpointer priv[4];
|
||||||
|
} GCleanupScope;
|
||||||
|
|
||||||
|
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_push (GCleanupScope *cleanup,
|
||||||
|
gint phase,
|
||||||
|
GCleanupFunc cleanup_func,
|
||||||
|
gpointer user_data);
|
||||||
|
GLIB_AVAILABLE_IN_2_40
|
||||||
|
gpointer g_cleanup_push_pointer (GCleanupScope *cleanup,
|
||||||
|
gint phase,
|
||||||
|
GCleanupFunc cleanup_func,
|
||||||
|
gpointer *pointer_to_data);
|
||||||
|
GLIB_AVAILABLE_IN_2_40
|
||||||
|
void g_cleanup_push_source (GCleanupScope *cleanup,
|
||||||
|
gint phase,
|
||||||
|
GSource *source);
|
||||||
|
GLIB_AVAILABLE_IN_2_40 /* NOTE: annotate() very useful for debugging, but might not merge */
|
||||||
|
void g_cleanup_annotate (gpointer cleanup_item,
|
||||||
|
const gchar *func_name);
|
||||||
|
GLIB_AVAILABLE_IN_2_40
|
||||||
|
void g_cleanup_remove (gpointer cleanup_item);
|
||||||
|
GLIB_AVAILABLE_IN_2_40
|
||||||
|
GCleanupFunc g_cleanup_steal (gpointer cleanup_item,
|
||||||
|
gpointer *user_data);
|
||||||
|
GLIB_AVAILABLE_IN_2_40
|
||||||
|
void g_cleanup_clean (GCleanupScope *cleanup);
|
||||||
|
|
||||||
|
#if defined(G_CLEANUP_SCOPE) && defined(G_HAS_CONSTRUCTORS) && !defined(G_DEFINE_CONSTRUCTOR_NEEDS_PRAGMA)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* G_CLEANUP_SCOPE acts as a pointer with a constant address usable in initializers.
|
||||||
|
*/
|
||||||
|
|
||||||
|
extern GCleanupScope G_CLEANUP_SCOPE[1];
|
||||||
|
|
||||||
|
#define G_CLEANUP_DEFINE_WITH_FLAGS(flags) \
|
||||||
|
GCleanupScope G_CLEANUP_SCOPE[1] = { { flags, { 0, }, } }; \
|
||||||
|
G_DEFINE_DESTRUCTOR (G_PASTE (G_CLEANUP_SCOPE, _perform)) \
|
||||||
|
static void G_PASTE (G_CLEANUP_SCOPE, _perform) (void) { \
|
||||||
|
g_cleanup_clean (G_CLEANUP_SCOPE); \
|
||||||
|
}
|
||||||
|
#define G_CLEANUP_IN(data, func, phase) \
|
||||||
|
G_STMT_START { \
|
||||||
|
gpointer _it = g_cleanup_push (G_CLEANUP_SCOPE, phase, \
|
||||||
|
(void*) (func), (data)); \
|
||||||
|
g_cleanup_annotate (_it, G_STRINGIFY (func)); \
|
||||||
|
if (0) (func) ((data)); \
|
||||||
|
} G_STMT_END
|
||||||
|
#define G_CLEANUP_FUNC_IN(func, phase) \
|
||||||
|
G_STMT_START { \
|
||||||
|
gpointer _it = g_cleanup_push (G_CLEANUP_SCOPE, phase, \
|
||||||
|
(void*) (func), NULL); \
|
||||||
|
g_cleanup_annotate (_it, G_STRINGIFY (func)); \
|
||||||
|
if (0) (func) (); \
|
||||||
|
} G_STMT_END
|
||||||
|
#define G_CLEANUP_POINTER_IN(pp, func, phase) \
|
||||||
|
G_STMT_START { \
|
||||||
|
gpointer *_pp = (gpointer *)pp; \
|
||||||
|
gpointer _it = g_cleanup_push_pointer (G_CLEANUP_SCOPE, phase, \
|
||||||
|
(void*) (func), _pp); \
|
||||||
|
g_cleanup_annotate (_it, G_STRINGIFY (func)); \
|
||||||
|
if (0) (func) (*(pp)); \
|
||||||
|
} G_STMT_END
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
#define G_CLEANUP_SCOPE NULL
|
||||||
|
#define G_CLEANUP_DEFINE_WITH_FLAGS(flags)
|
||||||
|
#define G_CLEANUP_IN(data, func, phase) \
|
||||||
|
G_STMT_START { \
|
||||||
|
if (0) (func) (data); \
|
||||||
|
} G_STMT_END
|
||||||
|
#define G_CLEANUP_FUNC_IN(func, phase) \
|
||||||
|
G_STMT_START { \
|
||||||
|
if (0) (func) (); \
|
||||||
|
} G_STMT_END
|
||||||
|
#define G_CLEANUP_POINTER_IN(pp, func, phase) \
|
||||||
|
G_STMT_START { \
|
||||||
|
if (0) (func) (*(pp)); \
|
||||||
|
} G_STMT_END
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define G_CLEANUP_DEFINE \
|
||||||
|
G_CLEANUP_DEFINE_WITH_FLAGS(0)
|
||||||
|
#define G_CLEANUP(data, func) \
|
||||||
|
G_CLEANUP_IN (data, func, G_CLEANUP_PHASE_DEFAULT)
|
||||||
|
#define G_CLEANUP_FUNC(func) \
|
||||||
|
G_CLEANUP_FUNC_IN (func, G_CLEANUP_PHASE_DEFAULT)
|
||||||
|
#define G_CLEANUP_POINTER(pp, func) \
|
||||||
|
G_CLEANUP_POINTER_IN (func, G_CLEANUP_PHASE_DEFAULT)
|
||||||
|
|
||||||
|
G_END_DECLS
|
||||||
|
|
||||||
|
#endif /* __G_CLEANUP_H__ */
|
@@ -43,6 +43,7 @@ gboolean g_mem_gc_friendly = TRUE;
|
|||||||
#else
|
#else
|
||||||
gboolean g_mem_gc_friendly = FALSE;
|
gboolean g_mem_gc_friendly = FALSE;
|
||||||
#endif
|
#endif
|
||||||
|
gboolean g_cleanup_enabled = FALSE;
|
||||||
GLogLevelFlags g_log_msg_prefix = G_LOG_LEVEL_ERROR | G_LOG_LEVEL_WARNING |
|
GLogLevelFlags g_log_msg_prefix = G_LOG_LEVEL_ERROR | G_LOG_LEVEL_WARNING |
|
||||||
G_LOG_LEVEL_CRITICAL | G_LOG_LEVEL_DEBUG;
|
G_LOG_LEVEL_CRITICAL | G_LOG_LEVEL_DEBUG;
|
||||||
GLogLevelFlags g_log_always_fatal = G_LOG_FATAL_MASK;
|
GLogLevelFlags g_log_always_fatal = G_LOG_FATAL_MASK;
|
||||||
@@ -203,6 +204,8 @@ g_debug_init (void)
|
|||||||
{
|
{
|
||||||
const GDebugKey keys[] = {
|
const GDebugKey keys[] = {
|
||||||
{ "gc-friendly", 1 },
|
{ "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-warnings", G_LOG_LEVEL_WARNING | G_LOG_LEVEL_CRITICAL },
|
||||||
{"fatal-criticals", 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_log_always_fatal |= flags & G_LOG_LEVEL_MASK;
|
||||||
|
|
||||||
g_mem_gc_friendly = flags & 1;
|
g_mem_gc_friendly = flags & 1;
|
||||||
|
g_cleanup_enabled = (flags & 2) != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
@@ -26,6 +26,7 @@
|
|||||||
|
|
||||||
extern GLogLevelFlags g_log_always_fatal;
|
extern GLogLevelFlags g_log_always_fatal;
|
||||||
extern GLogLevelFlags g_log_msg_prefix;
|
extern GLogLevelFlags g_log_msg_prefix;
|
||||||
|
extern gboolean g_cleanup_enabled;
|
||||||
|
|
||||||
#ifdef G_OS_WIN32
|
#ifdef G_OS_WIN32
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
|
@@ -40,6 +40,7 @@
|
|||||||
#include <glib/gbytes.h>
|
#include <glib/gbytes.h>
|
||||||
#include <glib/gcharset.h>
|
#include <glib/gcharset.h>
|
||||||
#include <glib/gchecksum.h>
|
#include <glib/gchecksum.h>
|
||||||
|
#include <glib/gcleanup.h>
|
||||||
#include <glib/gconstructor.h>
|
#include <glib/gconstructor.h>
|
||||||
#include <glib/gconvert.h>
|
#include <glib/gconvert.h>
|
||||||
#include <glib/gdataset.h>
|
#include <glib/gdataset.h>
|
||||||
|
1
glib/tests/.gitignore
vendored
1
glib/tests/.gitignore
vendored
@@ -11,6 +11,7 @@ bookmarkfile
|
|||||||
bytes
|
bytes
|
||||||
cache
|
cache
|
||||||
checksum
|
checksum
|
||||||
|
cleanup
|
||||||
collate
|
collate
|
||||||
cond
|
cond
|
||||||
convert
|
convert
|
||||||
|
@@ -30,6 +30,7 @@ test_extra_programs = \
|
|||||||
$(NULL)
|
$(NULL)
|
||||||
|
|
||||||
test_programs = \
|
test_programs = \
|
||||||
|
cleanup \
|
||||||
array-test \
|
array-test \
|
||||||
asyncqueue \
|
asyncqueue \
|
||||||
base64 \
|
base64 \
|
||||||
|
197
glib/tests/cleanup.c
Normal file
197
glib/tests/cleanup.c
Normal file
@@ -0,0 +1,197 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2011 Red Hat, Inc.
|
||||||
|
*
|
||||||
|
* This program 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.
|
||||||
|
*
|
||||||
|
* See the included COPYING file for more information.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#undef G_CLEANUP_SCOPE
|
||||||
|
#define G_CLEANUP_SCOPE my_cleanup
|
||||||
|
|
||||||
|
#include <glib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
GCleanupScope G_CLEANUP_SCOPE[1] = { { G_CLEANUP_SCOPE_FORCE, } };
|
||||||
|
|
||||||
|
static void
|
||||||
|
cleanup_one (gpointer data)
|
||||||
|
{
|
||||||
|
gint *value = data;
|
||||||
|
g_assert_cmpint (*value, ==, 1);
|
||||||
|
(*value)++;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
cleanup_two (gpointer data)
|
||||||
|
{
|
||||||
|
gint *value = data;
|
||||||
|
g_assert_cmpint (*value, ==, 2);
|
||||||
|
(*value)++;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
cleanup_three (gpointer data)
|
||||||
|
{
|
||||||
|
gint *value = data;
|
||||||
|
g_assert_cmpint (*value, ==, 3);
|
||||||
|
(*value)++;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
cleanup_four (gpointer data)
|
||||||
|
{
|
||||||
|
gint *value = data;
|
||||||
|
g_assert_cmpint (*value, ==, 4);
|
||||||
|
(*value)++;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
cleanup_two_and_push_three (gpointer data)
|
||||||
|
{
|
||||||
|
cleanup_two (data);
|
||||||
|
|
||||||
|
/* Push another item */
|
||||||
|
G_CLEANUP_IN (data, cleanup_three, G_CLEANUP_PHASE_DEFAULT);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
test_push_and_clean (void)
|
||||||
|
{
|
||||||
|
gint value = 1;
|
||||||
|
|
||||||
|
/* This tests ordering of pushing three, and having it run before four is executed */
|
||||||
|
G_CLEANUP_IN (&value, cleanup_one, G_CLEANUP_PHASE_EARLY);
|
||||||
|
G_CLEANUP_IN (&value, cleanup_two_and_push_three, G_CLEANUP_PHASE_DEFAULT);
|
||||||
|
G_CLEANUP_IN (&value, cleanup_four, G_CLEANUP_PHASE_LATE);
|
||||||
|
|
||||||
|
g_cleanup_clean (my_cleanup);
|
||||||
|
|
||||||
|
g_assert_cmpint (value, ==, 5);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
test_push_and_remove (void)
|
||||||
|
{
|
||||||
|
gint value = 1;
|
||||||
|
gpointer item;
|
||||||
|
gpointer item2;
|
||||||
|
gpointer func;
|
||||||
|
gpointer data;
|
||||||
|
|
||||||
|
/* This tests ordering of pushing three, and having it run before four is executed */
|
||||||
|
G_CLEANUP_IN (&value, cleanup_one, G_CLEANUP_PHASE_EARLY);
|
||||||
|
|
||||||
|
item = g_cleanup_push (my_cleanup, G_CLEANUP_PHASE_EARLY, cleanup_three, &value);
|
||||||
|
item2 = g_cleanup_push (my_cleanup, G_CLEANUP_PHASE_EARLY, cleanup_four, &value);
|
||||||
|
|
||||||
|
G_CLEANUP_IN (&value, cleanup_two, G_CLEANUP_PHASE_DEFAULT);
|
||||||
|
G_CLEANUP_IN (item2, g_cleanup_remove, G_CLEANUP_PHASE_EARLY);
|
||||||
|
|
||||||
|
G_CLEANUP_IN (&value, cleanup_three, G_CLEANUP_PHASE_DEFAULT + 1);
|
||||||
|
G_CLEANUP_IN (&value, cleanup_four, G_CLEANUP_PHASE_LATE);
|
||||||
|
|
||||||
|
/* One remove happens before clean */
|
||||||
|
func = g_cleanup_steal (item, &data);
|
||||||
|
g_assert (func == cleanup_three);
|
||||||
|
g_assert (data == &value);
|
||||||
|
|
||||||
|
g_cleanup_clean (my_cleanup);
|
||||||
|
|
||||||
|
/* The other remove happened during clean */
|
||||||
|
|
||||||
|
g_assert_cmpint (value, ==, 5);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
cleanup_pointer (gchar *value)
|
||||||
|
{
|
||||||
|
g_assert_cmpstr (value, ==, "blah");
|
||||||
|
memcpy (value, "alot", 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
test_pointer (void)
|
||||||
|
{
|
||||||
|
gchar buf_one[] = "blah";
|
||||||
|
gchar *pointer_one = buf_one;
|
||||||
|
gchar *pointer_two = NULL;
|
||||||
|
gchar buf_three[] = "blah";
|
||||||
|
gchar *pointer_three = buf_three;
|
||||||
|
|
||||||
|
gpointer item;
|
||||||
|
|
||||||
|
/* This tests ordering of pushing three, and having it run before four is executed */
|
||||||
|
G_CLEANUP_POINTER_IN (&pointer_one, cleanup_pointer, 0);
|
||||||
|
G_CLEANUP_POINTER_IN (&pointer_two, cleanup_pointer, 0);
|
||||||
|
|
||||||
|
item = g_cleanup_push_pointer (my_cleanup, 0, (GCleanupFunc)cleanup_pointer, (gpointer *)&pointer_three);
|
||||||
|
G_CLEANUP_IN (item, g_cleanup_remove, G_CLEANUP_PHASE_EARLY);
|
||||||
|
|
||||||
|
g_cleanup_clean (my_cleanup);
|
||||||
|
|
||||||
|
g_assert_cmpstr (buf_one, ==, "alot");
|
||||||
|
g_assert (pointer_one == NULL);
|
||||||
|
g_assert (pointer_two == NULL);
|
||||||
|
g_assert_cmpstr (buf_three, ==, "blah");
|
||||||
|
g_assert (pointer_three == buf_three);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
test_source_cleaned (void)
|
||||||
|
{
|
||||||
|
static GSourceFuncs funcs = { NULL, };
|
||||||
|
GSource *source;
|
||||||
|
guint id;
|
||||||
|
|
||||||
|
source = g_source_new (&funcs, sizeof (GSource));
|
||||||
|
id = g_source_attach (source, NULL);
|
||||||
|
g_cleanup_push_source (my_cleanup, 0, source);
|
||||||
|
g_source_unref (source);
|
||||||
|
|
||||||
|
g_assert (g_main_context_find_source_by_id (NULL, id) != NULL);
|
||||||
|
|
||||||
|
g_cleanup_clean (my_cleanup);
|
||||||
|
|
||||||
|
g_assert (g_main_context_find_source_by_id (NULL, id) == NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
test_source_destroyed (void)
|
||||||
|
{
|
||||||
|
static GSourceFuncs funcs = { NULL, };
|
||||||
|
GSource *source;
|
||||||
|
guint id;
|
||||||
|
|
||||||
|
source = g_source_new (&funcs, sizeof (GSource));
|
||||||
|
id = g_source_attach (source, NULL);
|
||||||
|
g_cleanup_push_source (my_cleanup, 0, source);
|
||||||
|
g_source_unref (source);
|
||||||
|
|
||||||
|
g_assert (g_main_context_find_source_by_id (NULL, id) != NULL);
|
||||||
|
g_source_destroy (source);
|
||||||
|
|
||||||
|
g_assert (g_main_context_find_source_by_id (NULL, id) == NULL);
|
||||||
|
|
||||||
|
g_cleanup_clean (my_cleanup);
|
||||||
|
|
||||||
|
/* We're really checking for invalid memory access in this test */
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int
|
||||||
|
main (int argc, char **argv)
|
||||||
|
{
|
||||||
|
g_test_init (&argc, &argv, NULL);
|
||||||
|
|
||||||
|
g_test_add_func ("/cleanup/push-and-clean", test_push_and_clean);
|
||||||
|
g_test_add_func ("/cleanup/push-and-remove", test_push_and_remove);
|
||||||
|
g_test_add_func ("/cleanup/pointer", test_pointer);
|
||||||
|
g_test_add_func ("/cleanup/source-cleaned", test_source_cleaned);
|
||||||
|
g_test_add_func ("/cleanup/source-destroyed", test_source_destroyed);
|
||||||
|
|
||||||
|
return g_test_run ();
|
||||||
|
}
|
Reference in New Issue
Block a user