On Tue, 27 Feb 2001, Greg Stein wrote:

> Euh... I don't think we want another ring.
>
> A simpler idea is to have the apr_bucket_pool structure contain a pointer to
> an apr_bucket_heap structure. At cleanup time, you do the following:
...
> So what you get here is a single allocation of a heap bucket. Then, you
> lazily repoint all other buckets to the new heap bucket.

I came up with a really cool idea that's an even greater simplification of
this.  It's coded up and ready to go, but I figured I'd give people a
chance to comment before I commit.

Basically, instead of having the pool bucket contain a pointer to a heap
bucket and having to fool with manipulating reference counts and keeping
track of when to destroy the old apr_bucket_pool struct, the
apr_bucket_pool *contains* the apr_bucket_heap struct that it will become
as its first element.

Since the apr_bucket_heap has an apr_bucket_refcount as ITS first element,
the pool and heap bucket share an apr_bucket_refcount (ie, no more
twiddling with reference counts).

All you have to do in the cleanup routine is malloc/memcpy out of the pool
and onto the heap and set p->data to NULL, which is the flag to later
pool_read() calls that they should morph their buckets.  All that entails
is changing b->type to &apr_bucket_type_heap... even the data pointer is
already correct because the apr_bucket_heap is the first element of
apr_bucket_pool.

After the morph, heap_read() and heap_destroy() take right over where
pool_read() and pool_destroy() left off.  When the last reference is
heap_destroy'ed, heap_destroy() free's the b->data.  As far as it knows,
b->data is just an apr_bucket_heap, but it unwittingly cleans up the
entire apr_bucket_pool struct for us since the b->data pointer never
changed.

=-)

Patch attached for comments.

--Cliff
Index: buckets/apr_buckets_pool.c
===================================================================
RCS file: /home/cvs/apr-util/buckets/apr_buckets_pool.c,v
retrieving revision 1.14
diff -u -d -r1.14 apr_buckets_pool.c
--- buckets/apr_buckets_pool.c  2001/02/28 17:52:43     1.14
+++ buckets/apr_buckets_pool.c  2001/02/28 19:46:48
@@ -57,71 +57,87 @@
 
 static apr_status_t pool_bucket_cleanup(void *data)
 {
-    apr_bucket_pool *h = data;
-    apr_bucket *b = h->b;
-    apr_off_t start  = b->start;
-    apr_off_t length = b->length;
+    apr_bucket_pool *p = data;
+    apr_bucket_heap *h = &p->heap;   /* &p->heap == data */
 
-    b = apr_bucket_heap_make(b, h->base, b->length, 1, NULL);
-    b->start  = start;
-    b->length = length;
     /*
-     * XXX: this is fubar... only the *first* apr_bucket to reference
-     * the bucket is changed to a heap bucket... ALL references must
-     * be changed or at least "notified" in some way.  Fix is in the
-     * works.  --jcw
+     * If the pool gets cleaned up, we have to copy the data out
+     * of the pool and onto the heap.  But the apr_bucket's that
+     * point to this pool bucket need to be notified such that
+     * they can morph themselves into a true heap bucket the next
+     * time they try to read.  To avoid having to manipulate
+     * reference counts and b->data pointers, the apr_bucket_pool
+     * actually _contains_ an apr_bucket_heap as its first element,
+     * so the two share their apr_bucket_refcount member and you
+     * can typecast a pool bucket struct to make it look like a
+     * regular old heap bucket struct.
      */
+    h->base = malloc(h->alloc_len); /* XXX: check for failure? */
+    memcpy(h->base, p->base, h->alloc_len);
+    p->base = NULL;
+
     return APR_SUCCESS;
 }
 
 static apr_status_t pool_read(apr_bucket *b, const char **str, 
                              apr_size_t *len, apr_read_type_e block)
 {
-    apr_bucket_pool *h = b->data;
+    apr_bucket_pool *p = b->data;
+    const char *base = p->base;
 
-    *str = h->base + b->start;
+    if (base == NULL) {
+        /* pool has been cleaned up... morph to a heap bucket */
+        apr_bucket_heap *h = &p->heap;
+        b->type = &apr_bucket_type_heap;
+        base = h->base;
+    }
+    *str = base + b->start;
     *len = b->length;
     return APR_SUCCESS;
 }
 
 static void pool_destroy(void *data)
 {
-    apr_bucket_pool *h = data;
+    apr_bucket_pool *p = data;
 
-    apr_pool_cleanup_kill(h->p, data, pool_bucket_cleanup);
     if (apr_bucket_shared_destroy(data)) {
-        free(h);
+        apr_pool_cleanup_kill(p->pool, p, pool_bucket_cleanup);
+        free(p);
     }
 }
 
 APU_DECLARE(apr_bucket *) apr_bucket_pool_make(apr_bucket *b,
-               const char *buf, apr_size_t length, apr_pool_t *p)
+                      const char *buf, apr_size_t length, apr_pool_t *pool)
 {
-    apr_bucket_pool *h;
+    apr_bucket_pool *p;
+    apr_bucket_heap *h;
 
-    h = malloc(sizeof(*h));
-    if (h == NULL) {
+    p = malloc(sizeof(*p));
+    if (p == NULL) {
        return NULL;
     }
 
     /* XXX: we lose the const qualifier here which indicates
      * there's something screwy with the API...
      */
-    h->base = (char *) buf;
-    h->p    = p;
+    p->base = (char *) buf;
+    p->pool = pool;
+    h = &p->heap;
+    h->alloc_len = length; /* pre-initialize heap bucket member */
+    h->base = NULL;
 
-    b = apr_bucket_shared_make(b, h, 0, length);
+    b = apr_bucket_shared_make(b, p, 0, length);
     b->type = &apr_bucket_type_pool;
-    h->b = b;  /* XXX: see comment in pool_bucket_cleanup() */
 
-    apr_pool_cleanup_register(h->p, b->data, pool_bucket_cleanup, 
apr_pool_cleanup_null);
+    apr_pool_cleanup_register(p->pool, p, pool_bucket_cleanup,
+                              apr_pool_cleanup_null);
     return b;
 }
 
 APU_DECLARE(apr_bucket *) apr_bucket_pool_create(
-               const char *buf, apr_size_t length, apr_pool_t *p)
+               const char *buf, apr_size_t length, apr_pool_t *pool)
 {
-    apr_bucket_do_create(apr_bucket_pool_make(b, buf, length, p));
+    apr_bucket_do_create(apr_bucket_pool_make(b, buf, length, pool));
 }
 
 APU_DECLARE_DATA const apr_bucket_type_t apr_bucket_type_pool = {
Index: include/apr_buckets.h
===================================================================
RCS file: /home/cvs/apr-util/include/apr_buckets.h,v
retrieving revision 1.86
diff -u -d -r1.86 apr_buckets.h
--- include/apr_buckets.h       2001/02/28 17:52:46     1.86
+++ include/apr_buckets.h       2001/02/28 19:47:01
@@ -499,41 +499,41 @@
 /*  *****  Reference-counted bucket types  *****  */
 
 
-typedef struct apr_bucket_pool apr_bucket_pool;
+typedef struct apr_bucket_heap apr_bucket_heap;
 /**
- * A bucket referring to data allocated from a pool
+ * A bucket referring to data allocated off the heap.
  */
-struct apr_bucket_pool {
+struct apr_bucket_heap {
     /** Number of buckets using this memory */
     apr_bucket_refcount  refcount;
     /** The start of the data actually allocated.  This should never be
      * modified, it is only used to free the bucket.
      */
-    const char *base;
-    /** The pool the data was allocated from */
-    apr_pool_t  *p;
-    /** This is a hack, because we call apr_destroy_bucket with the ->data
-     *  pointer, so the pool cleanup needs to be registered with that pointer,
-     *  but the whole point of the cleanup is to convert the bucket to another
-     *  type.  To do that conversion, we need a pointer to the bucket itself.
-     *  This gives us a pointer to the original bucket.
-     */
-    apr_bucket *b;
+    char    *base;
+    /** how much memory was allocated */
+    size_t  alloc_len;
 };
 
-typedef struct apr_bucket_heap apr_bucket_heap;
+typedef struct apr_bucket_pool apr_bucket_pool;
 /**
- * A bucket referring to data allocated off the heap.
+ * A bucket referring to data allocated from a pool
  */
-struct apr_bucket_heap {
-    /** Number of buckets using this memory */
-    apr_bucket_refcount  refcount;
+struct apr_bucket_pool {
+    /** The pool bucket must be able to be easily morphed to a heap
+     * bucket if the pool gets cleaned up before all references are
+     * destroyed.  To avoid having to do many extra refcount
+     * manipulations and b->data manipulations, the apr_bucket_pool
+     * struct actually *contains* the apr_bucket_heap struct that it
+     * will become as its first element.  The two share their
+     * apr_bucket_refcount members.
+     */
+    apr_bucket_heap  heap;
     /** The start of the data actually allocated.  This should never be
      * modified, it is only used to free the bucket.
      */
-    char    *base;
-    /** how much memory was allocated */
-    size_t  alloc_len;
+    const char *base;
+    /** The pool the data was allocated from */
+    apr_pool_t *pool;
 };
 
 #if APR_HAS_MMAP
@@ -1128,25 +1128,25 @@
  * Create a bucket referring to memory allocated from a pool.
  *
  * @param buf The buffer to insert into the bucket
- * @param p The pool the memory was allocated from
+ * @param pool The pool the memory was allocated from
  * @return The new bucket, or NULL if allocation failed
- * @deffunc apr_bucket *apr_bucket_pool_create(const char *buf, apr_size_t 
*length, apr_pool_t *p)
+ * @deffunc apr_bucket *apr_bucket_pool_create(const char *buf, apr_size_t 
*length, apr_pool_t *pool)
  */
 APU_DECLARE(apr_bucket *) 
                 apr_bucket_pool_create(const char *buf, apr_size_t length,
-                                      apr_pool_t *p);
+                                      apr_pool_t *pool);
 
 /**
  * Make the bucket passed in a bucket refer to pool data
  * @param b The bucket to make into a pool bucket
  * @param buf The buffer to insert into the bucket
- * @param p The pool the memory was allocated from
+ * @param pool The pool the memory was allocated from
  * @return The new bucket, or NULL if allocation failed
- * @deffunc apr_bucket *apr_bucket_pool_make(apr_bucket *b, const char *buf, 
apr_size_t *length, apr_pool_t *p)
+ * @deffunc apr_bucket *apr_bucket_pool_make(apr_bucket *b, const char *buf, 
apr_size_t *length, apr_pool_t *pool)
  */
 APU_DECLARE(apr_bucket *) 
                 apr_bucket_pool_make(apr_bucket *b, const char *buf, 
-                                    apr_size_t length, apr_pool_t *p);
+                                    apr_size_t length, apr_pool_t *pool);
 
 #if APR_HAS_MMAP
 /**

Reply via email to