lib/talloc: Sync talloc from samba git tree
authorAmitay Isaacs <amitay@gmail.com>
Thu, 12 Apr 2012 03:43:44 +0000 (13:43 +1000)
committerAmitay Isaacs <amitay@gmail.com>
Fri, 13 Apr 2012 07:28:51 +0000 (17:28 +1000)
Signed-off-by: Amitay Isaacs <amitay@gmail.com>
(This used to be ctdb commit 670ebada4bafe79bd535c9bb5cc2d5d49d646af3)

ctdb/lib/talloc/ABI/talloc-2.0.7.sigs [new file with mode: 0644]
ctdb/lib/talloc/talloc.3.xml
ctdb/lib/talloc/talloc.c
ctdb/lib/talloc/talloc.h
ctdb/lib/talloc/talloc_guide.txt
ctdb/lib/talloc/talloc_testsuite.h [new file with mode: 0644]
ctdb/lib/talloc/testsuite.c
ctdb/lib/talloc/testsuite_main.c

diff --git a/ctdb/lib/talloc/ABI/talloc-2.0.7.sigs b/ctdb/lib/talloc/ABI/talloc-2.0.7.sigs
new file mode 100644 (file)
index 0000000..6e236d5
--- /dev/null
@@ -0,0 +1,62 @@
+_talloc: void *(const void *, size_t)
+_talloc_array: void *(const void *, size_t, unsigned int, const char *)
+_talloc_free: int (void *, const char *)
+_talloc_get_type_abort: void *(const void *, const char *, const char *)
+_talloc_memdup: void *(const void *, const void *, size_t, const char *)
+_talloc_move: void *(const void *, const void *)
+_talloc_realloc: void *(const void *, void *, size_t, const char *)
+_talloc_realloc_array: void *(const void *, void *, size_t, unsigned int, const char *)
+_talloc_reference_loc: void *(const void *, const void *, const char *)
+_talloc_set_destructor: void (const void *, int (*)(void *))
+_talloc_steal_loc: void *(const void *, const void *, const char *)
+_talloc_zero: void *(const void *, size_t, const char *)
+_talloc_zero_array: void *(const void *, size_t, unsigned int, const char *)
+talloc_asprintf: char *(const void *, const char *, ...)
+talloc_asprintf_append: char *(char *, const char *, ...)
+talloc_asprintf_append_buffer: char *(char *, const char *, ...)
+talloc_autofree_context: void *(void)
+talloc_check_name: void *(const void *, const char *)
+talloc_disable_null_tracking: void (void)
+talloc_enable_leak_report: void (void)
+talloc_enable_leak_report_full: void (void)
+talloc_enable_null_tracking: void (void)
+talloc_enable_null_tracking_no_autofree: void (void)
+talloc_find_parent_byname: void *(const void *, const char *)
+talloc_free_children: void (void *)
+talloc_get_name: const char *(const void *)
+talloc_get_size: size_t (const void *)
+talloc_increase_ref_count: int (const void *)
+talloc_init: void *(const char *, ...)
+talloc_is_parent: int (const void *, const void *)
+talloc_named: void *(const void *, size_t, const char *, ...)
+talloc_named_const: void *(const void *, size_t, const char *)
+talloc_parent: void *(const void *)
+talloc_parent_name: const char *(const void *)
+talloc_pool: void *(const void *, size_t)
+talloc_realloc_fn: void *(const void *, void *, size_t)
+talloc_reference_count: size_t (const void *)
+talloc_reparent: void *(const void *, const void *, const void *)
+talloc_report: void (const void *, FILE *)
+talloc_report_depth_cb: void (const void *, int, int, void (*)(const void *, int, int, int, void *), void *)
+talloc_report_depth_file: void (const void *, int, int, FILE *)
+talloc_report_full: void (const void *, FILE *)
+talloc_set_abort_fn: void (void (*)(const char *))
+talloc_set_log_fn: void (void (*)(const char *))
+talloc_set_log_stderr: void (void)
+talloc_set_name: const char *(const void *, const char *, ...)
+talloc_set_name_const: void (const void *, const char *)
+talloc_show_parents: void (const void *, FILE *)
+talloc_strdup: char *(const void *, const char *)
+talloc_strdup_append: char *(char *, const char *)
+talloc_strdup_append_buffer: char *(char *, const char *)
+talloc_strndup: char *(const void *, const char *, size_t)
+talloc_strndup_append: char *(char *, const char *, size_t)
+talloc_strndup_append_buffer: char *(char *, const char *, size_t)
+talloc_total_blocks: size_t (const void *)
+talloc_total_size: size_t (const void *)
+talloc_unlink: int (const void *, void *)
+talloc_vasprintf: char *(const void *, const char *, va_list)
+talloc_vasprintf_append: char *(char *, const char *, va_list)
+talloc_vasprintf_append_buffer: char *(char *, const char *, va_list)
+talloc_version_major: int (void)
+talloc_version_minor: int (void)
index bc1676df50cfa4aa10bca3319a8d65348adc2f05..7a2f27e77b03166059f678a4f107cae8500e738c 100644 (file)
@@ -10,7 +10,7 @@
 <refpurpose>hierarchical reference counted memory pool system with destructors</refpurpose>
   </refnamediv>
   <refsynopsisdiv>
-<synopsis>#include &lt;talloc/talloc.h&gt;</synopsis>
+<synopsis>#include &lt;talloc.h&gt;</synopsis>
   </refsynopsisdiv>
   <refsect1><title>DESCRIPTION</title>
     <para>
@@ -645,17 +645,6 @@ if (ptr) memcpy(ptr, p, strlen(p)+1);</programlisting>
         </para>
         <programlisting>talloc_set_name_const(ptr, ptr)</programlisting>
     </refsect2>
-    <refsect2><title>char *talloc_append_string(const void *<emphasis role="italic">t</emphasis>, char *<emphasis role="italic">orig</emphasis>, const char *<emphasis role="italic">append</emphasis>);</title>
-        <para>
-         The talloc_append_string() function appends the given formatted
-         string to the given string.
-        </para>
-        <para>
-         This function sets the name of the new pointer to the new
-         string. This is equivalent to:
-        </para>
-        <programlisting>talloc_set_name_const(ptr, ptr)</programlisting>
-    </refsect2>
     <refsect2><title>char *talloc_vasprintf(const void *<emphasis role="italic">t</emphasis>, const char *<emphasis role="italic">fmt</emphasis>, va_list <emphasis role="italic">ap</emphasis>);</title>
         <para>
          The talloc_vasprintf() function is the talloc equivalent of the C
@@ -794,9 +783,9 @@ if (ptr) memcpy(ptr, p, strlen(p)+1);</programlisting>
     </para>
     <para>
       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 3 of the License, or (at
-      your option) any later version.
+      it under the terms of the GNU Lesser General Public License as
+      published by the Free Software Foundation; either version 3 of the
+      License, or (at your option) any later version.
     </para>
     <para>
       This program is distributed in the hope that it will be useful, but
index c8b042856c7b9bee4a9b38560611633c8bd8a90d..362293400247e4ab58452b0c061df1d1cd162cf0 100644 (file)
@@ -32,7 +32,6 @@
 
 #include "replace.h"
 #include "talloc.h"
-#include "includes.h"
 
 #ifdef TALLOC_BUILD_VERSION_MAJOR
 #if (TALLOC_VERSION_MAJOR != TALLOC_BUILD_VERSION_MAJOR)
 #endif
 #endif
 
+/* Special macros that are no-ops except when run under Valgrind on
+ * x86.  They've moved a little bit from valgrind 1.0.4 to 1.9.4 */
+#ifdef HAVE_VALGRIND_MEMCHECK_H
+        /* memcheck.h includes valgrind.h */
+#include <valgrind/memcheck.h>
+#elif defined(HAVE_VALGRIND_H)
+#include <valgrind.h>
+#endif
+
 /* use this to force every realloc to change the pointer, to stress test
    code that might not cope */
 #define ALWAYS_REALLOC 0
 static void *null_context;
 static void *autofree_context;
 
+/* used to enable fill of memory on free, which can be useful for
+ * catching use after free errors when valgrind is too slow
+ */
+static struct {
+       bool initialised;
+       bool enabled;
+       uint8_t fill_value;
+} talloc_fill;
+
+#define TALLOC_FILL_ENV "TALLOC_FREE_FILL"
+
+/*
+ * do not wipe the header, to allow the
+ * double-free logic to still work
+ */
+#define TC_INVALIDATE_FULL_FILL_CHUNK(_tc) do { \
+       if (unlikely(talloc_fill.enabled)) { \
+               size_t _flen = (_tc)->size; \
+               char *_fptr = (char *)TC_PTR_FROM_CHUNK(_tc); \
+               memset(_fptr, talloc_fill.fill_value, _flen); \
+       } \
+} while (0)
+
+#if defined(DEVELOPER) && defined(VALGRIND_MAKE_MEM_NOACCESS)
+/* Mark the whole chunk as not accessable */
+#define TC_INVALIDATE_FULL_VALGRIND_CHUNK(_tc) do { \
+       size_t _flen = TC_HDR_SIZE + (_tc)->size; \
+       char *_fptr = (char *)(_tc); \
+       VALGRIND_MAKE_MEM_NOACCESS(_fptr, _flen); \
+} while(0)
+#else
+#define TC_INVALIDATE_FULL_VALGRIND_CHUNK(_tc) do { } while (0)
+#endif
+
+#define TC_INVALIDATE_FULL_CHUNK(_tc) do { \
+       TC_INVALIDATE_FULL_FILL_CHUNK(_tc); \
+       TC_INVALIDATE_FULL_VALGRIND_CHUNK(_tc); \
+} while (0)
+
+#define TC_INVALIDATE_SHRINK_FILL_CHUNK(_tc, _new_size) do { \
+       if (unlikely(talloc_fill.enabled)) { \
+               size_t _flen = (_tc)->size - (_new_size); \
+               char *_fptr = (char *)TC_PTR_FROM_CHUNK(_tc); \
+               _fptr += (_new_size); \
+               memset(_fptr, talloc_fill.fill_value, _flen); \
+       } \
+} while (0)
+
+#if defined(DEVELOPER) && defined(VALGRIND_MAKE_MEM_NOACCESS)
+/* Mark the unused bytes not accessable */
+#define TC_INVALIDATE_SHRINK_VALGRIND_CHUNK(_tc, _new_size) do { \
+       size_t _flen = (_tc)->size - (_new_size); \
+       char *_fptr = (char *)TC_PTR_FROM_CHUNK(_tc); \
+       _fptr += (_new_size); \
+       VALGRIND_MAKE_MEM_NOACCESS(_fptr, _flen); \
+} while (0)
+#else
+#define TC_INVALIDATE_SHRINK_VALGRIND_CHUNK(_tc, _new_size) do { } while (0)
+#endif
+
+#define TC_INVALIDATE_SHRINK_CHUNK(_tc, _new_size) do { \
+       TC_INVALIDATE_SHRINK_FILL_CHUNK(_tc, _new_size); \
+       TC_INVALIDATE_SHRINK_VALGRIND_CHUNK(_tc, _new_size); \
+} while (0)
+
+#define TC_UNDEFINE_SHRINK_FILL_CHUNK(_tc, _new_size) do { \
+       if (unlikely(talloc_fill.enabled)) { \
+               size_t _flen = (_tc)->size - (_new_size); \
+               char *_fptr = (char *)TC_PTR_FROM_CHUNK(_tc); \
+               _fptr += (_new_size); \
+               memset(_fptr, talloc_fill.fill_value, _flen); \
+       } \
+} while (0)
+
+#if defined(DEVELOPER) && defined(VALGRIND_MAKE_MEM_UNDEFINED)
+/* Mark the unused bytes as undefined */
+#define TC_UNDEFINE_SHRINK_VALGRIND_CHUNK(_tc, _new_size) do { \
+       size_t _flen = (_tc)->size - (_new_size); \
+       char *_fptr = (char *)TC_PTR_FROM_CHUNK(_tc); \
+       _fptr += (_new_size); \
+       VALGRIND_MAKE_MEM_UNDEFINED(_fptr, _flen); \
+} while (0)
+#else
+#define TC_UNDEFINE_SHRINK_VALGRIND_CHUNK(_tc, _new_size) do { } while (0)
+#endif
+
+#define TC_UNDEFINE_SHRINK_CHUNK(_tc, _new_size) do { \
+       TC_UNDEFINE_SHRINK_FILL_CHUNK(_tc, _new_size); \
+       TC_UNDEFINE_SHRINK_VALGRIND_CHUNK(_tc, _new_size); \
+} while (0)
+
+#if defined(DEVELOPER) && defined(VALGRIND_MAKE_MEM_UNDEFINED)
+/* Mark the new bytes as undefined */
+#define TC_UNDEFINE_GROW_VALGRIND_CHUNK(_tc, _new_size) do { \
+       size_t _old_used = TC_HDR_SIZE + (_tc)->size; \
+       size_t _new_used = TC_HDR_SIZE + (_new_size); \
+       size_t _flen = _new_used - _old_used; \
+       char *_fptr = _old_used + (char *)(_tc); \
+       VALGRIND_MAKE_MEM_UNDEFINED(_fptr, _flen); \
+} while (0)
+#else
+#define TC_UNDEFINE_GROW_VALGRIND_CHUNK(_tc, _new_size) do { } while (0)
+#endif
+
+#define TC_UNDEFINE_GROW_CHUNK(_tc, _new_size) do { \
+       TC_UNDEFINE_GROW_VALGRIND_CHUNK(_tc, _new_size); \
+} while (0)
+
 struct talloc_reference_handle {
        struct talloc_reference_handle *next, *prev;
        void *ptr;
@@ -137,7 +253,8 @@ struct talloc_chunk {
 };
 
 /* 16 byte alignment seems to keep everyone happy */
-#define TC_HDR_SIZE ((sizeof(struct talloc_chunk)+15)&~15)
+#define TC_ALIGN16(s) (((s)+15)&~15)
+#define TC_HDR_SIZE TC_ALIGN16(sizeof(struct talloc_chunk))
 #define TC_PTR_FROM_CHUNK(tc) ((void *)(TC_HDR_SIZE + (char*)tc))
 
 _PUBLIC_ int talloc_version_major(void)
@@ -214,9 +331,9 @@ static void talloc_abort_magic(unsigned magic)
        talloc_abort("Bad talloc magic value - wrong talloc version used/mixed");
 }
 
-static void talloc_abort_double_free(void)
+static void talloc_abort_access_after_free(void)
 {
-       talloc_abort("Bad talloc magic value - double free");
+       talloc_abort("Bad talloc magic value - access after free");
 }
 
 static void talloc_abort_unknown_value(void)
@@ -236,8 +353,8 @@ static inline struct talloc_chunk *talloc_chunk_from_ptr(const void *ptr)
                }
 
                if (tc->flags & TALLOC_FLAG_FREE) {
-                       talloc_log("talloc: double free error - first free may be at %s\n", tc->name);
-                       talloc_abort_double_free();
+                       talloc_log("talloc: access after free error - first free may be at %s\n", tc->name);
+                       talloc_abort_access_after_free();
                        return NULL;
                } else {
                        talloc_abort_unknown_value();
@@ -322,9 +439,47 @@ _PUBLIC_ const char *talloc_parent_name(const void *ptr)
 
 #define TALLOC_POOL_HDR_SIZE 16
 
+#define TC_POOL_SPACE_LEFT(_pool_tc) \
+       PTR_DIFF(TC_HDR_SIZE + (_pool_tc)->size + (char *)(_pool_tc), \
+                (_pool_tc)->pool)
+
+#define TC_POOL_FIRST_CHUNK(_pool_tc) \
+       ((void *)(TC_HDR_SIZE + TALLOC_POOL_HDR_SIZE + (char *)(_pool_tc)))
+
+#define TC_POOLMEM_CHUNK_SIZE(_tc) \
+       TC_ALIGN16(TC_HDR_SIZE + (_tc)->size)
+
+#define TC_POOLMEM_NEXT_CHUNK(_tc) \
+       ((void *)(TC_POOLMEM_CHUNK_SIZE(tc) + (char*)(_tc)))
+
+/* Mark the whole remaining pool as not accessable */
+#define TC_INVALIDATE_FILL_POOL(_pool_tc) do { \
+       if (unlikely(talloc_fill.enabled)) { \
+               size_t _flen = TC_POOL_SPACE_LEFT(_pool_tc); \
+               char *_fptr = (char *)(_pool_tc)->pool; \
+               memset(_fptr, talloc_fill.fill_value, _flen); \
+       } \
+} while(0)
+
+#if defined(DEVELOPER) && defined(VALGRIND_MAKE_MEM_NOACCESS)
+/* Mark the whole remaining pool as not accessable */
+#define TC_INVALIDATE_VALGRIND_POOL(_pool_tc) do { \
+       size_t _flen = TC_POOL_SPACE_LEFT(_pool_tc); \
+       char *_fptr = (char *)(_pool_tc)->pool; \
+       VALGRIND_MAKE_MEM_NOACCESS(_fptr, _flen); \
+} while(0)
+#else
+#define TC_INVALIDATE_VALGRIND_POOL(_pool_tc) do { } while (0)
+#endif
+
+#define TC_INVALIDATE_POOL(_pool_tc) do { \
+       TC_INVALIDATE_FILL_POOL(_pool_tc); \
+       TC_INVALIDATE_VALGRIND_POOL(_pool_tc); \
+} while (0)
+
 static unsigned int *talloc_pool_objectcount(struct talloc_chunk *tc)
 {
-       return (unsigned int *)((char *)tc + sizeof(struct talloc_chunk));
+       return (unsigned int *)((char *)tc + TC_HDR_SIZE);
 }
 
 /*
@@ -354,13 +509,12 @@ static struct talloc_chunk *talloc_alloc_pool(struct talloc_chunk *parent,
                return NULL;
        }
 
-       space_left = ((char *)pool_ctx + TC_HDR_SIZE + pool_ctx->size)
-               - ((char *)pool_ctx->pool);
+       space_left = TC_POOL_SPACE_LEFT(pool_ctx);
 
        /*
         * Align size to 16 bytes
         */
-       chunk_size = ((size + 15) & ~15);
+       chunk_size = TC_ALIGN16(size);
 
        if (space_left < chunk_size) {
                return NULL;
@@ -451,13 +605,11 @@ _PUBLIC_ void *talloc_pool(const void *context, size_t size)
        tc = talloc_chunk_from_ptr(result);
 
        tc->flags |= TALLOC_FLAG_POOL;
-       tc->pool = (char *)result + TALLOC_POOL_HDR_SIZE;
+       tc->pool = TC_POOL_FIRST_CHUNK(tc);
 
        *talloc_pool_objectcount(tc) = 1;
 
-#if defined(DEVELOPER) && defined(VALGRIND_MAKE_MEM_NOACCESS)
-       VALGRIND_MAKE_MEM_NOACCESS(tc->pool, size);
-#endif
+       TC_INVALIDATE_POOL(tc);
 
        return result;
 }
@@ -557,6 +709,69 @@ _PUBLIC_ void *_talloc_reference_loc(const void *context, const void *ptr, const
 
 static void *_talloc_steal_internal(const void *new_ctx, const void *ptr);
 
+static inline void _talloc_free_poolmem(struct talloc_chunk *tc,
+                                       const char *location)
+{
+       struct talloc_chunk *pool;
+       void *next_tc;
+       unsigned int *pool_object_count;
+
+       pool = (struct talloc_chunk *)tc->pool;
+       next_tc = TC_POOLMEM_NEXT_CHUNK(tc);
+
+       tc->flags |= TALLOC_FLAG_FREE;
+
+       /* we mark the freed memory with where we called the free
+        * from. This means on a double free error we can report where
+        * the first free came from
+        */
+       tc->name = location;
+
+       TC_INVALIDATE_FULL_CHUNK(tc);
+
+       pool_object_count = talloc_pool_objectcount(pool);
+
+       if (unlikely(*pool_object_count == 0)) {
+               talloc_abort("Pool object count zero!");
+               return;
+       }
+
+       *pool_object_count -= 1;
+
+       if (unlikely(*pool_object_count == 1 && !(pool->flags & TALLOC_FLAG_FREE))) {
+               /*
+                * if there is just one object left in the pool
+                * and pool->flags does not have TALLOC_FLAG_FREE,
+                * it means this is the pool itself and
+                * the rest is available for new objects
+                * again.
+                */
+               pool->pool = TC_POOL_FIRST_CHUNK(pool);
+               TC_INVALIDATE_POOL(pool);
+       } else if (unlikely(*pool_object_count == 0)) {
+               /*
+                * we mark the freed memory with where we called the free
+                * from. This means on a double free error we can report where
+                * the first free came from
+                */
+               pool->name = location;
+
+               TC_INVALIDATE_FULL_CHUNK(pool);
+               free(pool);
+       } else if (pool->pool == next_tc) {
+               /*
+                * if pool->pool still points to end of
+                * 'tc' (which is stored in the 'next_tc' variable),
+                * we can reclaim the memory of 'tc'.
+                */
+               pool->pool = tc;
+       }
+}
+
+static inline void _talloc_free_children_internal(struct talloc_chunk *tc,
+                                                 void *ptr,
+                                                 const char *location);
+
 /* 
    internal talloc_free call
 */
@@ -568,12 +783,22 @@ static inline int _talloc_free_internal(void *ptr, const char *location)
                return -1;
        }
 
+       /* possibly initialised the talloc fill value */
+       if (unlikely(!talloc_fill.initialised)) {
+               const char *fill = getenv(TALLOC_FILL_ENV);
+               if (fill != NULL) {
+                       talloc_fill.enabled = true;
+                       talloc_fill.fill_value = strtoul(fill, NULL, 0);
+               }
+               talloc_fill.initialised = true;
+       }
+
        tc = talloc_chunk_from_ptr(ptr);
 
        if (unlikely(tc->refs)) {
                int is_child;
-               /* check this is a reference from a child or grantchild
-                * back to it's parent or grantparent
+               /* check if this is a reference from a child or
+                * grandchild back to it's parent or grandparent
                 *
                 * in that case we need to remove the reference and
                 * call another instance of talloc_free() on the current
@@ -613,30 +838,12 @@ static inline int _talloc_free_internal(void *ptr, const char *location)
        } else {
                if (tc->prev) tc->prev->next = tc->next;
                if (tc->next) tc->next->prev = tc->prev;
+               tc->prev = tc->next = NULL;
        }
 
        tc->flags |= TALLOC_FLAG_LOOP;
 
-       while (tc->child) {
-               /* we need to work out who will own an abandoned child
-                  if it cannot be freed. In priority order, the first
-                  choice is owner of any remaining reference to this
-                  pointer, the second choice is our parent, and the
-                  final choice is the null context. */
-               void *child = TC_PTR_FROM_CHUNK(tc->child);
-               const void *new_parent = null_context;
-               if (unlikely(tc->child->refs)) {
-                       struct talloc_chunk *p = talloc_parent_chunk(tc->child->refs);
-                       if (p) new_parent = TC_PTR_FROM_CHUNK(p);
-               }
-               if (unlikely(_talloc_free_internal(child, location) == -1)) {
-                       if (new_parent == null_context) {
-                               struct talloc_chunk *p = talloc_parent_chunk(ptr);
-                               if (p) new_parent = TC_PTR_FROM_CHUNK(p);
-                       }
-                       _talloc_steal_internal(new_parent, child);
-               }
-       }
+       _talloc_free_children_internal(tc, ptr, location);
 
        tc->flags |= TALLOC_FLAG_FREE;
 
@@ -646,27 +853,26 @@ static inline int _talloc_free_internal(void *ptr, const char *location)
         */
        tc->name = location;
 
-       if (tc->flags & (TALLOC_FLAG_POOL|TALLOC_FLAG_POOLMEM)) {
-               struct talloc_chunk *pool;
+       if (tc->flags & TALLOC_FLAG_POOL) {
                unsigned int *pool_object_count;
 
-               pool = (tc->flags & TALLOC_FLAG_POOL)
-                       ? tc : (struct talloc_chunk *)tc->pool;
+               pool_object_count = talloc_pool_objectcount(tc);
 
-               pool_object_count = talloc_pool_objectcount(pool);
-
-               if (*pool_object_count == 0) {
+               if (unlikely(*pool_object_count == 0)) {
                        talloc_abort("Pool object count zero!");
                        return 0;
                }
 
                *pool_object_count -= 1;
 
-               if (*pool_object_count == 0) {
-                       free(pool);
+               if (unlikely(*pool_object_count == 0)) {
+                       TC_INVALIDATE_FULL_CHUNK(tc);
+                       free(tc);
                }
-       }
-       else {
+       } else if (tc->flags & TALLOC_FLAG_POOLMEM) {
+               _talloc_free_poolmem(tc, location);
+       } else {
+               TC_INVALIDATE_FULL_CHUNK(tc);
                free(tc);
        }
        return 0;
@@ -720,6 +926,7 @@ static void *_talloc_steal_internal(const void *new_ctx, const void *ptr)
        } else {
                if (tc->prev) tc->prev->next = tc->next;
                if (tc->next) tc->next->prev = tc->prev;
+               tc->prev = tc->next = NULL;
        }
 
        tc->parent = new_tc;
@@ -832,11 +1039,11 @@ static inline int talloc_unreference(const void *context, const void *ptr)
 
 /*
   remove a specific parent context from a pointer. This is a more
-  controlled varient of talloc_free()
+  controlled variant of talloc_free()
 */
 _PUBLIC_ int talloc_unlink(const void *context, void *ptr)
 {
-       struct talloc_chunk *tc_p, *new_p;
+       struct talloc_chunk *tc_p, *new_p, *tc_c;
        void *new_parent;
 
        if (ptr == NULL) {
@@ -851,14 +1058,13 @@ _PUBLIC_ int talloc_unlink(const void *context, void *ptr)
                return 0;
        }
 
-       if (context == NULL) {
-               if (talloc_parent_chunk(ptr) != NULL) {
-                       return -1;
-               }
+       if (context != NULL) {
+               tc_c = talloc_chunk_from_ptr(context);
        } else {
-               if (talloc_chunk_from_ptr(context) != talloc_parent_chunk(ptr)) {
-                       return -1;
-               }
+               tc_c = NULL;
+       }
+       if (tc_c != talloc_parent_chunk(ptr)) {
+               return -1;
        }
        
        tc_p = talloc_chunk_from_ptr(ptr);
@@ -969,7 +1175,7 @@ _PUBLIC_ void *talloc_check_name(const void *ptr, const char *name)
        return NULL;
 }
 
-static void talloc_abort_type_missmatch(const char *location,
+static void talloc_abort_type_mismatch(const char *location,
                                        const char *name,
                                        const char *expected)
 {
@@ -992,7 +1198,7 @@ _PUBLIC_ void *_talloc_get_type_abort(const void *ptr, const char *name, const c
        const char *pname;
 
        if (unlikely(ptr == NULL)) {
-               talloc_abort_type_missmatch(location, NULL, name);
+               talloc_abort_type_mismatch(location, NULL, name);
                return NULL;
        }
 
@@ -1001,7 +1207,7 @@ _PUBLIC_ void *_talloc_get_type_abort(const void *ptr, const char *name, const c
                return discard_const_p(void, ptr);
        }
 
-       talloc_abort_type_missmatch(location, pname, name);
+       talloc_abort_type_mismatch(location, pname, name);
        return NULL;
 }
 
@@ -1014,13 +1220,6 @@ _PUBLIC_ void *talloc_init(const char *fmt, ...)
        void *ptr;
        const char *name;
 
-       /*
-        * samba3 expects talloc_report_depth_cb(NULL, ...)
-        * reports all talloc'ed memory, so we need to enable
-        * null_tracking
-        */
-       talloc_enable_null_tracking();
-
        ptr = __talloc(NULL, 0);
        if (unlikely(ptr == NULL)) return NULL;
 
@@ -1036,21 +1235,10 @@ _PUBLIC_ void *talloc_init(const char *fmt, ...)
        return ptr;
 }
 
-/*
-  this is a replacement for the Samba3 talloc_destroy_pool functionality. It
-  should probably not be used in new code. It's in here to keep the talloc
-  code consistent across Samba 3 and 4.
-*/
-_PUBLIC_ void talloc_free_children(void *ptr)
+static inline void _talloc_free_children_internal(struct talloc_chunk *tc,
+                                                 void *ptr,
+                                                 const char *location)
 {
-       struct talloc_chunk *tc;
-
-       if (unlikely(ptr == NULL)) {
-               return;
-       }
-
-       tc = talloc_chunk_from_ptr(ptr);
-
        while (tc->child) {
                /* we need to work out who will own an abandoned child
                   if it cannot be freed. In priority order, the first
@@ -1063,7 +1251,7 @@ _PUBLIC_ void talloc_free_children(void *ptr)
                        struct talloc_chunk *p = talloc_parent_chunk(tc->child->refs);
                        if (p) new_parent = TC_PTR_FROM_CHUNK(p);
                }
-               if (unlikely(talloc_free(child) == -1)) {
+               if (unlikely(_talloc_free_internal(child, location) == -1)) {
                        if (new_parent == null_context) {
                                struct talloc_chunk *p = talloc_parent_chunk(ptr);
                                if (p) new_parent = TC_PTR_FROM_CHUNK(p);
@@ -1071,14 +1259,46 @@ _PUBLIC_ void talloc_free_children(void *ptr)
                        _talloc_steal_internal(new_parent, child);
                }
        }
+}
 
-       if ((tc->flags & TALLOC_FLAG_POOL)
-           && (*talloc_pool_objectcount(tc) == 1)) {
-               tc->pool = ((char *)tc + TC_HDR_SIZE + TALLOC_POOL_HDR_SIZE);
-#if defined(DEVELOPER) && defined(VALGRIND_MAKE_MEM_NOACCESS)
-               VALGRIND_MAKE_MEM_NOACCESS(
-                       tc->pool, tc->size - TALLOC_POOL_HDR_SIZE);
-#endif
+/*
+  this is a replacement for the Samba3 talloc_destroy_pool functionality. It
+  should probably not be used in new code. It's in here to keep the talloc
+  code consistent across Samba 3 and 4.
+*/
+_PUBLIC_ void talloc_free_children(void *ptr)
+{
+       struct talloc_chunk *tc_name = NULL;
+       struct talloc_chunk *tc;
+
+       if (unlikely(ptr == NULL)) {
+               return;
+       }
+
+       tc = talloc_chunk_from_ptr(ptr);
+
+       /* we do not want to free the context name if it is a child .. */
+       if (likely(tc->child)) {
+               for (tc_name = tc->child; tc_name; tc_name = tc_name->next) {
+                       if (tc->name == TC_PTR_FROM_CHUNK(tc_name)) break;
+               }
+               if (tc_name) {
+                       _TLIST_REMOVE(tc->child, tc_name);
+                       if (tc->child) {
+                               tc->child->parent = tc;
+                       }
+               }
+       }
+
+       _talloc_free_children_internal(tc, ptr, __location__);
+
+       /* .. so we put it back after all other children have been freed */
+       if (tc_name) {
+               if (tc->child) {
+                       tc->child->parent = NULL;
+               }
+               tc_name->parent = tc;
+               _TLIST_ADD(tc->child, tc_name);
        }
 }
 
@@ -1160,6 +1380,7 @@ _PUBLIC_ void *_talloc_realloc(const void *context, void *ptr, size_t size, cons
        struct talloc_chunk *tc;
        void *new_ptr;
        bool malloced = false;
+       struct talloc_chunk *pool_tc = NULL;
 
        /* size zero is equivalent to free() */
        if (unlikely(size == 0)) {
@@ -1188,27 +1409,154 @@ _PUBLIC_ void *_talloc_realloc(const void *context, void *ptr, size_t size, cons
                return NULL;
        }
 
+       /* don't let anybody try to realloc a talloc_pool */
+       if (unlikely(tc->flags & TALLOC_FLAG_POOLMEM)) {
+               pool_tc = (struct talloc_chunk *)tc->pool;
+       }
+
+#if (ALWAYS_REALLOC == 0)
        /* don't shrink if we have less than 1k to gain */
-       if ((size < tc->size) && ((tc->size - size) < 1024)) {
-               tc->size = size;
+       if (size < tc->size) {
+               if (pool_tc) {
+                       void *next_tc = TC_POOLMEM_NEXT_CHUNK(tc);
+                       TC_INVALIDATE_SHRINK_CHUNK(tc, size);
+                       tc->size = size;
+                       if (next_tc == pool_tc->pool) {
+                               pool_tc->pool = TC_POOLMEM_NEXT_CHUNK(tc);
+                       }
+                       return ptr;
+               } else if ((tc->size - size) < 1024) {
+                       /*
+                        * if we call TC_INVALIDATE_SHRINK_CHUNK() here
+                        * we would need to call TC_UNDEFINE_GROW_CHUNK()
+                        * after each realloc call, which slows down
+                        * testing a lot :-(.
+                        *
+                        * That is why we only mark memory as undefined here.
+                        */
+                       TC_UNDEFINE_SHRINK_CHUNK(tc, size);
+
+                       /* do not shrink if we have less than 1k to gain */
+                       tc->size = size;
+                       return ptr;
+               }
+       } else if (tc->size == size) {
+               /*
+                * do not change the pointer if it is exactly
+                * the same size.
+                */
                return ptr;
        }
+#endif
 
        /* by resetting magic we catch users of the old memory */
        tc->flags |= TALLOC_FLAG_FREE;
 
 #if ALWAYS_REALLOC
-       new_ptr = malloc(size + TC_HDR_SIZE);
-       if (new_ptr) {
-               memcpy(new_ptr, tc, MIN(tc->size, size) + TC_HDR_SIZE);
-               free(tc);
+       if (pool_tc) {
+               new_ptr = talloc_alloc_pool(tc, size + TC_HDR_SIZE);
+               *talloc_pool_objectcount(pool_tc) -= 1;
+
+               if (new_ptr == NULL) {
+                       new_ptr = malloc(TC_HDR_SIZE+size);
+                       malloced = true;
+               }
+
+               if (new_ptr) {
+                       memcpy(new_ptr, tc, MIN(tc->size,size) + TC_HDR_SIZE);
+                       TC_INVALIDATE_FULL_CHUNK(tc);
+               }
+       } else {
+               new_ptr = malloc(size + TC_HDR_SIZE);
+               if (new_ptr) {
+                       memcpy(new_ptr, tc, MIN(tc->size, size) + TC_HDR_SIZE);
+                       free(tc);
+               }
        }
 #else
-       if (tc->flags & TALLOC_FLAG_POOLMEM) {
+       if (pool_tc) {
+               void *next_tc = TC_POOLMEM_NEXT_CHUNK(tc);
+               size_t old_chunk_size = TC_POOLMEM_CHUNK_SIZE(tc);
+               size_t new_chunk_size = TC_ALIGN16(TC_HDR_SIZE + size);
+               size_t space_needed;
+               size_t space_left;
+               unsigned int chunk_count = *talloc_pool_objectcount(pool_tc);
+
+               if (!(pool_tc->flags & TALLOC_FLAG_FREE)) {
+                       chunk_count -= 1;
+               }
+
+               if (chunk_count == 1) {
+                       /*
+                        * optimize for the case where 'tc' is the only
+                        * chunk in the pool.
+                        */
+                       space_needed = new_chunk_size;
+                       space_left = pool_tc->size - TALLOC_POOL_HDR_SIZE;
+
+                       if (space_left >= space_needed) {
+                               size_t old_used = TC_HDR_SIZE + tc->size;
+                               size_t new_used = TC_HDR_SIZE + size;
+                               pool_tc->pool = TC_POOL_FIRST_CHUNK(pool_tc);
+#if defined(DEVELOPER) && defined(VALGRIND_MAKE_MEM_UNDEFINED)
+                               /*
+                                * we need to prepare the memmove into
+                                * the unaccessable area.
+                                */
+                               {
+                                       size_t diff = PTR_DIFF(tc, pool_tc->pool);
+                                       size_t flen = MIN(diff, old_used);
+                                       char *fptr = (char *)pool_tc->pool;
+                                       VALGRIND_MAKE_MEM_UNDEFINED(fptr, flen);
+                               }
+#endif
+                               memmove(pool_tc->pool, tc, old_used);
+                               new_ptr = pool_tc->pool;
+
+                               tc = (struct talloc_chunk *)new_ptr;
+                               TC_UNDEFINE_GROW_CHUNK(tc, size);
+
+                               /*
+                                * first we do not align the pool pointer
+                                * because we want to invalidate the padding
+                                * too.
+                                */
+                               pool_tc->pool = new_used + (char *)new_ptr;
+                               TC_INVALIDATE_POOL(pool_tc);
+
+                               /* now the aligned pointer */
+                               pool_tc->pool = new_chunk_size + (char *)new_ptr;
+                               goto got_new_ptr;
+                       }
+
+                       next_tc = NULL;
+               }
+
+               if (new_chunk_size == old_chunk_size) {
+                       TC_UNDEFINE_GROW_CHUNK(tc, size);
+                       tc->flags &= ~TALLOC_FLAG_FREE;
+                       tc->size = size;
+                       return ptr;
+               }
+
+               if (next_tc == pool_tc->pool) {
+                       /*
+                        * optimize for the case where 'tc' is the last
+                        * chunk in the pool.
+                        */
+                       space_needed = new_chunk_size - old_chunk_size;
+                       space_left = TC_POOL_SPACE_LEFT(pool_tc);
+
+                       if (space_left >= space_needed) {
+                               TC_UNDEFINE_GROW_CHUNK(tc, size);
+                               tc->flags &= ~TALLOC_FLAG_FREE;
+                               tc->size = size;
+                               pool_tc->pool = TC_POOLMEM_NEXT_CHUNK(tc);
+                               return ptr;
+                       }
+               }
 
                new_ptr = talloc_alloc_pool(tc, size + TC_HDR_SIZE);
-               *talloc_pool_objectcount((struct talloc_chunk *)
-                                        (tc->pool)) -= 1;
 
                if (new_ptr == NULL) {
                        new_ptr = malloc(TC_HDR_SIZE+size);
@@ -1217,11 +1565,14 @@ _PUBLIC_ void *_talloc_realloc(const void *context, void *ptr, size_t size, cons
 
                if (new_ptr) {
                        memcpy(new_ptr, tc, MIN(tc->size,size) + TC_HDR_SIZE);
+
+                       _talloc_free_poolmem(tc, __location__ "_talloc_realloc");
                }
        }
        else {
                new_ptr = realloc(tc, size + TC_HDR_SIZE);
        }
+got_new_ptr:
 #endif
        if (unlikely(!new_ptr)) {       
                tc->flags &= ~TALLOC_FLAG_FREE; 
index 187d7e781672c5916918b710ecdb0377a15675b0..96c7e2467c27b27dd23107f799603fa855434351 100644 (file)
 #include <stdio.h>
 #include <stdarg.h>
 
+#ifdef __cplusplus
+extern "C" {
+#endif
+
 /**
  * @defgroup talloc The talloc API
  *
@@ -169,18 +173,11 @@ void *talloc_init(const char *fmt, ...) PRINTF_ATTRIBUTE(1,2);
  * destructors. Likewise, if "ptr" is NULL, then the function will make
  * no modifications and return -1.
  *
- * If this pointer has an additional parent when talloc_free() is called
- * then the memory is not actually released, but instead the most
- * recently established parent is destroyed. See talloc_reference() for
- * details on establishing additional parents.
- *
- * For more control on which parent is removed, see talloc_unlink()
- *
- * talloc_free() operates recursively on its children.
- *
- * From the 2.0 version of talloc, as a special case, talloc_free() is
- * refused on pointers that have more than one parent, as talloc would
- * have no way of knowing which parent should be removed. To free a
+ * From version 2.0 and onwards, as a special case, talloc_free() is
+ * refused on pointers that have more than one parent associated, as talloc
+ * would have no way of knowing which parent should be removed. This is
+ * different from older versions in the sense that always the reference to
+ * the most recently established parent has been destroyed. Hence to free a
  * pointer that has more than one parent please use talloc_unlink().
  *
  * To help you find problems in your code caused by this behaviour, if
@@ -197,6 +194,8 @@ void *talloc_init(const char *fmt, ...) PRINTF_ATTRIBUTE(1,2);
  * talloc_set_log_stderr() for more information on talloc logging
  * functions.
  *
+ * talloc_free() operates recursively on its children.
+ *
  * @param[in]  ptr      The chunk to be freed.
  *
  * @return              Returns 0 on success and -1 on error. A possible
@@ -229,7 +228,10 @@ int _talloc_free(void *ptr, const char *location);
  * The function walks along the list of all children of a talloc context and
  * talloc_free()s only the children, not the context itself.
  *
- * @param[in]  ptr      The chunk that you want to free the children of.
+ * A NULL argument is handled as no-op.
+ *
+ * @param[in]  ptr      The chunk that you want to free the children of
+ *                      (NULL is allowed too)
  */
 void talloc_free_children(void *ptr);
 
@@ -398,14 +400,14 @@ const char *talloc_set_name(const void *ptr, const char *fmt, ...) PRINTF_ATTRIB
  *
  * @param[in]  new_ctx  The new parent context.
  *
- * @param[in]  ptr      Pointer to the talloc chunk to move.
+ * @param[in]  pptr     Pointer to the talloc chunk to move.
  *
  * @return              The pointer of the talloc chunk it has been moved to,
  *                      NULL on error.
  */
-void *talloc_move(const void *new_ctx, const void *ptr);
+void *talloc_move(const void *new_ctx, void **pptr);
 #else
-#define talloc_move(ctx, ptr) (_TALLOC_TYPEOF(*(ptr)))_talloc_move((ctx),(void *)(ptr))
+#define talloc_move(ctx, pptr) (_TALLOC_TYPEOF(*(pptr)))_talloc_move((ctx),(void *)(pptr))
 void *_talloc_move(const void *new_ctx, const void *pptr);
 #endif
 
@@ -698,9 +700,9 @@ void *_talloc_memdup(const void *t, const void *p, size_t size, const char *name
 /**
  * @brief Assign a type to a talloc chunk.
  *
- * This macro allows you to force the name of a pointer to be a particular type.
- * This can be used in conjunction with talloc_get_type() to do type checking on
- * void* pointers.
+ * This macro allows you to force the name of a pointer to be of a particular
+ * type. This can be used in conjunction with talloc_get_type() to do type
+ * checking on void* pointers.
  *
  * It is equivalent to this:
  *
@@ -901,9 +903,9 @@ size_t talloc_reference_count(const void *ptr);
  *   will reduce the number of parents of this pointer by 1, and will
  *   cause this pointer to be freed if it runs out of parents.
  *
- * - you can talloc_free() the pointer itself. That will destroy the
- *   most recently established parent to the pointer and leave the
- *   pointer as a child of its current parent.
+ * - you can talloc_free() the pointer itself if it has at maximum one
+ *   parent. This behaviour has been changed since the release of version
+ *   2.0. Further informations in the description of "talloc_free".
  *
  * For more control on which parent to remove, see talloc_unlink()
  * @param[in]  ctx      The additional parent.
@@ -938,9 +940,10 @@ void *_talloc_reference_loc(const void *context, const void *ptr, const char *lo
  * either be a context used in talloc_reference() with this pointer, or must be
  * a direct parent of ptr.
  *
- * 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.
+ * You can just use talloc_free() instead of talloc_unlink() if there
+ * is at maximum one parent. This behaviour has been changed since the
+ * release of version 2.0. Further informations in the description of
+ * "talloc_free".
  *
  * @param[in]  context  The talloc parent to remove.
  *
@@ -972,6 +975,15 @@ int talloc_unlink(const void *context, void *ptr);
  * which will be automatically freed on program exit. This can be used
  * to reduce the noise in memory leak reports.
  *
+ * Never use this in code that might be used in objects loaded with
+ * dlopen and unloaded with dlclose. talloc_autofree_context()
+ * internally uses atexit(3). Some platforms like modern Linux handles
+ * this fine, but for example FreeBSD does not deal well with dlopen()
+ * and atexit() used simultaneously: dlclose() does not clean up the
+ * list of atexit-handlers, so when the program exits the code that
+ * was registered from within talloc_autofree_context() is gone, the
+ * program crashes at exit.
+ *
  * @return              A talloc context, NULL on error.
  */
 void *talloc_autofree_context(void);
@@ -979,7 +991,7 @@ void *talloc_autofree_context(void);
 /**
  * @brief Get the size of a talloc chunk.
  *
- * This function lets you know the amount of memory alloced so far by
+ * This function lets you know the amount of memory allocated so far by
  * this context. It does NOT account for subcontext memory.
  * This can be used to calculate the size of an array.
  *
@@ -1072,7 +1084,7 @@ void *talloc_reparent(const void *old_parent, const void *new_parent, const void
  * @endcode
  *
  * @see talloc()
- * @see talloc_array_zero()
+ * @see talloc_zero_array()
  */
 void *talloc_array(const void *ctx, #type, unsigned count);
 #else
@@ -1440,7 +1452,7 @@ char *talloc_asprintf(const void *t, const char *fmt, ...) PRINTF_ATTRIBUTE(2,3)
  * @brief Append a formatted string to another string.
  *
  * This function appends the given formatted string to the given string. Use
- * this varient when the string in the current talloc buffer may have been
+ * this variant when the string in the current talloc buffer may have been
  * truncated in length.
  *
  * This functions sets the name of the new pointer to the new
@@ -1538,7 +1550,7 @@ void talloc_report_depth_file(const void *ptr, int depth, int max_depth, FILE *f
  * @brief Print a summary report of all memory used by ptr.
  *
  * This provides a more detailed report than talloc_report(). It will
- * recursively print the ensire tree of memory referenced by the
+ * recursively print the entire tree of memory referenced by the
  * pointer. References in the tree are shown by giving the name of the
  * pointer that is referenced.
  *
@@ -1692,4 +1704,8 @@ void talloc_set_log_stderr(void);
 #define TALLOC_MAX_DEPTH 10000
 #endif
 
+#ifdef __cplusplus
+} /* end of extern "C" */
+#endif
+
 #endif
index 7293222b6ddccc10b32e6a5182dc2890157fe172..8164ffc2d91c15af6af88d06947be117cd6a71f5 100644 (file)
@@ -26,7 +26,7 @@ you can do this::
   X->name = talloc_strdup(X, "foo");
 
 and the pointer X->name would be a "child" of the talloc context "X"
-which is itself a child of mem_ctx. So if you do talloc_free(mem_ctx)
+which is itself a child of "mem_ctx". So if you do talloc_free(mem_ctx)
 then it is all destroyed, whereas if you do talloc_free(X) then just X
 and X->name are destroyed, and if you do talloc_free(X->name) then
 just the name element of X is destroyed.
@@ -64,7 +64,7 @@ Multi-threading
 talloc itself does not deal with threads. It is thread-safe (assuming  
 the underlying "malloc" is), as long as each thread uses different  
 memory contexts.
-If two threads uses the same context then they need to synchronize in  
+If two threads use the same context then they need to synchronize in
 order to be safe. In particular:
 - when using talloc_enable_leak_report(), giving directly NULL as a  
 parent context implicitly refers to a hidden "null context" global  
@@ -74,6 +74,19 @@ without proper synchronization ;
 shouldn't be used by several threads simultaneously without  
 synchronization.
 
+talloc and shared objects
+-------------------------
+
+talloc can be used in shared objects. Special care needs to be taken
+to never use talloc_autofree_context() in code that might be loaded
+with dlopen() and unloaded with dlclose(), as talloc_autofree_context()
+internally uses atexit(3). Some platforms like modern Linux handles
+this fine, but for example FreeBSD does not deal well with dlopen()
+and atexit() used simultaneously: dlclose() does not clean up the list
+of atexit-handlers, so when the program exits the code that was
+registered from within talloc_autofree_context() is gone, the program
+crashes at exit.
+
 
 =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
 (type *)talloc(const void *context, type);
@@ -123,18 +136,11 @@ returned -1. See talloc_set_destructor() for details on
 destructors. Likewise, if "ptr" is NULL, then the function will make
 no modifications and returns -1.
 
-If this pointer has an additional parent when talloc_free() is called
-then the memory is not actually released, but instead the most
-recently established parent is destroyed. See talloc_reference() for
-details on establishing additional parents.
-
-For more control on which parent is removed, see talloc_unlink()
-
-talloc_free() operates recursively on its children.
-
-From the 2.0 version of talloc, as a special case, talloc_free() is
-refused on pointers that have more than one parent, as talloc would
-have no way of knowing which parent should be removed. To free a
+From version 2.0 and onwards, as a special case, talloc_free() is
+refused on pointers that have more than one parent associated, as talloc
+would have no way of knowing which parent should be removed. This is
+different from older versions in the sense that always the reference to
+the most recently established parent has been destroyed. Hence to free a
 pointer that has more than one parent please use talloc_unlink().
 
 To help you find problems in your code caused by this behaviour, if
@@ -149,13 +155,16 @@ Please see the documentation for talloc_set_log_fn() and
 talloc_set_log_stderr() for more information on talloc logging
 functions.
 
+talloc_free() operates recursively on its children.
+
 =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
-int talloc_free_children(void *ptr);
+void talloc_free_children(void *ptr);
 
 The talloc_free_children() walks along the list of all children of a
 talloc context and talloc_free()s only the children, not the context
 itself.
 
+A NULL argument is handled as no-op.
 
 =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
 void *talloc_reference(const void *context, const void *ptr);
@@ -177,9 +186,9 @@ ways:
     will reduce the number of parents of this pointer by 1, and will
     cause this pointer to be freed if it runs out of parents.
 
-  - you can talloc_free() the pointer itself. That will destroy the
-    most recently established parent to the pointer and leave the
-    pointer as a child of its current parent.
+  - you can talloc_free() the pointer itself if it has at maximum one
+    parent. This behaviour has been changed since the release of version
+    2.0. Further informations in the description of "talloc_free".
 
 For more control on which parent to remove, see talloc_unlink()
 
@@ -195,10 +204,10 @@ Note that if the parent has already been removed using talloc_free()
 then this function will fail and will return -1.  Likewise, if "ptr"
 is NULL, then the function will make no modifications and return -1.
 
-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.
-
+You can just use talloc_free() instead of talloc_unlink() if there
+is at maximum one parent. This behaviour has been changed since the
+release of version 2.0. Further informations in the description of
+"talloc_free".
 
 =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
 void talloc_set_destructor(const void *ptr, int (*destructor)(void *));
@@ -470,7 +479,7 @@ been called.
 void talloc_report_full(const void *ptr, FILE *f);
 
 This provides a more detailed report than talloc_report(). It will
-recursively print the ensire tree of memory referenced by the
+recursively print the entire tree of memory referenced by the
 pointer. References in the tree are shown by giving the name of the
 pointer that is referenced.
 
@@ -629,7 +638,7 @@ char *talloc_asprintf_append(char *s, const char *fmt, ...);
 
 The talloc_asprintf_append() function appends the given formatted
 string to the given string.
-Use this varient when the string in the current talloc buffer may
+Use this variant when the string in the current talloc buffer may
 have been truncated in length.
 
 This functions sets the name of the new pointer to the new
@@ -643,7 +652,7 @@ char *talloc_asprintf_append_buffer(char *s, const char *fmt, ...);
 
 The talloc_asprintf_append() function appends the given formatted 
 string to the end of the currently allocated talloc buffer.
-Use this varient when the string in the current talloc buffer has
+Use this variant when the string in the current talloc buffer has
 not been changed.
 
 This functions sets the name of the new pointer to the new
@@ -717,7 +726,7 @@ this::
 =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
 talloc_set_type(const void *ptr, type);
 
-This macro allows you to force the name of a pointer to be a
+This macro allows you to force the name of a pointer to be of a
 particular type. This can be used in conjunction with
 talloc_get_type() to do type checking on void* pointers.
 
@@ -728,7 +737,7 @@ It is equivalent to this::
 =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
 talloc_get_size(const void *ctx);
 
-This function lets you know the amount of memory alloced so far by
+This function lets you know the amount of memory allocated so far by
 this context. It does NOT account for subcontext memory.
 This can be used to calculate the size of an array.
 
@@ -755,4 +764,4 @@ errors.
 =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
 void talloc_set_log_stderr(void)
 
-This sets the talloc log function to write log messages to stderr
+This sets the talloc log function to write log messages to stderr.
diff --git a/ctdb/lib/talloc/talloc_testsuite.h b/ctdb/lib/talloc/talloc_testsuite.h
new file mode 100644 (file)
index 0000000..acb9701
--- /dev/null
@@ -0,0 +1,7 @@
+#ifndef __LIB_TALLOC_TALLOC_TESTSUITE_H__
+#define __LIB_TALLOC_TALLOC_TESTSUITE_H__
+
+struct torture_context;
+bool torture_local_talloc(struct torture_context *tctx);
+
+#endif
index 5e9b3ec8f23d3c5b6f448b2dd8ed2e7590f8171b..71917038be9f01ee466987052b44ac63fca1f192 100644 (file)
@@ -27,6 +27,8 @@
 #include "system/time.h"
 #include <talloc.h>
 
+#include "talloc_testsuite.h"
+
 static struct timeval timeval_current(void)
 {
        struct timeval tv;
@@ -55,15 +57,6 @@ static double timeval_elapsed(struct timeval *tv)
                return false; \
        }
 
-#if _SAMBA_BUILD_==3
-#ifdef malloc
-#undef malloc
-#endif
-#ifdef strdup
-#undef strdup
-#endif
-#endif
-
 #define CHECK_SIZE(test, ptr, tsize) do { \
        if (talloc_total_size(ptr) != (tsize)) { \
                printf("failed: %s [\n%s: wrong '%s' tree size: got %u  expected %u\n]\n", \
@@ -101,6 +94,7 @@ static double timeval_elapsed(struct timeval *tv)
 
 static unsigned int test_abort_count;
 
+#if 0
 static void test_abort_fn(const char *reason)
 {
        printf("# test_abort_fn(%s)\n", reason);
@@ -112,6 +106,7 @@ static void test_abort_start(void)
        test_abort_count = 0;
        talloc_set_abort_fn(test_abort_fn);
 }
+#endif
 
 static void test_abort_stop(void)
 {
@@ -1119,19 +1114,158 @@ static bool test_pool(void)
 {
        void *pool;
        void *p1, *p2, *p3, *p4;
+       void *p2_2;
 
        pool = talloc_pool(NULL, 1024);
 
        p1 = talloc_size(pool, 80);
+       memset(p1, 0x11, talloc_get_size(p1));
        p2 = talloc_size(pool, 20);
+       memset(p2, 0x11, talloc_get_size(p2));
        p3 = talloc_size(p1, 50);
+       memset(p3, 0x11, talloc_get_size(p3));
        p4 = talloc_size(p3, 1000);
+       memset(p4, 0x11, talloc_get_size(p4));
+
+#if 1 /* this relies on ALWAYS_REALLOC == 0 in talloc.c */
+       p2_2 = talloc_realloc_size(pool, p2, 20+1);
+       torture_assert("pool realloc 20+1", p2_2 == p2, "failed: pointer changed");
+       memset(p2, 0x11, talloc_get_size(p2));
+       p2_2 = talloc_realloc_size(pool, p2, 20-1);
+       torture_assert("pool realloc 20-1", p2_2 == p2, "failed: pointer changed");
+       memset(p2, 0x11, talloc_get_size(p2));
+       p2_2 = talloc_realloc_size(pool, p2, 20-1);
+       torture_assert("pool realloc 20-1", p2_2 == p2, "failed: pointer changed");
+       memset(p2, 0x11, talloc_get_size(p2));
+
+       talloc_free(p3);
+
+       /* this should reclaim the memory of p4 and p3 */
+       p2_2 = talloc_realloc_size(pool, p2, 400);
+       torture_assert("pool realloc 400", p2_2 == p2, "failed: pointer changed");
+       memset(p2, 0x11, talloc_get_size(p2));
+
+       talloc_free(p1);
+
+       /* this should reclaim the memory of p1 */
+       p2_2 = talloc_realloc_size(pool, p2, 800);
+       torture_assert("pool realloc 800", p2_2 == p1, "failed: pointer not changed");
+       p2 = p2_2;
+       memset(p2, 0x11, talloc_get_size(p2));
+
+       /* this should do a malloc */
+       p2_2 = talloc_realloc_size(pool, p2, 1800);
+       torture_assert("pool realloc 1800", p2_2 != p2, "failed: pointer not changed");
+       p2 = p2_2;
+       memset(p2, 0x11, talloc_get_size(p2));
+
+       /* this should reclaim the memory from the pool */
+       p3 = talloc_size(pool, 80);
+       torture_assert("pool alloc 80", p3 == p1, "failed: pointer changed");
+       memset(p3, 0x11, talloc_get_size(p3));
+
+       talloc_free(p2);
+       talloc_free(p3);
+
+       p1 = talloc_size(pool, 80);
+       memset(p1, 0x11, talloc_get_size(p1));
+       p2 = talloc_size(pool, 20);
+       memset(p2, 0x11, talloc_get_size(p2));
+
+       talloc_free(p1);
+
+       p2_2 = talloc_realloc_size(pool, p2, 20-1);
+       torture_assert("pool realloc 20-1", p2_2 == p2, "failed: pointer changed");
+       memset(p2, 0x11, talloc_get_size(p2));
+       p2_2 = talloc_realloc_size(pool, p2, 20-1);
+       torture_assert("pool realloc 20-1", p2_2 == p2, "failed: pointer changed");
+       memset(p2, 0x11, talloc_get_size(p2));
+
+       /* this should do a malloc */
+       p2_2 = talloc_realloc_size(pool, p2, 1800);
+       torture_assert("pool realloc 1800", p2_2 != p2, "failed: pointer not changed");
+       p2 = p2_2;
+       memset(p2, 0x11, talloc_get_size(p2));
+
+       /* this should reclaim the memory from the pool */
+       p3 = talloc_size(pool, 800);
+       torture_assert("pool alloc 800", p3 == p1, "failed: pointer changed");
+       memset(p3, 0x11, talloc_get_size(p3));
+
+#endif /* this relies on ALWAYS_REALLOC == 0 in talloc.c */
 
        talloc_free(pool);
 
        return true;
 }
 
+static bool test_pool_steal(void)
+{
+       void *root;
+       void *pool;
+       void *p1, *p2;
+       void *p1_2, *p2_2;
+       size_t hdr;
+       size_t ofs1, ofs2;
+
+       root = talloc_new(NULL);
+       pool = talloc_pool(root, 1024);
+
+       p1 = talloc_size(pool, 4 * 16);
+       torture_assert("pool allocate 4 * 16", p1 != NULL, "failed ");
+       memset(p1, 0x11, talloc_get_size(p1));
+       p2 = talloc_size(pool, 4 * 16);
+       torture_assert("pool allocate 4 * 16", p2 > p1, "failed: !(p2 > p1) ");
+       memset(p2, 0x11, talloc_get_size(p2));
+
+       ofs1 = PTR_DIFF(p2, p1);
+       hdr = ofs1 - talloc_get_size(p1);
+
+       talloc_steal(root, p1);
+       talloc_steal(root, p2);
+
+       talloc_free(pool);
+
+       p1_2 = p1;
+
+#if 1 /* this relies on ALWAYS_REALLOC == 0 in talloc.c */
+       p1_2 = talloc_realloc_size(root, p1, 5 * 16);
+       torture_assert("pool realloc 5 * 16", p1_2 > p2, "failed: pointer not changed");
+       memset(p1_2, 0x11, talloc_get_size(p1_2));
+       ofs1 = PTR_DIFF(p1_2, p2);
+       ofs2 = talloc_get_size(p2) + hdr;
+
+       torture_assert("pool realloc ", ofs1 == ofs2, "failed: pointer offset unexpected");
+
+       p2_2 = talloc_realloc_size(root, p2, 3 * 16);
+       torture_assert("pool realloc 5 * 16", p2_2 == p2, "failed: pointer changed");
+       memset(p2_2, 0x11, talloc_get_size(p2_2));
+#endif /* this relies on ALWAYS_REALLOC == 0 in talloc.c */
+
+       talloc_free(p1_2);
+
+       p2_2 = p2;
+
+#if 1 /* this relies on ALWAYS_REALLOC == 0 in talloc.c */
+       /* now we should reclaim the full pool */
+       p2_2 = talloc_realloc_size(root, p2, 8 * 16);
+       torture_assert("pool realloc 8 * 16", p2_2 == p1, "failed: pointer not expected");
+       p2 = p2_2;
+       memset(p2_2, 0x11, talloc_get_size(p2_2));
+
+       /* now we malloc and free the full pool space */
+       p2_2 = talloc_realloc_size(root, p2, 2 * 1024);
+       torture_assert("pool realloc 2 * 1024", p2_2 != p1, "failed: pointer not expected");
+       memset(p2_2, 0x11, talloc_get_size(p2_2));
+
+#endif /* this relies on ALWAYS_REALLOC == 0 in talloc.c */
+
+       talloc_free(p2_2);
+
+       talloc_free(root);
+
+       return true;
+}
 
 static bool test_free_ref_null_context(void)
 {
@@ -1163,6 +1297,65 @@ static bool test_free_ref_null_context(void)
        return true;
 }
 
+static bool test_rusty(void)
+{
+       void *root;
+       const char *p1;
+
+       talloc_enable_null_tracking();
+       root = talloc_new(NULL);
+       p1 = talloc_strdup(root, "foo");
+       talloc_increase_ref_count(p1);
+       talloc_report_full(root, stdout);
+       talloc_free(root);
+       CHECK_BLOCKS("null_context", NULL, 2);
+       return true;
+}
+
+static bool test_free_children(void)
+{
+       void *root;
+       char *p1, *p2;
+       const char *name, *name2;
+
+       talloc_enable_null_tracking();
+       root = talloc_new(NULL);
+       p1 = talloc_strdup(root, "foo1");
+       p2 = talloc_strdup(p1, "foo2");
+
+       talloc_set_name(p1, "%s", "testname");
+       talloc_free_children(p1);
+       /* check its still a valid talloc ptr */
+       talloc_get_size(talloc_get_name(p1));
+       if (strcmp(talloc_get_name(p1), "testname") != 0) {
+               return false;
+       }
+
+       talloc_set_name(p1, "%s", "testname");
+       name = talloc_get_name(p1);
+       talloc_free_children(p1);
+       /* check its still a valid talloc ptr */
+       talloc_get_size(talloc_get_name(p1));
+       torture_assert("name", name == talloc_get_name(p1), "name ptr changed");
+       torture_assert("namecheck", strcmp(talloc_get_name(p1), "testname") == 0,
+                      "wrong name");
+       CHECK_BLOCKS("name1", p1, 2);
+
+       /* note that this does not free the old child name */
+       talloc_set_name_const(p1, "testname2");
+       name2 = talloc_get_name(p1);
+       /* but this does */
+       talloc_free_children(p1);
+       torture_assert("namecheck", strcmp(talloc_get_name(p1), "testname2") == 0,
+                      "wrong name");
+       CHECK_BLOCKS("name1", p1, 1);
+
+       talloc_report_full(root, stdout);
+       talloc_free(root);
+       return true;
+}
+
+
 static void test_reset(void)
 {
        talloc_set_log_fn(test_log_stdout);
@@ -1171,7 +1364,6 @@ static void test_reset(void)
        talloc_enable_null_tracking_no_autofree();
 }
 
-struct torture_context;
 bool torture_local_talloc(struct torture_context *tctx)
 {
        bool ret = true;
@@ -1217,7 +1409,13 @@ bool torture_local_talloc(struct torture_context *tctx)
        test_reset();
        ret &= test_pool();
        test_reset();
+       ret &= test_pool_steal();
+       test_reset();
        ret &= test_free_ref_null_context();
+       test_reset();
+       ret &= test_rusty();
+       test_reset();
+       ret &= test_free_children();
 
        if (ret) {
                test_reset();
@@ -1227,6 +1425,6 @@ bool torture_local_talloc(struct torture_context *tctx)
        ret &= test_autofree();
 
        test_reset();
-
+       talloc_disable_null_tracking();
        return ret;
 }
index 1b51333278783d8f2caa2c20ed623478c243e036..50ce0f8e3b4fed185fb1d5acf1780e021de2e2ca 100644 (file)
@@ -25,8 +25,7 @@
 
 #include "replace.h"
 
-struct torture_context;
-bool torture_local_talloc(struct torture_context *tctx);
+#include "talloc_testsuite.h"
 
 int main(void)
 {