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