talloc: Allow nested pools.
authorVolker Lendecke <vl@samba.org>
Fri, 6 Sep 2013 21:52:28 +0000 (14:52 -0700)
committerStefan Metzmacher <metze@samba.org>
Tue, 10 Mar 2015 09:55:38 +0000 (10:55 +0100)
Signed-off-by: Volker Lendecke <vl@samba.org>
Signed-off-by: Jeremy Allison <jra@samba.org>
(cherry picked from commit 20ad6d7aa3dc5e7db4d886202f757ac1f68287d4)

lib/talloc/talloc.c
lib/talloc/talloc.h
lib/talloc/testsuite.c

index 5d13567e6d8abc91e3a5f837f8df9d0f04ad26cf..198bab95f6a5851696f34db6a854e3ed0955bc53 100644 (file)
@@ -672,13 +672,6 @@ _PUBLIC_ void *talloc_pool(const void *context, size_t size)
        tc = talloc_chunk_from_ptr(result);
        pool_hdr = talloc_pool_from_chunk(tc);
 
-       if (unlikely(tc->flags & TALLOC_FLAG_POOLMEM)) {
-               /* We don't handle this correctly, so fail. */
-               talloc_log("talloc: cannot allocate pool off another pool %s\n",
-                          talloc_get_name(context));
-               talloc_free(result);
-               return NULL;
-       }
        tc->flags |= TALLOC_FLAG_POOL;
        tc->size = 0;
 
@@ -836,10 +829,19 @@ static inline void _talloc_free_poolmem(struct talloc_chunk *tc,
                 */
                pool_tc->name = location;
 
-               talloc_memlimit_update_on_free(pool_tc);
-
-               TC_INVALIDATE_FULL_CHUNK(pool_tc);
-               free(pool);
+               if (pool_tc->flags & TALLOC_FLAG_POOLMEM) {
+                       _talloc_free_poolmem(pool_tc, location);
+               } else {
+                       /*
+                        * The talloc_memlimit_update_on_free()
+                        * call takes into account the
+                        * prefix TP_HDR_SIZE allocated before
+                        * the pool talloc_chunk.
+                        */
+                       talloc_memlimit_update_on_free(pool_tc);
+                       TC_INVALIDATE_FULL_CHUNK(pool_tc);
+                       free(pool);
+               }
                return;
        }
 
@@ -869,6 +871,7 @@ static inline void _talloc_free_children_internal(struct talloc_chunk *tc,
 static inline int _talloc_free_internal(void *ptr, const char *location)
 {
        struct talloc_chunk *tc;
+       void *ptr_to_free;
 
        if (unlikely(ptr == NULL)) {
                return -1;
@@ -961,15 +964,13 @@ static inline int _talloc_free_internal(void *ptr, const char *location)
                }
 
                /*
-                * This call takes into account the
-                * prefix TP_HDR_SIZE allocated before
-                * the pool talloc_chunk.
-                */
-               talloc_memlimit_update_on_free(tc);
-
-               TC_INVALIDATE_FULL_CHUNK(tc);
-               free(pool);
-               return 0;
+                * With object_count==0, a pool becomes a normal piece of
+                * memory to free. If it's allocated inside a pool, it needs
+                * to be freed as poolmem, else it needs to be just freed.
+               */
+               ptr_to_free = pool;
+       } else {
+               ptr_to_free = tc;
        }
 
        if (tc->flags & TALLOC_FLAG_POOLMEM) {
@@ -980,7 +981,7 @@ static inline int _talloc_free_internal(void *ptr, const char *location)
        talloc_memlimit_update_on_free(tc);
 
        TC_INVALIDATE_FULL_CHUNK(tc);
-       free(tc);
+       free(ptr_to_free);
        return 0;
 }
 
@@ -2632,7 +2633,9 @@ static void talloc_memlimit_update_on_free(struct talloc_chunk *tc)
        /*
         * Pool entries don't count. Only the pools
         * themselves are counted as part of the memory
-        * limits.
+        * limits. Note that this also takes care of
+        * nested pools which have both flags
+        * TALLOC_FLAG_POOLMEM|TALLOC_FLAG_POOL set.
         */
        if (tc->flags & TALLOC_FLAG_POOLMEM) {
                return;
index f3cbcd0e7c0566aacee3e8752d065cab2eb7e04f..aa9864b436fea9a4fa444abdd8cb3b24d8b29dbf 100644 (file)
@@ -839,8 +839,7 @@ void *talloc_find_parent_bytype(const void *ptr, #type);
  * talloc pool to a talloc parent outside the pool, the whole pool memory is
  * not free(3)'ed until that moved chunk is also talloc_free()ed.
  *
- * @param[in]  context  The talloc context to hang the result off (must not
- *                     be another pool).
+ * @param[in]  context  The talloc context to hang the result off.
  *
  * @param[in]  size     Size of the talloc pool.
  *
index 426c31a8f24d2caaf7abc3881e95ce28cb641ed4..f04f4f1cc72d607b5c11d4d91104ab71b02ecffb 100644 (file)
@@ -1267,6 +1267,30 @@ static bool test_pool_steal(void)
        return true;
 }
 
+static bool test_pool_nest(void)
+{
+       void *p1, *p2, *p3;
+       void *e = talloc_new(NULL);
+
+       p1 = talloc_pool(NULL, 1024);
+       torture_assert("talloc_pool", p1 != NULL, "failed");
+
+       p2 = talloc_pool(p1, 500);
+       torture_assert("talloc_pool", p2 != NULL, "failed");
+
+       p3 = talloc_size(p2, 10);
+
+       talloc_steal(e, p3);
+
+       talloc_free(p2);
+
+       talloc_free(p3);
+
+       talloc_free(p1);
+
+       return true;
+}
+
 static bool test_free_ref_null_context(void)
 {
        void *p1, *p2, *p3;
@@ -1566,6 +1590,8 @@ bool torture_local_talloc(struct torture_context *tctx)
 
        setlinebuf(stdout);
 
+       test_reset();
+       ret &= test_pool_nest();
        test_reset();
        ret &= test_ref1();
        test_reset();