apr-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From "Sander Striker" <stri...@apache.org>
Subject Pools rewrite [4]
Date Sun, 09 Dec 2001 01:00:33 GMT
Hi,

This is the code with the latest changes based on comments
from various people.

I have yet to begin to start coding/migrating the debug
code.  I won't have time to complete that until at least
last week.  Bill (wrowe) can you live without that for
a little while?  I specifically ask because you have
recently added a bunch of debug code to pools.

What else?  Oh, there is a patch for apr_initialize() and
apr_terminate() included.  The creation/destruction of the
'global' pool now happens symmetrically in apr_pool_initialize()/
apr_pool_terminate().

Things to do are:
 - debug code (as mentioned);
 - cleaning up the includes (still an inheritence of the
   original pools code);
 - use apr_thread_once() in apr_[pool_]initialize() #if
   APR_HAS_THREADS.


Sander

PS.  Code/patch are inlined since last time it seemed to have
     worked with my new outlook settings.

===================================================================
Index: misc/unix/start.c
===================================================================
RCS file: /home/cvs/apr/misc/unix/start.c,v
retrieving revision 1.57
diff -u -r1.57 start.c
--- misc/unix/start.c   2001/11/27 02:31:55     1.57
+++ misc/unix/start.c   2001/12/09 00:21:13
@@ -64,10 +64,10 @@


 static int initialized = 0;
-static apr_pool_t *global_apr_pool;

 APR_DECLARE(apr_status_t) apr_initialize(void)
 {
+    apr_pool_t *pool;
     apr_status_t status;
 #if defined WIN32 || defined(NETWARE)
     int iVersionRequested;
@@ -82,21 +82,31 @@
         return APR_SUCCESS;
     }

-    if (apr_pool_create(&global_apr_pool, NULL) != APR_SUCCESS) {
+#if !defined(BEOS) && !defined(OS2) && !defined(WIN32) && !defined(NETWARE)
+    apr_unix_setup_lock();
+    apr_proc_mutex_unix_setup_lock();
+    apr_unix_setup_time();
+#endif
+
+#if defined(NETWARE)
+    apr_netware_setup_time();
+#endif
+
+    if ((status = apr_pool_initialize()) != APR_SUCCESS)
+        return status;
+
+    if (apr_pool_create(&pool, NULL) != APR_SUCCESS) {
         return APR_ENOPOOL;
     }

 #ifdef WIN32
     /* Initialize apr_os_level global */
-    if (apr_get_oslevel(global_apr_pool, &osver) != APR_SUCCESS) {
+    if (apr_get_oslevel(pool, &osver) != APR_SUCCESS) {
         return APR_EEXIST;
     }
 #endif
-#if !defined(BEOS) && !defined(OS2) && !defined(WIN32) && !defined(NETWARE)
-    apr_unix_setup_lock();
-    apr_proc_mutex_unix_setup_lock();
-    apr_unix_setup_time();
-#elif defined WIN32 || defined(NETWARE)
+
+#if defined WIN32 || defined(NETWARE)
     iVersionRequested = MAKEWORD(WSAHighByte, WSALowByte);
     err = WSAStartup((WORD) iVersionRequested, &wsaData);
     if (err) {
@@ -108,14 +118,8 @@
         return APR_EEXIST;
     }
 #endif
-#if defined(NETWARE)
-    apr_netware_setup_time();
-#endif
-
-    if ((status = apr_pool_alloc_init(global_apr_pool)) != APR_SUCCESS)
-        return status;
-
-    apr_signal_init(global_apr_pool);
+
+    apr_signal_init(pool);

     return APR_SUCCESS;
 }
@@ -126,7 +130,8 @@
     if (initialized) {
         return;
     }
-    apr_pool_alloc_term(global_apr_pool);
+    apr_pool_terminate();
+
 #if defined(NETWARE)
     WSACleanup();
 #endif

===================================================================
memory/unix/apr_pools.c
===================================================================

/* ====================================================================
 * The Apache Software License, Version 1.1
 *
 * Copyright (c) 2000-2001 The Apache Software Foundation.  All rights
 * reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *
 * 3. The end-user documentation included with the redistribution,
 *    if any, must include the following acknowledgment:
 *       "This product includes software developed by the
 *        Apache Software Foundation (http://www.apache.org/)."
 *    Alternately, this acknowledgment may appear in the software itself,
 *    if and wherever such third-party acknowledgments normally appear.
 *
 * 4. The names "Apache" and "Apache Software Foundation" must
 *    not be used to endorse or promote products derived from this
 *    software without prior written permission. For written
 *    permission, please contact apache@apache.org.
 *
 * 5. Products derived from this software may not be called "Apache",
 *    nor may "Apache" appear in their name, without prior written
 *    permission of the Apache Software Foundation.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 * ====================================================================
 *
 * This software consists of voluntary contributions made by many
 * individuals on behalf of the Apache Software Foundation.  For more
 * information on the Apache Software Foundation, please see
 * <http://www.apache.org/>.
 */

#include "apr.h"
#include "apr_private.h"

/* TODO: Clean out the #includes */

#include "apr_portable.h" /* for get_os_proc */
#include "apr_strings.h"
#include "apr_general.h"
#include "apr_pools.h"
#include "apr_lib.h"
#include "apr_thread_mutex.h"
#include "apr_hash.h"
#define APR_WANT_MEMFUNC
#include "apr_want.h"

#if APR_HAVE_STDIO_H
#include <stdio.h>
#endif
#ifdef HAVE_SYS_STAT_H
#include <sys/stat.h>
#endif
#if APR_HAVE_SYS_SIGNAL_H
#include <sys/signal.h>
#endif
#if APR_HAVE_SIGNAL_H
#include <signal.h>
#endif
#if APR_HAVE_SYS_WAIT_H
#include <sys/wait.h>
#endif
#if APR_HAVE_SYS_TYPES_H
#include <sys/types.h>
#endif
#if APR_HAVE_UNISTD_H
#include <unistd.h>
#endif
#if APR_HAVE_FCNTL_H
#include <fcntl.h>
#endif
#if APR_HAVE_STRING_H
#include <string.h>
#endif
#if APR_HAVE_STDLIB_H
#include <stdlib.h>
#endif
#ifdef HAVE_MALLOC_H
#include <malloc.h>
#endif



/*
 * Magic numbers
 */

#define MIN_ALLOC 8192
#define MAX_INDEX   20

#define BOUNDARY_INDEX 12
#define BOUNDARY_SIZE (1 << BOUNDARY_INDEX)

/*
 * Macros and defines
 */

/* ALIGN() is only to be used to align on a power of 2 boundary */
#define ALIGN(size, boundary) \
    (((size) + ((boundary) - 1)) & ~((boundary) - 1))

#define ALIGN_DEFAULT(size) ALIGN(size, 8)

#if APR_HAS_THREADS
#define LOCK(mutex) \
    do { \
        if (mutex) \
            apr_thread_mutex_lock(mutex); \
    } while(0)

#define UNLOCK(mutex) \
    do { \
        if (mutex) \
            apr_thread_mutex_unlock(mutex); \
    } while(0)
#else
#define LOCK(mutex)
#define UNLOCK(mutex)
#endif

/*
 * Structures
 */

typedef struct cleanup_t cleanup_t;
typedef struct allocator_t allocator_t;
typedef struct node_t node_t;

struct node_t {
    node_t      *next;
    node_t     **ref;
    apr_uint32_t index;
    char        *first_avail;
    char        *endp;
};

struct allocator_t {
    apr_uint32_t        max_index;
    apr_thread_mutex_t *mutex;
    apr_pool_t         *owner;
    node_t             *free[MAX_INDEX];
};

/* The ref field in the apr_pool_t struct holds a
 * pointer to the pointer referencing this pool.
 * It is used for parent, child, sibling management.
 * Look at apr_pool_create_ex() and apr_pool_destroy()
 * to see how it is used.
 */
struct apr_pool_t {
    allocator_t          *allocator;
    node_t               *active;
    node_t               *self; /* The node containing the pool itself */
    char                 *self_first_avail;
    apr_pool_t           *parent;
    apr_pool_t           *child;
    apr_pool_t           *sibling;
    apr_pool_t          **ref;
    cleanup_t            *cleanups;
    struct process_chain *subprocesses;
    apr_abortfunc_t       abort_fn;
    apr_hash_t           *user_data;
#ifdef APR_POOL_DEBUG
    const char           *tag;
#endif
};

#define SIZEOF_NODE_T       ALIGN_DEFAULT(sizeof(node_t))
#define SIZEOF_ALLOCATOR_T  ALIGN_DEFAULT(sizeof(allocator_t))
#define SIZEOF_POOL_T       ALIGN_DEFAULT(sizeof(apr_pool_t))

/*
 * Variables
 */

static apr_pool_t  *global_pool = NULL;
static apr_byte_t   global_allocator_initialized = 0;
static allocator_t  global_allocator = { 
    0,          /* max_index */
    NULL,       /* mutex */
    NULL,       /* owner */
    { NULL }    /* free[0] */
};

/*
 * Memory allocation
 */

static APR_INLINE node_t *node_malloc(allocator_t *allocator, apr_size_t size)
{
    node_t *node, **ref;
    apr_uint32_t i, index, max_index; 

    /* Round up the block size to the next boundary, but always
     * allocate at least a certain size (MIN_ALLOC).
     */
    size = ALIGN(size + SIZEOF_NODE_T, BOUNDARY_SIZE);
    if (size < MIN_ALLOC)
        size = MIN_ALLOC;

    /* Find the index for this node size by
     * deviding its size by the boundary size
     */
    index = (size >> BOUNDARY_INDEX) - 1;

    /* First see if there are any nodes in the area we know
     * our node will fit into.
     */
    if (index <= allocator->max_index) {
        LOCK(allocator->mutex);

        /* Walk the free list to see if there are
         * any nodes on it of the requested size
         *
         * NOTE: an optimization would be to check
         * allocator->free[index] first and if no
         * node is present, directly use 
         * allocator->free[max_index].  This seems
         * like overkill though and could cause
         * memory waste.
         */
        max_index = allocator->max_index;
        ref = &allocator->free[index];
        i = index;
        while (*ref == NULL && i < max_index) {
           ref++;
           i++;
        }

        if ((node = *ref) != NULL) {
            /* If we have found a node and it doesn't have any
             * nodes waiting in line behind it _and_ we are on
             * the highest available index, find the new highest
             * available index
             */
            if ((*ref = node->next) == NULL && i >= max_index) {
                do {
                    ref--;
                    max_index--;
                }
                while (*ref == NULL && max_index > 0);

                allocator->max_index = max_index;
            }
            else
                node->next = NULL;

            UNLOCK(allocator->mutex);

            return node;
        }

        UNLOCK(allocator->mutex);
    }

    /* If we found nothing, seek the sink (at index 0), if
     * it is not empty.
     */
    else if (allocator->free[0]) {
        LOCK(allocator->mutex);

        /* Walk the free list to see if there are
         * any nodes on it of the requested size
         */
        ref = &allocator->free[0];
        while ((node = *ref) != NULL && index > node->index)
            ref = &node->next;

        if (node) {
            *ref = node->next;
            node->next = NULL;
            
            UNLOCK(allocator->mutex);

            return node;
        }
        
        UNLOCK(allocator->mutex);
    }

    /* If we haven't got a suitable node, malloc a new one
     * and initialize it.
     */
    if ((node = malloc(size)) == NULL)
        return NULL;

    node->next = NULL;
    node->index = index;
    node->first_avail = (char *)node + SIZEOF_NODE_T;
    node->endp = (char *)node + size;

    return node;
}

static APR_INLINE void node_free(allocator_t *allocator, node_t *node)
{
    node_t *next;
    apr_uint32_t index, max_index;

    LOCK(allocator->mutex);

    max_index = allocator->max_index;

    /* Walk the list of submitted nodes and free them one by one,
     * shoving them in the right 'size' buckets as we go.
     */
    do {
        next = node->next;
        index = node->index;

        if (index < MAX_INDEX) {
            /* Add the node to the appropiate 'size' bucket.  Adjust
             * the max_index when appropiate.
             */
            if ((node->next = allocator->free[index]) == NULL && index > max_index) {
                 max_index = index;
            }
            allocator->free[index] = node;
        }
        else {
            /* This node is too large to keep in a specific size bucket,
             * just add it to the sink (at index 0).
             */
            node->next = allocator->free[0];
            allocator->free[0] = node;
        }
    }
    while ((node = next) != NULL);

    allocator->max_index = max_index;

    UNLOCK(allocator->mutex);
}

APR_DECLARE(void *) apr_palloc(apr_pool_t *pool, apr_size_t size)
{
    node_t *active, *node;
    void *mem;
    char *endp;

    size = ALIGN_DEFAULT(size);
    active = pool->active;

    /* If the active node has enough bytes left, use it. */
    endp = active->first_avail + size;
    if (endp < active->endp) {
        mem = active->first_avail;
        active->first_avail = endp;
        
        return mem;
    }

    /* Reset the active node, get ourselves a new one and activate it. */
    active->first_avail = (char *)active + SIZEOF_NODE_T;

    if ((node = node_malloc(pool->allocator, size)) == NULL) {
        active->first_avail = active->endp;

        if (pool->abort_fn)
            pool->abort_fn(APR_ENOMEM);

        return NULL;
    }

    active->next = pool->active = node; 
    node->ref = &active->next;

    mem = node->first_avail;
    node->first_avail += size;
    
    return mem;
}

APR_DECLARE(void *) apr_pcalloc(apr_pool_t *pool, apr_size_t size)
{
    node_t *active, *node;
    void *mem;
    char *endp;

    size = ALIGN_DEFAULT(size);
    active = pool->active;

    /* If the active node has enough bytes left, use it. */
    endp = active->first_avail + size;
    if (endp < active->endp) {
        mem = active->first_avail;
        active->first_avail = endp;

        memset(mem, 0, size);
        
        return mem;
    }

    /* Reset the active node, get ourselves a new one and activate it. */
    active->first_avail = (char *)active + SIZEOF_NODE_T;

    if ((node = node_malloc(pool->allocator, size)) == NULL) {
        active->first_avail = active->endp;

        if (pool->abort_fn)
            pool->abort_fn(APR_ENOMEM);

        return NULL;
    }

    active->next = pool->active = node; 
    node->ref = &active->next;

    mem = node->first_avail;
    node->first_avail += size;
    
    memset(mem, 0, size);
    
    return mem;
}

/*
 * Pool management
 */

static void run_cleanups(cleanup_t *c);
static void free_proc_chain(struct process_chain *procs);

APR_DECLARE(void) apr_pool_clear(apr_pool_t *pool)
{
    node_t *active;

    /* Destroy the subpools.  The subpools will detach themselve from 
     * this pool thus this loop is safe and easy.
     */
    while (pool->child)
        apr_pool_destroy(pool->child);

    /* Run cleanups and free any subprocesses. */
    run_cleanups(pool->cleanups);
    pool->cleanups = NULL;
    free_proc_chain(pool->subprocesses);
    pool->subprocesses = NULL;

    /* Reset the active node */
    if ((active = pool->active) == pool->self) {
        active->first_avail = pool->self_first_avail;
        return;
    }

    active->first_avail = (char *)active + SIZEOF_NODE_T;

    /* Find the node attached to the pool structure, make
     * it the active node and free the rest of the nodes.
     */
    active = pool->active = pool->self; 
    active->first_avail = pool->self_first_avail;
    node_free(pool->allocator, active->next);
    active->next = NULL;
}

APR_DECLARE(void) apr_pool_destroy(apr_pool_t *pool)
{
    node_t *node, *active, **ref;
    allocator_t *allocator;
    apr_thread_mutex_t *mutex;
    apr_uint32_t index;

    /* Destroy the subpools.  The subpools will detach themselve from 
     * this pool thus this loop is safe and easy.
     */
    while (pool->child)
        apr_pool_destroy(pool->child);

    /* Run cleanups and free any subprocesses. */
    run_cleanups(pool->cleanups);
    free_proc_chain(pool->subprocesses);

    /* Remove the pool from the parents child list */
    if (pool->parent) {
        mutex = pool->parent->allocator->mutex;

        LOCK(mutex);

        if ((*pool->ref = pool->sibling) != NULL)
            pool->sibling->ref = pool->ref;

        UNLOCK(mutex);
    }
    
    /* Reset the active block */
    active = pool->active;
    active->first_avail = (char *)active + SIZEOF_NODE_T;

    /* Find the block attached to the pool structure.  Save a copy of the
     * allocator pointer, because the pool struct soon will be no more.
     */
    allocator = pool->allocator;
    active = pool->self;
    active->first_avail = (char *)active + SIZEOF_NODE_T;

    /* If this pool happens to be the owner of the allocator, free 
     * everything in the allocator (that includes the pool struct
     * and the allocator).  Don't worry about destroying the optional mutex
     * in the allocator, it will have been destroyed by the cleanup function.
     */
    if (allocator->owner == pool) {
        for (index = 0; index < MAX_INDEX; index++) {
            ref = &allocator->free[index];
            while ((node = *ref) != NULL) {
                *ref = node->next;
                free(node);
            }
        }
            
        ref = &active;
        while ((node = *ref) != NULL) {
            *ref = node->next;
            free(node);
        }
            
        return;
    }

    /* Free all the nodes in the pool (including the node holding the
     * pool struct), by giving them back to the allocator.
     */
    node_free(allocator, active);
}

APR_DECLARE(apr_status_t) apr_pool_create_ex(apr_pool_t **newpool, 
                                             apr_pool_t *parent,
                                             apr_abortfunc_t abort_fn,
                                             apr_uint32_t flags)
{
    apr_pool_t *pool;
    node_t *node;
    allocator_t *allocator, *new_allocator;
    apr_status_t rv;

    *newpool = NULL;

    if (!parent)
        parent = global_pool;

    allocator = parent ? parent->allocator : &global_allocator;
    if ((node = node_malloc(allocator, MIN_ALLOC - SIZEOF_NODE_T)) == NULL) {
        if (abort_fn)
            abort_fn(APR_ENOMEM);

        return APR_ENOMEM;
    }

    if ((flags & APR_POOL_FNEW_ALLOCATOR) == APR_POOL_FNEW_ALLOCATOR) {
        new_allocator = (allocator_t *)node->first_avail;
        pool = (apr_pool_t *)((char *)new_allocator + SIZEOF_ALLOCATOR_T);
        node->first_avail = pool->self_first_avail = (char *)pool + SIZEOF_POOL_T;
        
        memset(new_allocator, 0, SIZEOF_ALLOCATOR_T);
        new_allocator->owner = pool;

        pool->allocator = new_allocator;
        pool->active = pool->self = node;
        pool->abort_fn = abort_fn;
        pool->child = NULL;
        pool->cleanups = NULL;
        pool->subprocesses = NULL;
        pool->user_data = NULL;
#ifdef APR_POOL_DEBUG
        pool->tag = NULL;
#endif

#if APR_HAS_THREADS
        if ((flags & APR_POOL_FLOCK) == APR_POOL_FLOCK) {
            if ((rv = apr_thread_mutex_create(&allocator->mutex, 
                    APR_THREAD_MUTEX_DEFAULT, pool)) != APR_SUCCESS) {
                node_free(allocator, node);
                return rv;
            }
        }
#endif
    }
    else {
        pool = (apr_pool_t *)node->first_avail;
        node->first_avail = pool->self_first_avail = (char *)pool + SIZEOF_POOL_T;
    
        pool->allocator = allocator;
        pool->active = pool->self = node;
        pool->abort_fn = abort_fn;
        pool->child = NULL;
        pool->cleanups = NULL;
        pool->subprocesses = NULL;
        pool->user_data = NULL;
#ifdef APR_POOL_DEBUG
        pool->tag = NULL;
#endif
    }

    if ((pool->parent = parent) != NULL) {
        LOCK(allocator->mutex);

        if ((pool->sibling = parent->child) != NULL)
            pool->sibling->ref = &pool->sibling;

        parent->child = pool;
        pool->ref = &parent->child;

        UNLOCK(allocator->mutex);
    }
    else {
        pool->sibling = NULL;
        pool->ref = &pool->sibling;
    }

    *newpool = pool;

    return APR_SUCCESS;
}

APR_DECLARE(void) apr_pool_set_abort(apr_abortfunc_t abort_fn,
                                     apr_pool_t *pool)
{
    pool->abort_fn = abort_fn;
}

APR_DECLARE(apr_abortfunc_t) apr_pool_get_abort(apr_pool_t *pool)
{
    return pool->abort_fn;
}

APR_DECLARE(apr_pool_t *) apr_pool_get_parent(apr_pool_t *pool)
{
    return pool->parent;
}

/* return TRUE if a is an ancestor of b
 * NULL is considered an ancestor of all pools
 */
APR_DECLARE(int) apr_pool_is_ancestor(apr_pool_t *a, apr_pool_t *b)
{
    if (a == NULL)
        return 1;

    while (b) {
        if (a == b)
            return 1;

        b = b->parent;
    }

    return 0;
}

/*
 * Initialization
 */

APR_DECLARE(apr_status_t) apr_pool_initialize(void)
{
    apr_status_t rv;

    if (global_allocator_initialized++)
        return APR_SUCCESS;
    
    memset(&global_allocator, 0, SIZEOF_ALLOCATOR_T);

    if ((rv = apr_pool_create_ex(&global_pool, NULL, NULL, APR_POOL_FDEFAULT)) != APR_SUCCESS) {
        return rv;
    }
    
#if APR_HAS_THREADS    
    if ((rv = apr_thread_mutex_create(&global_allocator.mutex, 
                  APR_THREAD_MUTEX_DEFAULT, global_pool)) != APR_SUCCESS) {
        return rv;
    }
#endif

    global_allocator.owner = global_pool;
    global_allocator_initialized = 1;

    return APR_SUCCESS;
}

APR_DECLARE(void) apr_pool_terminate(void)
{
    if (!global_allocator_initialized)
        return;

    global_allocator_initialized = 0;
    
    apr_pool_destroy(global_pool); /* This will also destroy the mutex */
    global_pool = NULL;

    memset(&global_allocator, 0, SIZEOF_ALLOCATOR_T);
}

/*
 * Cleanup
 */

struct cleanup_t {
    struct cleanup_t *next;
    const void *data;
    apr_status_t (*plain_cleanup_fn)(void *data);
    apr_status_t (*child_cleanup_fn)(void *data);
};

APR_DECLARE(void) apr_pool_cleanup_register(apr_pool_t *p, const void *data,
                      apr_status_t (*plain_cleanup_fn)(void *data),
                      apr_status_t (*child_cleanup_fn)(void *data))
{
    cleanup_t *c;

    if (p != NULL) {
        c = (cleanup_t *) apr_palloc(p, sizeof(cleanup_t));
        c->data = data;
        c->plain_cleanup_fn = plain_cleanup_fn;
        c->child_cleanup_fn = child_cleanup_fn;
        c->next = p->cleanups;
        p->cleanups = c;
    }
}

APR_DECLARE(void) apr_pool_cleanup_kill(apr_pool_t *p, const void *data,
                    apr_status_t (*cleanup_fn)(void *))
{
    cleanup_t *c, **lastp;

    if (p == NULL)
        return;

    c = p->cleanups;
    lastp = &p->cleanups;
    while (c) {
        if (c->data == data && c->plain_cleanup_fn == cleanup_fn) {
            *lastp = c->next;
            break;
        }

        lastp = &c->next;
        c = c->next;
    }
}

APR_DECLARE(void) apr_pool_child_cleanup_set(apr_pool_t *p, const void *data,
                                       apr_status_t (*plain_cleanup_fn) (void *),
                                       apr_status_t (*child_cleanup_fn) (void *))
{
    cleanup_t *c;

    if (p == NULL)
        return;

    c = p->cleanups;
    while (c) {
        if (c->data == data && c->plain_cleanup_fn == plain_cleanup_fn) {
            c->child_cleanup_fn = child_cleanup_fn;
            break;
        }

        c = c->next;
    }
}

APR_DECLARE(apr_status_t) apr_pool_cleanup_run(apr_pool_t *p, void *data,
                                       apr_status_t (*cleanup_fn) (void *))
{
    apr_pool_cleanup_kill(p, data, cleanup_fn);
    return (*cleanup_fn)(data);
}

static void run_cleanups(cleanup_t *c)
{
    while (c) {
        (*c->plain_cleanup_fn)((void *)c->data);
        c = c->next;
    }
}

static void run_child_cleanups(cleanup_t *c)
{
    while (c) {
        (*c->child_cleanup_fn)((void *)c->data);
        c = c->next;
    }
}

static void cleanup_pool_for_exec(apr_pool_t *p)
{
    run_child_cleanups(p->cleanups);
    p->cleanups = NULL;

    for (p = p->child; p; p = p->sibling)
        cleanup_pool_for_exec(p);
}

APR_DECLARE(void) apr_pool_cleanup_for_exec(void)
{
#if !defined(WIN32) && !defined(OS2)
    /*
     * Don't need to do anything on NT or OS/2, because I
     * am actually going to spawn the new process - not
     * exec it. All handles that are not inheritable, will
     * be automajically closed. The only problem is with
     * file handles that are open, but there isn't much
     * I can do about that (except if the child decides
     * to go out and close them
     */
    cleanup_pool_for_exec(global_pool);
#endif /* !defined(WIN32) && !defined(OS2) */
}

APR_DECLARE_NONSTD(apr_status_t) apr_pool_cleanup_null(void *data)
{
    /* do nothing cleanup routine */
    return APR_SUCCESS;
}

/*
 * Debug functions
 */

#ifdef APR_POOL_DEBUG
APR_DECLARE(void) apr_pool_tag(apr_pool_t *pool, const char *tag)
{
    pool->tag = tag;
}
#endif

/*
 * User data management
 */

APR_DECLARE(apr_status_t) apr_pool_userdata_set(const void *data, const char *key,
                                                apr_status_t (*cleanup) (void *),
                                                apr_pool_t *pool)
{
    if (pool->user_data == NULL)
        pool->user_data = apr_hash_make(pool);

    if (apr_hash_get(pool->user_data, key, APR_HASH_KEY_STRING) == NULL) {
        char *new_key = apr_pstrdup(pool, key);
        apr_hash_set(pool->user_data, new_key, APR_HASH_KEY_STRING, data);
    } 
    else {
        apr_hash_set(pool->user_data, key, APR_HASH_KEY_STRING, data);
    }

    if (cleanup)
        apr_pool_cleanup_register(pool, data, cleanup, cleanup);
        
    return APR_SUCCESS;
}

APR_DECLARE(apr_status_t) apr_pool_userdata_setn(const void *data, const char *key,
                                                 apr_status_t (*cleanup) (void *),
                                                 apr_pool_t *pool)
{
    if (pool->user_data == NULL)
        pool->user_data = apr_hash_make(pool);

    apr_hash_set(pool->user_data, key, APR_HASH_KEY_STRING, data);

    if (cleanup)
        apr_pool_cleanup_register(pool, data, cleanup, cleanup);
    
    return APR_SUCCESS;
}

APR_DECLARE(apr_status_t) apr_pool_userdata_get(void **data, const char *key, apr_pool_t *pool)
{
    if (pool->user_data == NULL)
        *data = NULL;
    else
        *data = apr_hash_get(pool->user_data, key, APR_HASH_KEY_STRING);

    return APR_SUCCESS;
}


/*
 * "Print" functions
 */

/*
 * apr_psprintf is implemented by writing directly into the current
 * block of the pool, starting right at first_avail.  If there's
 * insufficient room, then a new block is allocated and the earlier
 * output is copied over.  The new block isn't linked into the pool
 * until all the output is done.
 *
 * Note that this is completely safe because nothing else can
 * allocate in this apr_pool_t while apr_psprintf is running.  alarms are
 * blocked, and the only thing outside of alloc.c that's invoked
 * is apr_vformatter -- which was purposefully written to be
 * self-contained with no callouts.
 */

struct psprintf_data {
    apr_vformatter_buff_t vbuff;
    node_t               *node;
    allocator_t          *allocator;
    apr_byte_t            got_a_new_node;
    node_t               *free;
};

static int psprintf_flush(apr_vformatter_buff_t *vbuff)
{
    struct psprintf_data *ps = (struct psprintf_data *)vbuff;
    node_t *node, *active;
    apr_size_t cur_len;
    char *strp;
    allocator_t *allocator;

    allocator = ps->allocator;
    node = ps->node;
    strp = ps->vbuff.curpos;
    cur_len = strp - node->first_avail;

    if ((active = node_malloc(allocator, cur_len << 1)) == NULL)
        return -1;

    memcpy(active->first_avail, node->first_avail, cur_len);

    if (ps->got_a_new_node) {
        node->next = ps->free;
        ps->free = node; 
    }

    ps->node = active;
    ps->vbuff.curpos = active->first_avail + cur_len;
    ps->vbuff.endpos = active->endp - 1; /* Save a byte for NUL terminator */
    ps->got_a_new_node = 1;

    return 0;
}

APR_DECLARE(char *) apr_pvsprintf(apr_pool_t *pool, const char *fmt, va_list ap)
{
    struct psprintf_data ps;
    char *strp;
    apr_size_t size;
    node_t *active;

    ps.node = active = pool->active;
    ps.allocator = pool->allocator;
    ps.vbuff.curpos  = ps.node->first_avail;
    /* Save a byte for the NUL terminator */
    ps.vbuff.endpos = ps.node->endp - 1;
    ps.got_a_new_node = 0;
    ps.free = NULL;

    if (apr_vformatter(psprintf_flush, &ps.vbuff, fmt, ap) == -1) {
        if (pool->abort_fn)
            pool->abort_fn(APR_ENOMEM);

        return NULL;
    }

    strp = ps.vbuff.curpos;
    *strp++ = '\0';

    size = strp - ps.node->first_avail;
    size = ALIGN_DEFAULT(size);
    strp = ps.node->first_avail;
    ps.node->first_avail += size;

    /* 
     * Link the node in if it's a new one 
     */
    if (ps.got_a_new_node) {
        active->next = pool->active = ps.node;
    }

    if (ps.free)
        node_free(ps.allocator, ps.free);

    return strp;
}

APR_DECLARE_NONSTD(char *) apr_psprintf(apr_pool_t *p, const char *fmt, ...)
{
    va_list ap;
    char *res;

    va_start(ap, fmt);
    res = apr_pvsprintf(p, fmt, ap);
    va_end(ap);
    return res;
}

/*****************************************************************
 *
 * More grotty system stuff... subprocesses.  Frump.  These don't use
 * the generic cleanup interface because I don't want multiple
 * subprocesses to result in multiple three-second pauses; the
 * subprocesses have to be "freed" all at once.  If someone comes
 * along with another resource they want to allocate which has the
 * same property, we might want to fold support for that into the
 * generic interface, but for now, it's a special case
 */

APR_DECLARE(void) apr_pool_note_subprocess(apr_pool_t *pool, apr_proc_t *pid,
                                    enum kill_conditions how)
{
    struct process_chain *pc = apr_palloc(pool, sizeof(struct process_chain));

    pc->pid = pid;
    pc->kill_how = how;
    pc->next = pool->subprocesses;
    pool->subprocesses = pc;
}

static void free_proc_chain(struct process_chain *procs)
{
    /* Dispose of the subprocesses we've spawned off in the course of
     * whatever it was we're cleaning up now.  This may involve killing
     * some of them off...
     */
    struct process_chain *pc;
    int need_timeout = 0;

    if (!procs)
        return; /* No work.  Whew! */

    /* First, check to see if we need to do the SIGTERM, sleep, SIGKILL
     * dance with any of the processes we're cleaning up.  If we've got
     * any kill-on-sight subprocesses, ditch them now as well, so they
     * don't waste any more cycles doing whatever it is that they shouldn't
     * be doing anymore.
     */

#ifndef NEED_WAITPID
    /* Pick up all defunct processes */
    for (pc = procs; pc; pc = pc->next) {
        if (apr_proc_wait(pc->pid, NULL, NULL, APR_NOWAIT) != APR_CHILD_NOTDONE)
            pc->kill_how = kill_never;
    }
#endif

    for (pc = procs; pc; pc = pc->next) {
        if ((pc->kill_how == kill_after_timeout) ||
            (pc->kill_how == kill_only_once)) {
            /*
             * Subprocess may be dead already.  Only need the timeout if not.
             * Note: apr_proc_kill on Windows is TerminateProcess(), which is 
             * similar to a SIGKILL, so always give the process a timeout
             * under Windows before killing it.
             */
#ifdef WIN32
            need_timeout = 1;
#else
            if (apr_proc_kill(pc->pid, SIGTERM) == APR_SUCCESS)
                need_timeout = 1;
#endif
        }
        else if (pc->kill_how == kill_always) {
            apr_proc_kill(pc->pid, SIGKILL);
        }
    }

    /* Sleep only if we have to... */
    if (need_timeout)
        sleep(3);

    /* OK, the scripts we just timed out for have had a chance to clean up
     * --- now, just get rid of them, and also clean up the system accounting
     * goop...
     */
    for (pc = procs; pc; pc = pc->next) {
        if (pc->kill_how == kill_after_timeout)
            apr_proc_kill(pc->pid, SIGKILL);
    }

    /* Now wait for all the signaled processes to die */
    for (pc = procs; pc; pc = pc->next) {
        if (pc->kill_how != kill_never)
            (void)apr_proc_wait(pc->pid, NULL, NULL, APR_WAIT);
    }
#ifdef WIN32
    /* 
     * XXX: Do we need an APR function to clean-up a proc_t?
     * Well ... yeah ... but we can't since it's scope is ill defined.
     * We can't dismiss the handle until the apr_proc_wait above is
     * finished with the proc_t.
     */
    {
        for (p = procs; p; p = p->next) {
            if (p->pid->hproc) {
                CloseHandle(p->pid->hproc);
                p->pid->hproc = NULL;
            }
        }
    }

#endif /* WIN32 */
}


===================================================================
include/apr_pools.h
===================================================================

/* ====================================================================
 * The Apache Software License, Version 1.1
 *
 * Copyright (c) 2000-2001 The Apache Software Foundation.  All rights
 * reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *
 * 3. The end-user documentation included with the redistribution,
 *    if any, must include the following acknowledgment:
 *       "This product includes software developed by the
 *        Apache Software Foundation (http://www.apache.org/)."
 *    Alternately, this acknowledgment may appear in the software itself,
 *    if and wherever such third-party acknowledgments normally appear.
 *
 * 4. The names "Apache" and "Apache Software Foundation" must
 *    not be used to endorse or promote products derived from this
 *    software without prior written permission. For written
 *    permission, please contact apache@apache.org.
 *
 * 5. Products derived from this software may not be called "Apache",
 *    nor may "Apache" appear in their name, without prior written
 *    permission of the Apache Software Foundation.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 * ====================================================================
 *
 * This software consists of voluntary contributions made by many
 * individuals on behalf of the Apache Software Foundation.  For more
 * information on the Apache Software Foundation, please see
 * <http://www.apache.org/>.
 */

#ifndef APR_POOLS_H
#define APR_POOLS_H

#ifdef __cplusplus
extern "C" {
#endif

/**
 * @file apr_pools.h
 * @brief APR memory allocation
 *
 * Resource allocation routines...
 *
 * designed so that we don't have to keep track of EVERYTHING so that
 * it can be explicitly freed later (a fundamentally unsound strategy ---
 * particularly in the presence of die()).
 *
 * Instead, we maintain pools, and allocate items (both memory and I/O
 * handlers) from the pools --- currently there are two, one for per
 * transaction info, and one for config info.  When a transaction is over,
 * we can delete everything in the per-transaction apr_pool_t without fear, 
 * and without thinking too hard about it either.
 */
/** 
 * @defgroup APR_Pool Pool Allocation Functions
 * @ingroup APR
 * @{ 
 */
#include "apr.h"
#include "apr_errno.h"
#define APR_WANT_MEMFUNC
#include "apr_want.h"

/* Memory allocation/Pool debugging options... 
 *
 * Look in the developer documentation for details of what these do.
 *
 * NB These should ALL normally be commented out unless you REALLY
 * need them!!
 */
 
/*
#define APR_POOL_DEBUG
*/

/** The fundamental pool type */
typedef struct apr_pool_t apr_pool_t;

/** A function that is called when allocation fails. */
typedef int (*apr_abortfunc_t)(int retcode);

/**
 * @defgroup PoolDebug Pool Debugging functions.
 *
 * pools have nested lifetimes -- sub_pools are destroyed when the
 * parent pool is cleared.  We allow certain liberties with operations
 * on things such as tables (and on other structures in a more general
 * sense) where we allow the caller to insert values into a table which
 * were not allocated from the table's pool.  The table's data will
 * remain valid as long as all the pools from which its values are
 * allocated remain valid.
 *
 * For example, if B is a sub pool of A, and you build a table T in
 * pool B, then it's safe to insert data allocated in A or B into T
 * (because B lives at most as long as A does, and T is destroyed when
 * B is cleared/destroyed).  On the other hand, if S is a table in
 * pool A, it is safe to insert data allocated in A into S, but it
 * is *not safe* to insert data allocated from B into S... because
 * B can be cleared/destroyed before A is (which would leave dangling
 * pointers in T's data structures).
 *
 * In general we say that it is safe to insert data into a table T
 * if the data is allocated in any ancestor of T's pool.  This is the
 * basis on which the APR_POOL_DEBUG code works -- it tests these ancestor
 * relationships for all data inserted into tables.  APR_POOL_DEBUG also
 * provides tools (apr_find_pool, and apr_pool_is_ancestor) for other
 * folks to implement similar restrictions for their own data
 * structures.
 *
 * However, sometimes this ancestor requirement is inconvenient --
 * sometimes we're forced to create a sub pool (such as through
 * apr_sub_req_lookup_uri), and the sub pool is guaranteed to have
 * the same lifetime as the parent pool.  This is a guarantee implemented
 * by the *caller*, not by the pool code.  That is, the caller guarantees
 * they won't destroy the sub pool individually prior to destroying the
 * parent pool.
 *
 * In this case the caller must call apr_pool_join() to indicate this
 * guarantee to the APR_POOL_DEBUG code.  There are a few examples spread
 * through the standard modules.
 *
 * These functions are only implemented when #APR_POOL_DEBUG is set.
 *
 * @{
 */
#if defined(APR_POOL_DEBUG) || defined(DOXYGEN)
/**
 * Guarantee that a subpool has the same lifetime as the parent.
 * @param p The parent pool
 * @param sub The subpool
 */
APR_DECLARE(void) apr_pool_join(apr_pool_t *p, apr_pool_t *sub);

/**
 * Find a pool from something allocated in it.
 * @param ts The thing allocated in the pool
 * @return The pool it is allocated in
 */
APR_DECLARE(apr_pool_t *) apr_find_pool(const void *ts);

/**
 * Report the number of bytes currently in the pool
 * @param p The pool to inspect
 * @param recurse Recurse/include the subpools' sizes
 * @return The number of bytes
 */
APR_DECLARE(apr_size_t) apr_pool_num_bytes(apr_pool_t *p, int recurse);

/**
 * Report the number of bytes currently in the list of free blocks
 * @return The number of bytes
 */
APR_DECLARE(apr_size_t) apr_pool_free_blocks_num_bytes(void);

/**
 * Tag a pool (give it a name)
 * @param pool The pool to tag
 * @param tag  The tag
 */
APR_DECLARE(void) apr_pool_tag(apr_pool_t *pool, const char *tag);

/* @} */

#else
#    ifdef apr_pool_join
#        undef apr_pool_join
#    endif
#    define apr_pool_join(a,b)

#    ifdef apr_pool_tag
#        undef apr_pool_tag
#    endif
#    define apr_pool_tag(pool, tag)
#endif

/**
 * Determine if pool a is an ancestor of pool b
 * @param a The pool to search 
 * @param b The pool to search for
 * @return True if a is an ancestor of b, NULL is considered an ancestor
 *         of all pools.
 */
APR_DECLARE(int) apr_pool_is_ancestor(apr_pool_t *a, apr_pool_t *b);


/*
 * APR memory structure manipulators (pools, tables, and arrays).
 */

/**
 * Setup all of the internal structures required to use pools
 * @remark Programs do NOT need to call this directly.  APR will call this
 *      automatically from apr_initialize. 
 * @internal
 */
APR_DECLARE(apr_status_t) apr_pool_initialize(void);

/**
 * Tear down all of the internal structures required to use pools
 * @remark Programs do NOT need to call this directly.  APR will call this
 *      automatically from apr_terminate. 
 * @internal
 */
APR_DECLARE(void) apr_pool_terminate(void); 
 
/* pool functions */

#define APR_POOL_FDEFAULT       0x0
#define APR_POOL_FNEW_ALLOCATOR 0x1
#define APR_POOL_FLOCK          0x2

/**
 * Create a new pool.
 * @param newpool The pool we have just created.
 * @param parent The parent pool.  If this is NULL, the new pool is a root
 *        pool.  If it is non-NULL, the new pool will inherit all
 *        of its parent pool's attributes, except the apr_pool_t will 
 *        be a sub-pool.
 * @param apr_abort A function to use if the pool cannot allocate more memory.
 * @param flags Flags indicating how the pool should be created:
 *        - POOL_FNEW_ALLOCATOR  will create a new allocator for the pool
 *          instead of using the allocator of the parent.
 *        - POOL_FLOCK will create a mutex for the newly created allocator
 *          (this flag only makes sense in combination with POOL_FNEW_ALLOCATOR)
 *
 */
APR_DECLARE(apr_status_t) apr_pool_create_ex(apr_pool_t **newpool,
                                             apr_pool_t *parent,
                                             apr_abortfunc_t abort_fn,
                                             apr_uint32_t flags);

/**
 * Create a new pool.
 * @param newpool The pool we have just created.
 * @param parent The parent pool.  If this is NULL, the new pool is a root
 *        pool.  If it is non-NULL, the new pool will inherit all
 *        of its parent pool's attributes, except the apr_pool_t will 
 *        be a sub-pool.
 */
#if defined(DOXYGEN)
APR_DECLARE(apr_status_t) apr_pool_create(apr_pool_t **newpool,
                                          apr_pool_t *parent);
#else
#define apr_pool_create(newpool, parent) \
    apr_pool_create_ex(newpool, parent, NULL, APR_POOL_FDEFAULT)
#endif

/**
 * @param newpool The new sub-pool
 * @param parent The pool to use as a parent pool
 * @param apr_abort A function to use if the pool cannot allocate more memory.
 * @deffunc void apr_pool_sub_make(apr_pool_t **p, apr_pool_t *parent, int (*apr_abort)(int retcode), const char *created)
 * @remark The @a apr_abort function provides a way to quit the program if the
 *      machine is out of memory.  By default, APR will return on error.
 */
#if defined(DOXYGEN)
APR_DECLARE(void) apr_pool_sub_make(apr_pool_t **newpool, 
                                    apr_pool_t *parent,
                                    int (*apr_abort)(int retcode));
#else
#define apr_pool_sub_make(newpool, parent, abort_fn) \
    (void)apr_pool_create_ex(newpool, parent, abort_fn, APR_POOL_FDEFAULT);
#endif

/**
 * Allocate a block of memory from a pool
 * @param c The pool to allocate from 
 * @param reqsize The amount of memory to allocate 
 * @return The allocated memory
 */
APR_DECLARE(void *) apr_palloc(apr_pool_t *c, apr_size_t reqsize);

/**
 * Allocate a block of memory from a pool and set all of the memory to 0
 * @param p The pool to allocate from 
 * @param size The amount of memory to allocate 
 * @return The allocated memory
 */
APR_DECLARE(void *) apr_pcalloc(apr_pool_t *p, apr_size_t size);

/**
 * Clear all memory in the pool and run all the cleanups. This also clears all
 * subpools.
 * @param p The pool to clear
 * @remark  This does not actually free the memory, it just allows the pool
 *       to re-use this memory for the next allocation.
 * @see apr_pool_destroy()
 */
APR_DECLARE(void) apr_pool_clear(apr_pool_t *p);

/**
 * Destroy the pool. This runs apr_pool_clear() and then frees all the memory.
 * @param p The pool to destroy
 * @remark This will actually free the memory
 */
APR_DECLARE(void) apr_pool_destroy(apr_pool_t *p);

/**
 * Set the function to be called when an allocation failure occurs.
 * @tip If the program wants APR to exit on a memory allocation error,
 *      then this function can be called to set the callback to use (for
 *      performing cleanup and then exiting). If this function is not called,
 *      then APR will return an error and expect the calling program to
 *      deal with the error accordingly.
 * @deffunc apr_status_t apr_pool_set_abort(apr_abortfunc_t abortfunc, apr_pool_t *pool)
 */
APR_DECLARE(void) apr_pool_set_abort(apr_abortfunc_t abortfunc,
                                     apr_pool_t *pool);

/**
 * Get the abort function associated with the specified pool.
 * @param pool The pool for retrieving the abort function.
 * @return The abort function for the given pool.
 * @deffunc apr_abortfunc_t apr_pool_get_abort(apr_pool_t *pool)
 */
APR_DECLARE(apr_abortfunc_t) apr_pool_get_abort(apr_pool_t *pool);

/**
 * Get the parent pool of the specified pool.
 * @param pool The pool for retrieving the parent pool.
 * @return The parent of the given pool.
 * @deffunc apr_pool_t * apr_pool_get_parent(apr_pool_t *pool)
 */
APR_DECLARE(apr_pool_t *) apr_pool_get_parent(apr_pool_t *pool);

/**
 * Set the data associated with the current pool
 * @param data The user data associated with the pool.
 * @param key The key to use for association
 * @param cleanup The cleanup program to use to cleanup the data (NULL if none)
 * @param pool The current pool
 * @warning The data to be attached to the pool should have a life span
 *          at least as long as the pool it is being attached to.
 *
 *      Users of APR must take EXTREME care when choosing a key to
 *      use for their data.  It is possible to accidentally overwrite
 *      data by choosing a key that another part of the program is using
 *      It is advised that steps are taken to ensure that a unique
 *      key is used at all times.
 * @bug Specify how to ensure this uniqueness!
 */
APR_DECLARE(apr_status_t) apr_pool_userdata_set(const void *data,
                                                const char *key,
                                                apr_status_t (*cleanup)(void *),
                                                apr_pool_t *pool);

/**
 * Set the data associated with the current pool
 * @param data The user data associated with the pool.
 * @param key The key to use for association
 * @param cleanup The cleanup program to use to cleanup the data (NULL if none)
 * @param pool The current pool
 * @note same as apr_pool_userdata_set(), except that this version doesn't
 *       make a copy of the key (this function is useful, for example, when
 *       the key is a string literal)
 * @warning The key and the data to be attached to the pool should have
 *       a life span at least as long as the pool itself.
 *
 */
APR_DECLARE(apr_status_t) apr_pool_userdata_setn(const void *data,
                                                 const char *key,
                                                 apr_status_t (*cleanup)(void *),
                                                 apr_pool_t *pool);

/**
 * Return the data associated with the current pool.
 * @param data The user data associated with the pool.
 * @param key The key for the data to retrieve
 * @param pool The current pool.
 */
APR_DECLARE(apr_status_t) apr_pool_userdata_get(void **data, const char *key,
                                           apr_pool_t *pool);

/**
 * Register a function to be called when a pool is cleared or destroyed
 * @param p The pool register the cleanup with 
 * @param data The data to pass to the cleanup function.
 * @param plain_cleanup The function to call when the pool is cleared 
 *                      or destroyed
 * @param child_cleanup The function to call when a child process is created -
 *                      this function is called in the child, obviously!
 */
APR_DECLARE(void) apr_pool_cleanup_register(apr_pool_t *p, const void *data,
                                       apr_status_t (*plain_cleanup)(void *),
                                       apr_status_t (*child_cleanup)(void *));

/**
 * Remove a previously registered cleanup function
 * @param p The pool remove the cleanup from 
 * @param data The data to remove from cleanup
 * @param cleanup The function to remove from cleanup
 * @remarks For some strange reason only the plain_cleanup is handled by this
 *          function
 */
APR_DECLARE(void) apr_pool_cleanup_kill(apr_pool_t *p, const void *data,
                                   apr_status_t (*cleanup)(void *));

/**
 * Replace the child cleanup of a previously registered cleanup
 * @param p The pool of the registered cleanup
 * @param data The data of the registered cleanup
 * @param plain_cleanup The plain cleanup function of the registered cleanup
 * @param child_cleanup The function to register as the child cleanup
 */
APR_DECLARE(void) apr_pool_child_cleanup_set(apr_pool_t *p, const void *data,
                                      apr_status_t (*plain_cleanup)(void *),
                                      apr_status_t (*child_cleanup)(void *));

/**
 * Run the specified cleanup function immediately and unregister it. Use
 * @a data instead of the data that was registered with the cleanup.
 * @param p The pool remove the cleanup from 
 * @param data The data to remove from cleanup
 * @param cleanup The function to remove from cleanup
 */
APR_DECLARE(apr_status_t) apr_pool_cleanup_run(apr_pool_t *p, void *data,
                                          apr_status_t (*cleanup)(void *));

/**
 * An empty cleanup function 
 * @param data The data to cleanup
 */
APR_DECLARE_NONSTD(apr_status_t) apr_pool_cleanup_null(void *data);

/* Preparing for exec() --- close files, etc., but *don't* flush I/O
 * buffers, *don't* wait for subprocesses, and *don't* free any memory.
 */
/**
 * Run all of the child_cleanups, so that any unnecessary files are 
 * closed because we are about to exec a new program
 */
APR_DECLARE(void) apr_pool_cleanup_for_exec(void);

/*
 * Pool accessor functions.
 *
 * These standardized function are used by opaque (APR) data types to return
 * the apr_pool_t that is associated with the data type.
 *
 * APR_POOL_DECLARE_ACCESSOR() is used in a header file to declare the
 * accessor function. A typical usage and result would be:
 *
 *    APR_POOL_DECLARE_ACCESSOR(file);
 * becomes:
 *    APR_DECLARE(apr_pool_t *) apr_file_pool_get(apr_file_t *ob);
 *
 * In the implementation, the APR_POOL_IMPLEMENT_ACCESSOR() is used to
 * actually define the function. It assumes the field is named "pool". For
 * data types with a different field name (e.g. "cont" or "cntxt") the
 * APR_POOL_IMPLEMENT_ACCESSOR_X() macro should be used.
 *
 * Note: the linkage is specified for APR. It would be possible to expand
 *       the macros to support other linkages.
 */
#define APR_POOL_DECLARE_ACCESSOR(typename) \
    APR_DECLARE(apr_pool_t *) apr_##typename##_pool_get \
        (const apr_##typename##_t *ob)

#define APR_POOL_IMPLEMENT_ACCESSOR(typename) \
    APR_POOL_IMPLEMENT_ACCESSOR_X(typename, pool)
#define APR_POOL_IMPLEMENT_ACCESSOR_X(typename, fieldname) \
    APR_DECLARE(apr_pool_t *) apr_##typename##_pool_get \
        (const apr_##typename##_t *ob) { return ob->fieldname; }

/** @} */
#ifdef __cplusplus
}
#endif

#endif /* !APR_POOLS_H */


Mime
View raw message