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
/**