APR_POOL 帮助文档
Directory tree
1. Brief introduction of apr_pool--------------------------------
2. There are three basic APIs as follows:----------------------
APR_DECLARE(apr_status_t) apr_pool_create(apr_pool_t **newpool,
apr_pool_t *parent);
APR_DECLARE(void *) apr_palloc(apr_pool_t *p, apr_size_t size);
APR_DECLARE(void) apr_pool_destroy(apr_pool_t *p);
i.
3. Give a demonstration----------------------------------------------
4. The detail-----------------------------------------------------------
( Apr_pool_clearn and apr_pool_destroy------------------------- )
6. Delegate or hook function-------------------------------------
7. Apr_pool_cleanup_register and apr_pool_cleanup_kill-
8. Sub pool-------------------------------------------------------------
Brief introduction of apr_pool
Most of libapr APIs are dependent on memory pool. By memory pool, you can easily manage a set of memory chunks. Imagine the case without memory pool system, where you allocate several memory chunks. You have to free each of them. If you have ten memory chunks, you have to free ten times, otherwise you would suffer from memory leak bugs. Memory pool solves this issue. After you allocate one memory pool, you can allocate multiple memory chunks from the pool. To free them, all you have to do is to destroy the memory pool. By which, you can free all the memory chunks. There are two good points. First, as stated above, it is defensive against memory leak bugs. Second, allocation costs of memory chunks become relatively lower. In a sense, memory pool forces you to obey a session-oriented programming. A memory pool is a kind of a session context, that is, a set of objects that have the same lifetimes. You can control a set of objects within a session context. At the beginning of a session you create a memory pool. Then, you create objects in the memory pool during the session. Note that you don't need to care about their lifetimes. Finally, at the end of the session all you have to do is to destroy the memory pool.
REMARK: In general, objects lifetime control is the most difficult part in programming. Thus, there are many other techniques for it, such as smart pointer, GC(garbage collection) and so on. Note that it is a bit hard to use such techniques at the same time. Since memory pool is one of such techniques, you have to be careful about the mixture.
There are three basic APIs as follows:
/* excerpted from apr_pools.h */
APR_DECLARE(apr_status_t) apr_pool_create(apr_pool_t **newpool,
apr_pool_t *parent);
APR_DECLARE(void *) apr_palloc(apr_pool_t *p, apr_size_t size);
APR_DECLARE(void) apr_pool_destroy(apr_pool_t *p);
Give a demonstration
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <apr_general.h>
#include <apr_pools.h>
#define MEM_ALLOC_SIZE 1024
/**
* memory pool sample code
* @remark Error checks omitted
*/
int main(int argc, const char *argv[])
{
apr_status_t rv;
apr_pool_t *mp;
char *buf1;
char *buf2;
/* per-process initialization */
rv = apr_initialize();
if (rv != APR_SUCCESS) {
assert(0);
return -1;
}
/* create a memory pool. */
apr_pool_create(&mp, NULL);
/* allocate memory chunks from the memory pool */
buf1 = apr_palloc(mp, MEM_ALLOC_SIZE);
buf2 = apr_palloc(mp, MEM_ALLOC_SIZE);
/* destroy the memory pool. These chunks above are freed by this */
apr_pool_destroy(mp);
apr_terminate();
return 0;
}
The detail
/**
* 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.
*/
APR_DECLARE(apr_status_t) apr_pool_create(apr_pool_t **newpool,
apr_pool_t *parent);
/*
* Memory allocation
*/
/**
* Allocate a block of memory from a pool
* @param p The pool to allocate from
* @param size The amount of memory to allocate
* @return The allocated memory
*/
APR_DECLARE(void *) apr_palloc(apr_pool_t *p, apr_size_t size);
/**
* Destroy the pool. This takes similar action as 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);
Remark
We create a memory pool by apr_pool_create(). The memory pool is alive until you call apr_pool_destroy(). The first argument of apr_pool_create() is a result argument. A newly created memory pool object, apr_pool_t, is returned by this API call. We call apr_palloc() to get a memory chunk by specifing the chunk's size. someone think it should have a function to free the memory chunk.
But,in fact, the APIs do not provide such function.That is needless. The memory chunkes that you allo-
cated through apr_palloc() will be destroyed by apr_pool_destroy(). Overview,the only way to destroy
memory chunkes is destroy the memory pool by calling the apr_pool_destroy.
We should take notice of the rv = apr_initialize() and apr_terminate(). The apr_initialize() is setup any APR internal data structures. This must be the first function called for any APR library.See apr_app_initalize() if this is an application , rather than a library consumer of apr.You must be close apr with a call to apr_terminate at the end of program execution.
The contrast apr_palloc/apr_pcalloc between malloc/calloc
As you can guess, we can use apr_palloc() like malloc().The different between them is that the apr_palloc() is allocate and manage memory chunk by the memory pool,but the malloc by the system.As you know, we can also use calloc().In the APIs of apr,we can use apr_pcalloc() like calloc(),apr_pcalloc() returns a zero-cleard memory chunk.If you use malloc/calloc, you need to call free() for the allocated memories.In contrast,you don’t need to free each memory chunks in memory pool.You just call apr_pool_destory() for the memory pool and it frees all the memory chunks.
When we use apr_palloc() to allocate memory chunk,there is no limitation about memory chunk si-
ze. But it isn’t a good idea to allocate large size memory chunk in memory pool. That is because memo
ry pool is essentially designed for smaller chunks.Actually, the initial size of memory pool is 8 kilo byt
es.If you need a large size memory chunk,you should not use memory pool,but you should use malloc /
calloc.
Apr_pool_clearn and apr_pool_destroy
apr_pool_clear() is similar to apr_pool_destroy(), but the memory pool is still reusable. A typical code is as follows:
/* sample code about apr_pool_clear() */
apr_pool_t *mp;
apr_pool_create(&mp, NULL);
for (i = 0; i < n; ++i) {
do_operation(..., mp);
apr_pool_clear(mp);
}
apr_pool_destroy(mp);
The memory pool is used in do_operation(), that is, several memory chunks are allocated. If you don't need the memory chunks out of do_operation(), you can call apr_pool_clear(). You are able to reduce the amount of memory usage. If you are familiar with local stack memory system, you can think of memory pool as local stack memory. Calling apr_palloc() is similar to moving SP(stack pointer), and calling apr_pool_clear() is similar to rewinding SP. Both are very light operations.
Delegate or hook function
Apr_pool_cleanup_register and arp_pool_cleanup_kill
By apr_pool_cleanup_register(), we can have hook functions on memory pool clear/destroy. You have a callback function that is called whenever the memory pool is cleared or destroyed. In the callback functions, you can implement any finalization code depending on the memory pool. A typical code is as follows:
….
Int *value = (int *)malloc(sizeof(int));
Apr_pool_cleanup_register(my_pool,value,free,apr_pool_cleanup_null);
…
Using the way,we need not take care of whether the memory chunk is free.
when apr_pool_destory(my_pool) is called,the value is automatic freed.
someone want to obvious free the vaule again. You can kill the delegate with the apr_pool_cleanup_kill(my_pool,value,free).as follow:
…
Int *value = (int *)malloc(sizeof(int));
Apr_pool_cleanup_register(my_pool,value,free,apr_pool_cleanup_null);
Free(value);
apr_pool_cleanup_kill(my_pool,value,free);
…
/**
* 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 about
* to exec - 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 *));
/**
* Register a function to be called when a pool is cleared or destroyed.
*
* Unlike apr_pool_cleanup_register which register a cleanup
* that is called AFTER all subpools are destroyed this function register
* a function that will be called before any of the subpool is destoryed.
*
* @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
*/
APR_DECLARE(void) apr_pool_pre_cleanup_register(
apr_pool_t *p,
const void *data,
apr_status_t (*plain_cleanup)(void *));
/**
* Remove a previously registered cleanup function.
*
* The cleanup most recently registered with @a p having the same values of
* @a data and @a cleanup will be removed.
*
* @param p The pool to remove the cleanup from
* @param data The data of the registered 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 function of a previously registered cleanup.
*
* The cleanup most recently registered with @a p having the same values of
* @a data and @a plain_cleanup will have the registered child cleanup
* function replaced with @a child_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 *));
/**
* An empty cleanup function.
*
* Passed to apr_pool_cleanup_register() when no cleanup is required.
*
* @param data The data to cleanup, will not be used by this function.
*/
APR_DECLARE_NONSTD(apr_status_t) apr_pool_cleanup_null(void *data);
Sub pool
The last topic about memory pool is sub pool. Each memory pool is able to have a parent memory pool. Accordingly, memory pools construct trees. The second argument of apr_pool_create() is a parent memory pool. When you pass NULL as the parent memory pool, the newly created memory pool becomes a root memory pool. You can create sub memory pools under the root memory pool. Whene you call apr_pool_destroy() for a memory pool in the tree, the child memory pools are also destroyed. When you call apr_pool_clear() for the memory pool, the memory pool is alive but the child memory pools are destroyed. Whenever a child memory pool is destroyed, the cleanup functions for it mentioned above are called.