apr_pools.c

来自「Apache 2.0.63 is the current stable vers」· C语言 代码 · 共 2,297 行 · 第 1/5 页

C
2,297
字号
    apr_memnode_t *node, *active;
    apr_size_t cur_len, size;
    char *strp;
    apr_pool_t *pool;
    apr_uint32_t free_index;

    pool = ps->pool;
    active = ps->node;
    strp = ps->vbuff.curpos;
    cur_len = strp - active->first_avail;
    size = cur_len << 1;

    /* Make sure that we don't try to use a block that has less
     * than APR_PSPRINTF_MIN_STRINGSIZE bytes left in it.  This
     * also catches the case where size == 0, which would result
     * in reusing a block that can't even hold the NUL byte.
     */
    if (size < APR_PSPRINTF_MIN_STRINGSIZE)
        size = APR_PSPRINTF_MIN_STRINGSIZE;

    node = active->next;
    if (!ps->got_a_new_node
        && size < (apr_size_t)(node->endp - node->first_avail)) {

        list_remove(node);
        list_insert(node, active);

        node->free_index = 0;

        pool->active = node;

        free_index = (APR_ALIGN(active->endp - active->first_avail + 1,
                                BOUNDARY_SIZE) - BOUNDARY_SIZE) >> BOUNDARY_INDEX;

        active->free_index = free_index;
        node = active->next;
        if (free_index < node->free_index) {
            do {
                node = node->next;
            }
            while (free_index < node->free_index);

            list_remove(active);
            list_insert(active, node);
        }

        node = pool->active;
    }
    else {
        if ((node = allocator_alloc(pool->allocator, size)) == NULL)
            return -1;

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

        ps->got_a_new_node = 1;
    }

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

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

    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;
    apr_memnode_t *active, *node;
    apr_uint32_t free_index;

    ps.node = active = pool->active;
    ps.pool = pool;
    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;

    /* Make sure that the first node passed to apr_vformatter has at least
     * room to hold the NUL terminator.
     */
    if (ps.node->first_avail == ps.node->endp) {
        if (psprintf_flush(&ps.vbuff) == -1) {
            if (pool->abort_fn) {
                pool->abort_fn(APR_ENOMEM);
            }

            return 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 = APR_ALIGN_DEFAULT(size);
    strp = ps.node->first_avail;
    ps.node->first_avail += size;

    if (ps.free)
        allocator_free(pool->allocator, ps.free);

    /*
     * Link the node in if it's a new one
     */
    if (!ps.got_a_new_node)
        return strp;

    active = pool->active;
    node = ps.node;

    node->free_index = 0;

    list_insert(node, active);

    pool->active = node;

    free_index = (APR_ALIGN(active->endp - active->first_avail + 1,
                            BOUNDARY_SIZE) - BOUNDARY_SIZE) >> BOUNDARY_INDEX;

    active->free_index = free_index;
    node = active->next;

    if (free_index >= node->free_index)
        return strp;

    do {
        node = node->next;
    }
    while (free_index < node->free_index);

    list_remove(active);
    list_insert(active, node);

    return strp;
}


#else /* APR_POOL_DEBUG */
/*
 * Debug helper functions
 */


/*
 * Walk the pool tree rooted at pool, depth first.  When fn returns
 * anything other than 0, abort the traversal and return the value
 * returned by fn.
 */
static int apr_pool_walk_tree(apr_pool_t *pool,
                              int (*fn)(apr_pool_t *pool, void *data),
                              void *data)
{
    int rv;
    apr_pool_t *child;

    rv = fn(pool, data);
    if (rv)
        return rv;

#if APR_HAS_THREADS
    if (pool->mutex) {
        apr_thread_mutex_lock(pool->mutex);
                        }
#endif /* APR_HAS_THREADS */

    child = pool->child;
    while (child) {
        rv = apr_pool_walk_tree(child, fn, data);
        if (rv)
            break;

        child = child->sibling;
    }

#if APR_HAS_THREADS
    if (pool->mutex) {
        apr_thread_mutex_unlock(pool->mutex);
    }
#endif /* APR_HAS_THREADS */

    return rv;
}

#if (APR_POOL_DEBUG & APR_POOL_DEBUG_VERBOSE_ALL)
static void apr_pool_log_event(apr_pool_t *pool, const char *event,
                               const char *file_line, int deref)
{
    if (file_stderr) {
        if (deref) {
            apr_file_printf(file_stderr,
                "POOL DEBUG: "
                "[%lu"
#if APR_HAS_THREADS
                "/%lu"
#endif /* APR_HAS_THREADS */
                "] "
                "%7s "
                "(%10lu/%10lu/%10lu) "
                "0x%08X \"%s\" "
                "<%s> "
                "(%u/%u/%u) "
                "\n",
                (unsigned long)getpid(),
#if APR_HAS_THREADS
                (unsigned long)apr_os_thread_current(),
#endif /* APR_HAS_THREADS */
                event,
                (unsigned long)apr_pool_num_bytes(pool, 0),
                (unsigned long)apr_pool_num_bytes(pool, 1),
                (unsigned long)apr_pool_num_bytes(global_pool, 1),
                (unsigned int)pool, pool->tag,
                file_line,
                pool->stat_alloc, pool->stat_total_alloc, pool->stat_clear);
        }
        else {
            apr_file_printf(file_stderr,
                "POOL DEBUG: "
                "[%lu"
#if APR_HAS_THREADS
                "/%lu"
#endif /* APR_HAS_THREADS */
                "] "
                "%7s "
                "                                   "
                "0x%08X "
                "<%s> "
                "\n",
                (unsigned long)getpid(),
#if APR_HAS_THREADS
                (unsigned long)apr_os_thread_current(),
#endif /* APR_HAS_THREADS */
                event,
                (unsigned int)pool,
                file_line);
        }
    }
}
#endif /* (APR_POOL_DEBUG & APR_POOL_DEBUG_VERBOSE_ALL) */

#if (APR_POOL_DEBUG & APR_POOL_DEBUG_LIFETIME)
static int pool_is_child_of(apr_pool_t *parent, void *data)
{
    apr_pool_t *pool = (apr_pool_t *)data;

    return (pool == parent);
}

static int apr_pool_is_child_of(apr_pool_t *pool, apr_pool_t *parent)
{
    if (parent == NULL)
        return 0;

    return apr_pool_walk_tree(parent, pool_is_child_of, pool);
}
#endif /* (APR_POOL_DEBUG & APR_POOL_DEBUG_LIFETIME) */

static void apr_pool_check_integrity(apr_pool_t *pool)
{
    /* Rule of thumb: use of the global pool is always
     * ok, since the only user is apr_pools.c.  Unless
     * people have searched for the top level parent and
     * started to use that...
     */
    if (pool == global_pool || global_pool == NULL)
        return;

    /* Lifetime
     * This basically checks to see if the pool being used is still
     * a relative to the global pool.  If not it was previously
     * destroyed, in which case we abort().
     */
#if (APR_POOL_DEBUG & APR_POOL_DEBUG_LIFETIME)
    if (!apr_pool_is_child_of(pool, global_pool)) {
#if (APR_POOL_DEBUG & APR_POOL_DEBUG_VERBOSE_ALL)
        apr_pool_log_event(pool, "LIFE",
                           __FILE__ ":apr_pool_integrity check", 0);
#endif /* (APR_POOL_DEBUG & APR_POOL_DEBUG_VERBOSE_ALL) */
        abort();
    }
#endif /* (APR_POOL_DEBUG & APR_POOL_DEBUG_LIFETIME) */

#if (APR_POOL_DEBUG & APR_POOL_DEBUG_OWNER)
#if APR_HAS_THREADS
    if (!apr_os_thread_equal(pool->owner, apr_os_thread_current())) {
#if (APR_POOL_DEBUG & APR_POOL_DEBUG_VERBOSE_ALL)
        apr_pool_log_event(pool, "THREAD",
                           __FILE__ ":apr_pool_integrity check", 0);
#endif /* (APR_POOL_DEBUG & APR_POOL_DEBUG_VERBOSE_ALL) */
        abort();
    }
#endif /* APR_HAS_THREADS */
#endif /* (APR_POOL_DEBUG & APR_POOL_DEBUG_OWNER) */
}


/*
 * Initialization (debug)
 */

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

    if (apr_pools_initialized++)
        return APR_SUCCESS;

    /* Since the debug code works a bit differently then the
     * regular pools code, we ask for a lock here.  The regular
     * pools code has got this lock embedded in the global
     * allocator, a concept unknown to debug mode.
     */
    if ((rv = apr_pool_create_ex(&global_pool, NULL, NULL,
                                 NULL)) != APR_SUCCESS) {
        return rv;
    }

    apr_pool_tag(global_pool, "APR global pool");

    apr_pools_initialized = 1;

    /* This has to happen here because mutexes might be backed by
     * atomics.  It used to be snug and safe in apr_initialize().
     */
    if ((rv = apr_atomic_init(global_pool)) != APR_SUCCESS) {
        return rv;
    }

#if (APR_POOL_DEBUG & APR_POOL_DEBUG_VERBOSE_ALL)
    apr_file_open_stderr(&file_stderr, global_pool);
    if (file_stderr) {
        apr_file_printf(file_stderr,
            "POOL DEBUG: [PID"
#if APR_HAS_THREADS
            "/TID"
#endif /* APR_HAS_THREADS */
            "] ACTION  (SIZE      /POOL SIZE /TOTAL SIZE) "
            "POOL       \"TAG\" <__FILE__:__LINE__> (ALLOCS/TOTAL ALLOCS/CLEARS)\n");

        apr_pool_log_event(global_pool, "GLOBAL", __FILE__ ":apr_pool_initialize", 0);
    }
#endif /* (APR_POOL_DEBUG & APR_POOL_DEBUG_VERBOSE_ALL) */

    return APR_SUCCESS;
}

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

    apr_pools_initialized = 0;

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

#if (APR_POOL_DEBUG & APR_POOL_DEBUG_VERBOSE_ALL)
    file_stderr = NULL;
#endif /* (APR_POOL_DEBUG & APR_POOL_DEBUG_VERBOSE_ALL) */
}


/*
 * Memory allocation (debug)
 */

static void *pool_alloc(apr_pool_t *pool, apr_size_t size)
{
    debug_node_t *node;
    void *mem;

    if ((mem = malloc(size)) == NULL) {
        if (pool->abort_fn)
            pool->abort_fn(APR_ENOMEM);

        return NULL;
    }

    node = pool->nodes;
    if (node == NULL || node->index == 64) {
        if ((node = malloc(SIZEOF_DEBUG_NODE_T)) == NULL) {
            if (pool->abort_fn)
                pool->abort_fn(APR_ENOMEM);

            return NULL;
        }

        memset(node, 0, SIZEOF_DEBUG_NODE_T);

        node->next = pool->nodes;
        pool->nodes = node;
        node->index = 0;
    }

    node->beginp[node->index] = mem;
    node->endp[node->index] = (char *)mem + size;
    node->index++;

    pool->stat_alloc++;
    pool->stat_total_alloc++;

    return mem;
}

APR_DECLARE(void *) apr_palloc_debug(apr_pool_t *pool, apr_size_t size,
                                     const char *file_line)
{
    void *mem;

    apr_pool_check_integrity(pool);

    mem = pool_alloc(pool, size);

#if (APR_POOL_DEBUG & APR_POOL_DEBUG_VERBOSE_ALLOC)
    apr_pool_log_event(pool, "PALLOC", file_line, 1);
#endif /* (APR_POOL_DEBUG & APR_POOL_DEBUG_VERBOSE_ALLOC) */

    return mem;
}

APR_DECLARE(void *) apr_pcalloc_debug(apr_pool_t *pool, apr_size_t size,
                                      const char *file_line)
{
    void *mem;

    apr_pool_check_integrity(pool);

    mem = pool_alloc(pool, size);
    memset(mem, 0, size);

#if (APR_POOL_DEBUG & APR_POOL_DEBUG_VERBOSE_ALLOC)
    apr_pool_log_event(pool, "PCALLOC", file_line, 1);
#endif /* (APR_POOL_DEBUG & APR_POOL_DEBUG_VERBOSE_ALLOC) */

    return mem;
}


/*
 * Pool creation/destruction (debug)
 */

#define POOL_POISON_BYTE 'A'

static void pool_clear_debug(apr_pool_t *pool, const char *file_line)

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?