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 [EMAIL PROTECTED] * * 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 [EMAIL PROTECTED] * * 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 */