diff options
-rw-r--r-- | doc/README.wmem | 31 | ||||
-rw-r--r-- | epan/wmem/Makefile.common | 3 | ||||
-rw-r--r-- | epan/wmem/wmem_allocator.h | 12 | ||||
-rw-r--r-- | epan/wmem/wmem_core.c | 43 | ||||
-rw-r--r-- | epan/wmem/wmem_core.h | 15 | ||||
-rw-r--r-- | epan/wmem/wmem_test.c | 35 | ||||
-rw-r--r-- | epan/wmem/wmem_user_cb.h | 57 |
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: + */ |