You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@apr.apache.org by jw...@apache.org on 2001/03/01 05:46:13 UTC

cvs commit: apr-util/include apr_buckets.h

jwoolley    01/02/28 20:46:13

  Modified:    buckets  apr_buckets_pool.c
               include  apr_buckets.h
  Log:
  Rework pool buckets so that they can effectively handle the
  pool being cleaned up.  Each reference to the pool bucket
  changes its type to a heap bucket the first time it calls
  pool_read(), and the heap bucket code takes over from there.
  See the inline commentary for details on how this magic happens.
  
  Inspired by:  Greg Stein
  
  Revision  Changes    Path
  1.15      +64 -29    apr-util/buckets/apr_buckets_pool.c
  
  Index: apr_buckets_pool.c
  ===================================================================
  RCS file: /home/cvs/apr-util/buckets/apr_buckets_pool.c,v
  retrieving revision 1.14
  retrieving revision 1.15
  diff -u -d -u -r1.14 -r1.15
  --- apr_buckets_pool.c	2001/02/28 17:52:43	1.14
  +++ apr_buckets_pool.c	2001/03/01 04:46:13	1.15
  @@ -57,71 +57,106 @@
   
   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;
   
  -    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_buckets out there
  +     * that point to this pool bucket need to be notified such that
  +     * they can morph themselves into a regular 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.
        */
  +    p->heap.base = malloc(p->heap.alloc_len);
  +    memcpy(p->heap.base, p->base, p->heap.alloc_len);
  +    p->base = NULL;
  +    p->pool = 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 (p->pool == NULL) {
  +        /*
  +         * pool has been cleaned up... masquerade as a heap bucket from now
  +         * on. subsequent bucket operations will use the heap bucket code.
  +         */
  +        b->type = &apr_bucket_type_heap;
  +        base = p->heap.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);
  +    /* If the pool is cleaned up before the last reference goes
  +     * away, buckets that have performed at least one read will
  +     * have been turned into heap buckets, so heap_destroy() takes
  +     * over.  If the very last reference to be destroyed now
  +     * considers itself a heap bucket, we don't have to worry about
  +     * it anymore... free() in heap_destroy() thinks it's freeing
  +     * an apr_bucket_heap, when in reality it's freeing the whole
  +     * apr_bucket_pool for us.  We have to be a little careful, though,
  +     * because it's _possible_ the very last reference to be destroyed
  +     * has never been read since the pool was cleaned up, in which
  +     * case we have to avoid deregistering a pool cleanup that
  +     * has already taken place.
  +     */
  +    if (apr_bucket_shared_destroy(p)) {
  +        if (p->pool) {
  +            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;
   
  -    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;
  +    /* XXX: why is this?  buf is const, p->base is const... what's
  +     * the problem?  --jcw */
  +    p->base = (char *) buf;
  +    p->pool = pool;
   
  -    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);
  +    /* pre-initialize heap bucket member */
  +    p->heap.alloc_len = length;
  +    p->heap.base      = 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 = {
  
  
  
  1.87      +38 -28    apr-util/include/apr_buckets.h
  
  Index: apr_buckets.h
  ===================================================================
  RCS file: /home/cvs/apr-util/include/apr_buckets.h,v
  retrieving revision 1.86
  retrieving revision 1.87
  diff -u -d -u -r1.86 -r1.87
  --- apr_buckets.h	2001/02/28 17:52:46	1.86
  +++ apr_buckets.h	2001/03/01 04:46:13	1.87
  @@ -499,28 +499,6 @@
   /*  *****  Reference-counted bucket types  *****  */
   
   
  -typedef struct apr_bucket_pool apr_bucket_pool;
  -/**
  - * A bucket referring to data allocated from a pool
  - */
  -struct apr_bucket_pool {
  -    /** 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;
  -};
  -
   typedef struct apr_bucket_heap apr_bucket_heap;
   /**
    * A bucket referring to data allocated off the heap.
  @@ -536,6 +514,38 @@
       size_t  alloc_len;
   };
   
  +typedef struct apr_bucket_pool apr_bucket_pool;
  +/**
  + * A bucket referring to data allocated from a pool
  + */
  +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.  This apr_bucket_heap structure is populated automatically
  +     * when the pool gets cleaned up, and subsequent calls to pool_read()
  +     * will result in the apr_bucket in question being morphed into a
  +     * regular heap bucket.  (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 block of data actually allocated from the pool.
  +     * Segments of this block are referenced by adjusting
  +     * the start and length of the apr_bucket accordingly.
  +     * This will be NULL after the pool gets cleaned up.
  +     */
  +    const char *base;
  +    /** The pool the data was allocated from.  When the pool
  +     * is cleaned up, this gets set to NULL as an indicator
  +     * to pool_read() that the data is now on the heap and
  +     * so it should morph the bucket into a regular heap
  +     * bucket before continuing.
  +     */
  +    apr_pool_t *pool;
  +};
  +
   #if APR_HAS_MMAP
   typedef struct apr_bucket_mmap apr_bucket_mmap;
   /**
  @@ -1128,25 +1138,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
   /**