aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEvan Huus <eapache@gmail.com>2013-05-07 19:23:10 +0000
committerEvan Huus <eapache@gmail.com>2013-05-07 19:23:10 +0000
commit2e92c6dfdec952484ebe96ea1718467c2fa5de6b (patch)
tree021f78948c075de21a76e60d05672ad929b4e11e
parent572d68a33bc1fadcd0ff7b0de8b718fb98fc259c (diff)
Add user callbacks to wmem. This feature is a generic way to transparently mimic
the behaviour emem has for seasonal trees, which is that the master tree structure is not actually seasonal - it is permanent. When the seasonal memory pool is cleared, the root node pointer in all of these permanent trees is set to NULL, and the pool takes care of actually freeing the nodes. Wmem can now mimic this by allocating the tree header struct in epan_scope(), allocating any node structs in file_scope(), and registering a callback on file_scope() that NULLs the pointer in the epan_scope() header. Yes, this is confusing, but it seemed simpler than adding manual callback registrations to every single dissector that currently uses seasonal trees. The callbacks may also be useful for other things that need cleanup (I'm thinking resource handles stored in wmem memory that need to be fclosed or what-have-you before they the handle is lost). As indicated by the number of caveats in README.wmem, the implementation probably needs a bit of work to make it safer/saner/more-useful. Thoughts (or patches!) in this direction are more than welcome. svn path=/trunk/; revision=49205
-rw-r--r--doc/README.wmem31
-rw-r--r--epan/wmem/Makefile.common3
-rw-r--r--epan/wmem/wmem_allocator.h12
-rw-r--r--epan/wmem/wmem_core.c43
-rw-r--r--epan/wmem/wmem_core.h15
-rw-r--r--epan/wmem/wmem_test.c35
-rw-r--r--epan/wmem/wmem_user_cb.h57
7 files changed, 188 insertions, 8 deletions
diff --git a/doc/README.wmem b/doc/README.wmem
index 13ed863818..67f9967564 100644
--- a/doc/README.wmem
+++ b/doc/README.wmem
@@ -102,6 +102,37 @@ wmem_slist.h
wmem_stack.h
- A stack implementation (push, pop, etc).
+2.3 Callbacks
+
+WARNING: You probably don't actually need these; use them only when you're
+ sure you understand the implications and the consequences.
+
+Sometimes (though hopefully rarely) it may be necessary to store data in a wmem
+pool that requires additional cleanup before it is freed. For example, perhaps
+you have a pointer to a file-handle that needs to be closed. In this case, you
+can register a callback with the wmem_register_cleanup_callback function
+declared in wmem_core.h.
+
+This function takes the usual allocator, a function pointer (see wmem_user_cb_t
+also in wmem_core.h) and a void user_data pointer. Every time the memory in a
+pool is freed, all registered cleanup functions are called first, being passed
+a pointer to the allocator as well as whatever user_data was registered with
+that callback.
+
+WARNING: Callback calling order is not defined, you cannot rely on a certain
+ callback being called before or after another (in practice at the
+ moment it's first-in-last-out, but that may change).
+
+WARNING: Callbacks are not cleared when they are called - they are only cleared
+ when the pool is fully destroyed. (Do we need an unregister function?).
+
+WARNING: The user_data pointer is not freed when a callback is cleared, you
+ have to do that yourself (or just allocate it in the appropriate wmem
+ pool).
+
+WARNING: Calling wmem_free on allocated memory that a callback depends on will
+ not unregister that callback. Do not do this, it will crash!
+
3. Usage for Producers
NB: If you're just writing a dissector, you probably don't need to read
diff --git a/epan/wmem/Makefile.common b/epan/wmem/Makefile.common
index da9e6e5a51..12dbfb2e2a 100644
--- a/epan/wmem/Makefile.common
+++ b/epan/wmem/Makefile.common
@@ -45,7 +45,8 @@ LIBWMEM_INCLUDES = \
wmem_slist.h \
wmem_stack.h \
wmem_strbuf.h \
- wmem_strutl.h
+ wmem_strutl.h \
+ wmem_user_cb.h
#
diff --git a/epan/wmem/wmem_allocator.h b/epan/wmem/wmem_allocator.h
index baa7822326..296e206de5 100644
--- a/epan/wmem/wmem_allocator.h
+++ b/epan/wmem/wmem_allocator.h
@@ -26,6 +26,7 @@
#ifndef __WMEM_ALLOCATOR_H__
#define __WMEM_ALLOCATOR_H__
+#include <glib.h>
#include <string.h>
#ifdef __cplusplus
@@ -37,10 +38,6 @@ enum _wmem_allocator_type_t;
/* See section "4. Internal Design" of doc/README.wmem for details
* on this structure */
struct _wmem_allocator_t {
- /* Implementation details */
- void *private_data;
- enum _wmem_allocator_type_t type;
-
/* Consumer functions */
void *(*alloc)(void *private_data, const size_t size);
void (*free)(void *private_data, void *ptr);
@@ -50,6 +47,13 @@ struct _wmem_allocator_t {
void (*free_all)(void *private_data);
void (*gc)(void *private_data);
void (*destroy)(struct _wmem_allocator_t *allocator);
+
+ /* Callback List */
+ GSList *callbacks;
+
+ /* Implementation details */
+ void *private_data;
+ enum _wmem_allocator_type_t type;
};
#ifdef __cplusplus
diff --git a/epan/wmem/wmem_core.c b/epan/wmem/wmem_core.c
index 415826005a..8e0b0b1938 100644
--- a/epan/wmem/wmem_core.c
+++ b/epan/wmem/wmem_core.c
@@ -29,6 +29,7 @@
#include "wmem_core.h"
#include "wmem_scopes.h"
+#include "wmem_user_cb.h"
#include "wmem_allocator.h"
#include "wmem_allocator_simple.h"
#include "wmem_allocator_block.h"
@@ -101,6 +102,18 @@ wmem_realloc(wmem_allocator_t *allocator, void *ptr, const size_t size)
void
wmem_free_all(wmem_allocator_t *allocator)
{
+ GSList *tmp;
+ wmem_user_cb_container_t *cb;
+
+ /* Call all the user-registered callbacks */
+ tmp = allocator->callbacks;
+ while (tmp) {
+ cb = (wmem_user_cb_container_t*) tmp->data;
+ cb->cb(allocator, cb->user_data);
+ tmp = tmp->next;
+ }
+
+ /* Actually free-all */
allocator->free_all(allocator->private_data);
}
@@ -111,9 +124,38 @@ wmem_gc(wmem_allocator_t *allocator)
}
void
+wmem_register_cleanup_callback(wmem_allocator_t *allocator,
+ wmem_user_cb_t callback, void *user_data)
+{
+ wmem_user_cb_container_t *container;
+
+ container = g_slice_new(wmem_user_cb_container_t);
+
+ container->cb = callback;
+ container->user_data = user_data;
+
+ allocator->callbacks = g_slist_prepend(allocator->callbacks,
+ container);
+}
+
+void
wmem_destroy_allocator(wmem_allocator_t *allocator)
{
+ GSList *tmp;
+
+ /* Free-all first (this calls all the user-registered callbacks) */
wmem_free_all(allocator);
+
+ /* Destroy all user-registered callbacks
+ * (they were called in wmem_free_all) */
+ tmp = allocator->callbacks;
+ while (tmp) {
+ g_slice_free(wmem_user_cb_container_t, tmp->data);
+ tmp = tmp->next;
+ }
+ g_slist_free(allocator->callbacks);
+
+ /* Tell the allocator to destroy itself */
allocator->destroy(allocator);
}
@@ -166,6 +208,7 @@ wmem_allocator_new(const wmem_allocator_type_t type)
};
allocator->type = real_type;
+ allocator->callbacks = NULL;
return allocator;
}
diff --git a/epan/wmem/wmem_core.h b/epan/wmem/wmem_core.h
index 4ce51baba9..83735a7095 100644
--- a/epan/wmem/wmem_core.h
+++ b/epan/wmem/wmem_core.h
@@ -33,15 +33,19 @@
extern "C" {
#endif /* __cplusplus */
+/* Allocator structure and typedef */
+struct _wmem_allocator_t;
+typedef struct _wmem_allocator_t wmem_allocator_t;
+
+/* Different types of allocators */
typedef enum _wmem_allocator_type_t {
WMEM_ALLOCATOR_SIMPLE,
WMEM_ALLOCATOR_BLOCK,
WMEM_ALLOCATOR_STRICT
} wmem_allocator_type_t;
-struct _wmem_allocator_t;
-
-typedef struct _wmem_allocator_t wmem_allocator_t;
+/* User callback type for registering cleanup routines */
+typedef void (*wmem_user_cb_t) (wmem_allocator_t *, void *);
WS_DLL_PUBLIC
void *
@@ -78,6 +82,11 @@ wmem_gc(wmem_allocator_t *allocator);
WS_DLL_PUBLIC
void
+wmem_register_cleanup_callback(wmem_allocator_t *allocator,
+ wmem_user_cb_t callback, void *user_data);
+
+WS_DLL_PUBLIC
+void
wmem_destroy_allocator(wmem_allocator_t *allocator);
WS_DLL_PUBLIC
diff --git a/epan/wmem/wmem_test.c b/epan/wmem/wmem_test.c
index 7f17898a62..f1b502f55c 100644
--- a/epan/wmem/wmem_test.c
+++ b/epan/wmem/wmem_test.c
@@ -77,10 +77,28 @@ wmem_allocator_force_new(const wmem_allocator_type_t type)
};
allocator->type = type;
+ allocator->callbacks = NULL;
return allocator;
}
+/* Some helpers for properly testing the user callback functionality */
+wmem_allocator_t *cur_global_allocator;
+void *cur_global_user_data;
+gboolean cb_called;
+
+static void
+wmem_test_callback(wmem_allocator_t *allocator, void *user_data)
+{
+ g_assert(allocator == cur_global_allocator);
+ g_assert(user_data == cur_global_user_data);
+ g_assert(!cb_called);
+
+ cb_called = TRUE;
+}
+
+/* ALLOCATOR TESTING FUNCTIONS (/wmem/allocator/) */
+
static void
wmem_test_allocator(wmem_allocator_type_t type, wmem_verify_func verify)
{
@@ -105,8 +123,15 @@ wmem_test_allocator(wmem_allocator_type_t type, wmem_verify_func verify)
wmem_free(allocator, ptrs[i]);
}
+ wmem_register_cleanup_callback(allocator, &wmem_test_callback,
+ GINT_TO_POINTER(42));
+ cur_global_allocator = allocator;
+ cur_global_user_data = GINT_TO_POINTER(42);
+
if (verify) (*verify)(allocator);
+ cb_called = FALSE;
wmem_free_all(allocator);
+ g_assert(cb_called);
wmem_gc(allocator);
if (verify) (*verify)(allocator);
@@ -118,7 +143,9 @@ wmem_test_allocator(wmem_allocator_type_t type, wmem_verify_func verify)
}
if (verify) (*verify)(allocator);
+ cb_called = FALSE;
wmem_free_all(allocator);
+ g_assert(cb_called);
wmem_gc(allocator);
if (verify) (*verify)(allocator);
@@ -135,7 +162,9 @@ wmem_test_allocator(wmem_allocator_type_t type, wmem_verify_func verify)
}
if (verify) (*verify)(allocator);
+ cb_called = FALSE;
wmem_free_all(allocator);
+ g_assert(cb_called);
wmem_gc(allocator);
if (verify) (*verify)(allocator);
@@ -184,7 +213,9 @@ wmem_test_allocator(wmem_allocator_type_t type, wmem_verify_func verify)
if (verify) (*verify)(allocator);
}
+ cb_called = FALSE;
wmem_destroy_allocator(allocator);
+ g_assert(cb_called);
}
static void
@@ -249,6 +280,8 @@ wmem_test_allocator_strict(void)
wmem_test_allocator(WMEM_ALLOCATOR_STRICT, &wmem_strict_check_canaries);
}
+/* UTILITY TESTING FUNCTIONS (/wmem/utils/) */
+
static void
wmem_test_strutls(void)
{
@@ -280,6 +313,8 @@ wmem_test_strutls(void)
wmem_destroy_allocator(allocator);
}
+/* DATA STRUCTURE TESTING FUNCTIONS (/wmem/datastruct/) */
+
static void
wmem_test_slist(void)
{
diff --git a/epan/wmem/wmem_user_cb.h b/epan/wmem/wmem_user_cb.h
new file mode 100644
index 0000000000..8a5b093eb1
--- /dev/null
+++ b/epan/wmem/wmem_user_cb.h
@@ -0,0 +1,57 @@
+/* wmem_user_cb.h
+ * Definitions for the Wireshark Memory Manager User Callbacks
+ * Copyright 2012, Evan Huus <eapache@gmail.com>
+ *
+ * $Id$
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef __WMEM_USER_CB_H__
+#define __WMEM_USER_CB_H__
+
+#include "wmem_core.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+typedef struct _wmem_user_cb_container_t {
+ wmem_user_cb_t cb;
+ void *user_data;
+} wmem_user_cb_container_t;
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __WMEM_USER_CB_H__ */
+
+/*
+ * Editor modelines - http://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 4
+ * tab-width: 8
+ * indent-tabs-mode: nil
+ * End:
+ *
+ * vi: set shiftwidth=4 tabstop=8 expandtab:
+ * :indentSize=4:tabSize=8:noTabs=true:
+ */