mirror of
https://github.com/samba-team/samba.git
synced 2025-02-22 05:57:43 +03:00
r2791: got rid of talloc_unreference() and instead created talloc_unlink(),
which is much clearer and simpler to use. It removes a specific parent from a pointer, no matter whether that parent is a "reference" or a direct parent. This gives complete control over the free process. (This used to be commit 6c563887f1b9b8c842309a523e88b6f2a32db10f)
This commit is contained in:
parent
a248164de5
commit
15b9736ed3
@ -59,7 +59,7 @@ void *_talloc(const void *context, size_t size);
|
||||
void talloc_set_destructor(const void *ptr, int (*destructor)(void *));
|
||||
void talloc_increase_ref_count(const void *ptr);
|
||||
void *talloc_reference(const void *context, const void *ptr);
|
||||
void *talloc_unreference(const void *context, const void *ptr);
|
||||
int talloc_unlink(const void *context, void *ptr);
|
||||
void talloc_set_name(const void *ptr, const char *fmt, ...) PRINTF_ATTRIBUTE(2,3);
|
||||
void talloc_set_name_const(const void *ptr, const char *name);
|
||||
void *talloc_named(const void *context, size_t size,
|
||||
|
@ -241,7 +241,7 @@ void *talloc_reference(const void *context, const void *ptr)
|
||||
talloc_reference() has done. The context and pointer arguments
|
||||
must match those given to a talloc_reference()
|
||||
*/
|
||||
void *talloc_unreference(const void *context, const void *ptr)
|
||||
static int talloc_unreference(const void *context, const void *ptr)
|
||||
{
|
||||
struct talloc_chunk *tc = talloc_chunk_from_ptr(ptr);
|
||||
struct talloc_reference_handle *h;
|
||||
@ -255,13 +255,58 @@ void *talloc_unreference(const void *context, const void *ptr)
|
||||
if ((p==NULL && context==NULL) || p+1 == context) break;
|
||||
}
|
||||
if (h == NULL) {
|
||||
return NULL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
talloc_set_destructor(h, NULL);
|
||||
_TLIST_REMOVE(tc->refs, h);
|
||||
talloc_free(h);
|
||||
return discard_const_p(void, ptr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
remove a specific parent context from a pointer. This is a more
|
||||
controlled varient of talloc_free()
|
||||
*/
|
||||
int talloc_unlink(const void *context, void *ptr)
|
||||
{
|
||||
struct talloc_chunk *tc_p, *new_p;
|
||||
void *new_parent;
|
||||
|
||||
if (talloc_unreference(context, ptr) == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (context == NULL) {
|
||||
if (talloc_parent_chunk(ptr) != NULL) {
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
if (talloc_chunk_from_ptr(context) != talloc_parent_chunk(ptr)) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
tc_p = talloc_chunk_from_ptr(ptr);
|
||||
|
||||
if (tc_p->refs == NULL) {
|
||||
return talloc_free(ptr);
|
||||
}
|
||||
|
||||
new_p = talloc_parent_chunk(tc_p->refs);
|
||||
if (new_p) {
|
||||
new_parent = new_p+1;
|
||||
} else {
|
||||
new_parent = NULL;
|
||||
}
|
||||
|
||||
if (talloc_unreference(new_parent, ptr) != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
talloc_steal(new_parent, ptr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -20,7 +20,48 @@
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#ifdef _STANDALONE_
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdarg.h>
|
||||
#include <sys/time.h>
|
||||
#include <time.h>
|
||||
#include "talloc.h"
|
||||
#else
|
||||
#include "includes.h"
|
||||
#endif
|
||||
|
||||
/* the test suite can be built standalone, or as part of Samba */
|
||||
#ifdef _STANDALONE_
|
||||
typedef enum {False=0,True=1} BOOL;
|
||||
|
||||
static struct timeval tp1,tp2;
|
||||
|
||||
static void start_timer(void)
|
||||
{
|
||||
gettimeofday(&tp1,NULL);
|
||||
}
|
||||
|
||||
static double end_timer(void)
|
||||
{
|
||||
gettimeofday(&tp2,NULL);
|
||||
return((tp2.tv_sec - tp1.tv_sec) +
|
||||
(tp2.tv_usec - tp1.tv_usec)*1.0e-6);
|
||||
}
|
||||
#endif /* _STANDALONE_ */
|
||||
|
||||
|
||||
#define CHECK_SIZE(ptr, tsize) do { \
|
||||
if (talloc_total_size(ptr) != (tsize)) { \
|
||||
printf(__location__ " failed: wrong '%s' tree size: got %u expected %u\n", \
|
||||
#ptr, \
|
||||
(unsigned)talloc_total_size(ptr), \
|
||||
(unsigned)tsize); \
|
||||
talloc_report_full(ptr, stdout); \
|
||||
return False; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define CHECK_BLOCKS(ptr, tblocks) do { \
|
||||
if (talloc_total_blocks(ptr) != (tblocks)) { \
|
||||
@ -33,16 +74,6 @@
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define CHECK_SIZE(ptr, tsize) do { \
|
||||
if (talloc_total_size(ptr) != (tsize)) { \
|
||||
printf(__location__ " failed: wrong '%s' tree size: got %u expected %u\n", \
|
||||
#ptr, \
|
||||
(unsigned)talloc_total_size(ptr), \
|
||||
(unsigned)tsize); \
|
||||
talloc_report_full(ptr, stdout); \
|
||||
return False; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
/*
|
||||
test references
|
||||
@ -241,11 +272,11 @@ static BOOL test_ref4(void)
|
||||
/*
|
||||
test references
|
||||
*/
|
||||
static BOOL test_unref1(void)
|
||||
static BOOL test_unlink1(void)
|
||||
{
|
||||
void *root, *p1, *p2, *ref, *r1;
|
||||
|
||||
printf("TESTING UNREFERENCE\n");
|
||||
printf("TESTING UNLINK\n");
|
||||
|
||||
root = talloc_named_const(NULL, 0, "root");
|
||||
p1 = talloc_named_const(root, 1, "p1");
|
||||
@ -263,7 +294,7 @@ static BOOL test_unref1(void)
|
||||
CHECK_BLOCKS(r1, 2);
|
||||
|
||||
printf("Unreferencing r1\n");
|
||||
talloc_unreference(r1, p2);
|
||||
talloc_unlink(r1, p2);
|
||||
talloc_report_full(root, stdout);
|
||||
|
||||
CHECK_BLOCKS(p1, 6);
|
||||
@ -314,11 +345,16 @@ static BOOL test_misc(void)
|
||||
talloc_free(p1);
|
||||
CHECK_BLOCKS(p1, 1);
|
||||
CHECK_BLOCKS(root, 2);
|
||||
talloc_unreference(NULL, p1);
|
||||
talloc_unlink(NULL, p1);
|
||||
CHECK_BLOCKS(p1, 1);
|
||||
CHECK_BLOCKS(root, 2);
|
||||
if (talloc_unreference(root, p1) != NULL) {
|
||||
printf("failed: talloc_unreference() of non-reference context should return NULL\n");
|
||||
p2 = talloc_strdup(p1, "foo");
|
||||
if (talloc_unlink(root, p2) != -1) {
|
||||
printf("failed: talloc_unlink() of non-reference context should return -1\n");
|
||||
return False;
|
||||
}
|
||||
if (talloc_unlink(p1, p2) != 0) {
|
||||
printf("failed: talloc_unlink() of parent should succeed\n");
|
||||
return False;
|
||||
}
|
||||
talloc_free(p1);
|
||||
@ -406,6 +442,44 @@ static BOOL test_misc(void)
|
||||
talloc_free(p1);
|
||||
CHECK_BLOCKS(root, 1);
|
||||
|
||||
p1 = talloc_named(root, 100, "%d bytes", 100);
|
||||
CHECK_BLOCKS(p1, 2);
|
||||
CHECK_BLOCKS(root, 3);
|
||||
talloc_unlink(root, p1);
|
||||
|
||||
p1 = talloc_init("%d bytes", 200);
|
||||
p2 = talloc_asprintf(p1, "my test '%s'", "string");
|
||||
CHECK_BLOCKS(p1, 3);
|
||||
CHECK_SIZE(p2, 17);
|
||||
CHECK_BLOCKS(root, 1);
|
||||
talloc_unlink(NULL, p1);
|
||||
|
||||
p1 = talloc_named_const(root, 10, "p1");
|
||||
p2 = talloc_named_const(root, 20, "p2");
|
||||
talloc_reference(p1, p2);
|
||||
talloc_report_full(root, stdout);
|
||||
talloc_unlink(root, p2);
|
||||
talloc_report_full(root, stdout);
|
||||
CHECK_BLOCKS(p2, 1);
|
||||
CHECK_BLOCKS(p1, 2);
|
||||
CHECK_BLOCKS(root, 3);
|
||||
talloc_unlink(p1, p2);
|
||||
talloc_unlink(root, p1);
|
||||
|
||||
p1 = talloc_named_const(root, 10, "p1");
|
||||
p2 = talloc_named_const(root, 20, "p2");
|
||||
talloc_reference(NULL, p2);
|
||||
talloc_report_full(root, stdout);
|
||||
talloc_unlink(root, p2);
|
||||
talloc_report_full(root, stdout);
|
||||
CHECK_BLOCKS(p2, 1);
|
||||
CHECK_BLOCKS(p1, 1);
|
||||
CHECK_BLOCKS(root, 2);
|
||||
talloc_unlink(NULL, p2);
|
||||
talloc_unlink(root, p1);
|
||||
|
||||
|
||||
|
||||
talloc_report(root, stdout);
|
||||
talloc_report(NULL, stdout);
|
||||
|
||||
@ -567,7 +641,7 @@ static BOOL test_ldb(void)
|
||||
static BOOL test_speed(void)
|
||||
{
|
||||
void *ctx = talloc(NULL, 0);
|
||||
uint_t count;
|
||||
unsigned count;
|
||||
|
||||
printf("MEASURING TALLOC VS MALLOC SPEED\n");
|
||||
|
||||
@ -609,18 +683,32 @@ BOOL torture_local_talloc(int dummy)
|
||||
{
|
||||
BOOL ret = True;
|
||||
|
||||
init_iconv();
|
||||
|
||||
ret &= test_ref1();
|
||||
ret &= test_ref2();
|
||||
ret &= test_ref3();
|
||||
ret &= test_ref4();
|
||||
ret &= test_unref1();
|
||||
ret &= test_unlink1();
|
||||
ret &= test_misc();
|
||||
ret &= test_realloc();
|
||||
ret &= test_steal();
|
||||
ret &= test_ldb();
|
||||
ret &= test_speed();
|
||||
if (ret) {
|
||||
ret &= test_speed();
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
|
||||
#ifdef _STANDALONE_
|
||||
int main(void)
|
||||
{
|
||||
if (!torture_local_talloc(0)) {
|
||||
printf("ERROR: TESTSUIE FAILED\n");
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -33,10 +33,9 @@ n-ary tree, where you can free any part of the tree with
|
||||
talloc_free().
|
||||
|
||||
If you find this confusing, then I suggest you run the LOCAL-TALLOC
|
||||
smbtorture test with the --leak-report-full option to watch talloc in
|
||||
action. You may also like to add your own tests to
|
||||
source/torture/local/talloc.c to clarify how some particular situation
|
||||
is handled.
|
||||
smbtorture test to watch talloc in action. You may also like to add
|
||||
your own tests to source/torture/local/talloc.c to clarify how some
|
||||
particular situation is handled.
|
||||
|
||||
|
||||
Performance
|
||||
@ -103,9 +102,11 @@ destructor returned -1. See talloc_set_destructor() for details on
|
||||
destructors.
|
||||
|
||||
If this pointer has an additional reference when talloc_free() is
|
||||
called then the memory is not actually released, but instead the
|
||||
reference is destroyed. See talloc_reference() for details on
|
||||
establishing additional references.
|
||||
called then the memory is not actually released, but instead the most
|
||||
recently established reference is destroyed. See talloc_reference()
|
||||
for details on establishing additional references.
|
||||
|
||||
For more control on which parent is removed, see talloc_unlink()
|
||||
|
||||
talloc_free() operates recursively on its children.
|
||||
|
||||
@ -137,21 +138,22 @@ ways:
|
||||
reference. That will destroy the reference, and leave the pointer
|
||||
where it is.
|
||||
|
||||
For more control on which parent to remove, see talloc_unlink()
|
||||
|
||||
|
||||
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
|
||||
void *talloc_unreference(const void *context, const void *ptr);
|
||||
int talloc_unlink(const void *context, const void *ptr);
|
||||
|
||||
The talloc_unreference() function removes a reference added by
|
||||
talloc_reference(). It must be called with exactly the same arguments
|
||||
as talloc_reference().
|
||||
The talloc_unlink() function removes a specific parent from ptr. The
|
||||
context passed must either be a context used in talloc_reference()
|
||||
with this pointer, or must be a direct parent of ptr.
|
||||
|
||||
Note that if the reference has already been removed using
|
||||
talloc_free() then this function will fail and will return NULL.
|
||||
Note that if the parent has already been removed using talloc_free()
|
||||
then this function will fail and will return -1.
|
||||
|
||||
Usually you can just use talloc_free() instead of
|
||||
talloc_unreference(), but sometimes it is useful to have the
|
||||
additional control on who becomes the parent of the pointer given by
|
||||
talloc_unreference().
|
||||
Usually you can just use talloc_free() instead of talloc_unlink(), but
|
||||
sometimes it is useful to have the additional control on which parent
|
||||
is removed.
|
||||
|
||||
|
||||
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
|
||||
|
Loading…
x
Reference in New Issue
Block a user