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
This commit is contained in:
Evan Huus 2013-05-07 19:23:10 +00:00
parent 572d68a33b
commit 2e92c6dfde
7 changed files with 188 additions and 8 deletions

View File

@ -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

View File

@ -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
#

View File

@ -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

View File

@ -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);
}
@ -110,10 +123,39 @@ wmem_gc(wmem_allocator_t *allocator)
allocator->gc(allocator->private_data);
}
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;
}

View File

@ -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 *
@ -76,6 +80,11 @@ WS_DLL_PUBLIC
void
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);

View File

@ -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)
{

57
epan/wmem/wmem_user_cb.h Normal file
View File

@ -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:
*/