views:

54

answers:

1

More generally, does anyone know where the way APC works internally is documented?

A: 

The short answer is yes, it appears to free and reclaim memory. Below I have listed the main functions involved, descending further into the call stack as we go:

  1. apc_delete
  2. apc_cache_user_delete
  3. remove_slot
  4. free_slot
  5. apc_sma_free
  6. sma_deallocate

apc_delete

// taken from the file php_apc.c
// http://php-apc.sourcearchive.com/documentation/3.0.18/php__apc_8c-source.html

/* {{{ proto mixed apc_delete(string key)
 */
PHP_FUNCTION(apc_delete) {
    char *strkey;
    int strkey_len;

    if(!APCG(enabled)) RETURN_FALSE;

    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &strkey, &strkey_len) == FAILURE) {
        return;
    }

    if(!strkey_len) RETURN_FALSE;

    if(apc_cache_user_delete(apc_user_cache, strkey, strkey_len + 1)) {
        RETURN_TRUE;
    } else {
        RETURN_FALSE;
    }
}
/* }}} */

apc_cache_user_delete

// taken from the file apc_cache.c
// http://php-apc.sourcearchive.com/documentation/3.0.18/apc__cache_8c-source.html

/* {{{ apc_cache_user_delete */
int apc_cache_user_delete(apc_cache_t* cache, char *strkey, int keylen)
{
    slot_t** slot;

    LOCK(cache);

    slot = &cache->slots[string_nhash_8(strkey, keylen) % cache->num_slots];

    while (*slot) {
        if (!memcmp((*slot)->key.data.user.identifier, strkey, keylen)) {
            remove_slot(cache, slot);
            UNLOCK(cache);
            return 1;
        }
        slot = &(*slot)->next;
    }

    UNLOCK(cache);
    return 0;
}
/* }}} */

remove_slot

// taken from the file apc_cache.c
// http://php-apc.sourcearchive.com/documentation/3.0.18/apc__cache_8c-source.html

/* {{{ remove_slot */
static void remove_slot(apc_cache_t* cache, slot_t** slot)
{
    slot_t* dead = *slot;
    *slot = (*slot)->next;

    cache->header->mem_size -= dead->value->mem_size;
    cache->header->num_entries--;
    if (dead->value->ref_count <= 0) {
        free_slot(dead);
    }
    else {
        dead->next = cache->header->deleted_list;
        dead->deletion_time = time(0);
        cache->header->deleted_list = dead;
    }
}
/* }}} */

free_slot

// taken from the file apc_cache.c
// http://php-apc.sourcearchive.com/documentation/3.0.18/apc__cache_8c-source.html

/* {{{ free_slot */
static void free_slot(slot_t* slot)
{
    if(slot->value->type == APC_CACHE_ENTRY_USER) {
        apc_sma_free((char *)slot->key.data.user.identifier);
    } else if(slot->key.type == APC_CACHE_KEY_FPFILE) {
        apc_sma_free((char *)slot->key.data.fpfile.fullpath);
    }
    apc_cache_free_entry(slot->value);
    apc_sma_free(slot);
}
/* }}} */

apc_sma_free

// taken from the file apc_sma.c
// http://php-apc.sourcearchive.com/documentation/3.0.18/apc__sma_8c-source.html

/* {{{ apc_sma_free */
void apc_sma_free(void* p)
{
    int i;
    size_t offset;
    size_t d_size;
    TSRMLS_FETCH();

    if (p == NULL) {
        return;
    }

    assert(sma_initialized);

    for (i = 0; i < sma_numseg; i++) {
        LOCK(((header_t*)sma_shmaddrs[i])->sma_lock);
        offset = (size_t)((char *)p - (char *)(sma_shmaddrs[i]));
        if (p >= sma_shmaddrs[i] && offset < sma_segsize) {
            d_size = sma_deallocate(sma_shmaddrs[i], offset);
            if (APCG(mem_size_ptr) != NULL) { *(APCG(mem_size_ptr)) -= d_size; }
            UNLOCK(((header_t*)sma_shmaddrs[i])->sma_lock);
#ifdef VALGRIND_FREELIKE_BLOCK
            VALGRIND_FREELIKE_BLOCK(p, 0);
#endif
            return;
        }
        UNLOCK(((header_t*)sma_shmaddrs[i])->sma_lock);
    }

    apc_eprint("apc_sma_free: could not locate address %p", p);
}
/* }}} */

sma_deallocate

// taken from the file apc_sma.c
// http://php-apc.sourcearchive.com/documentation/3.0.18/apc__sma_8c-source.html

/* {{{ sma_deallocate: deallocates the block at the given offset */
static size_t sma_deallocate(void* shmaddr, size_t offset)
{
    header_t* header;   /* header of shared memory segment */
    block_t* cur;       /* the new block to insert */
    block_t* prv;       /* the block before cur */
    block_t* nxt;       /* the block after cur */
    size_t size;        /* size of deallocated block */

    offset -= ALIGNWORD(sizeof(struct block_t));
    assert(offset >= 0);

    /* find position of new block in free list */
    cur = BLOCKAT(offset);
    prv = BLOCKAT(ALIGNWORD(sizeof(header_t)));

    CHECK_CANARY(cur);

#ifdef __APC_SMA_DEBUG__
    CHECK_CANARY(prv);
    fprintf(stderr, "free(%p, size=%d,id=%d)\n", cur, (int)(cur->size), cur->id);
#endif
    while (prv->next != 0 && prv->next < offset) {
        prv = BLOCKAT(prv->next);
#ifdef __APC_SMA_DEBUG__
        CHECK_CANARY(prv);
#endif
    }

    CHECK_CANARY(prv);

    /* insert new block after prv */
    cur->next = prv->next;
    prv->next = offset;

#ifdef __APC_SMA_DEBUG__
    CHECK_CANARY(cur);
    cur->id = -1;
#endif

    /* update the block header */
    header = (header_t*) shmaddr;
    header->avail += cur->size;
    size = cur->size;

    if (((char *)prv) + prv->size == (char *) cur) {
        /* cur and prv share an edge, combine them */
        prv->size += cur->size;
        prv->next = cur->next;
        RESET_CANARY(cur);
        cur = prv;
    }

    nxt = BLOCKAT(cur->next);

    if (((char *)cur) + cur->size == (char *) nxt) {
        /* cur and nxt shared an edge, combine them */
        cur->size += nxt->size;
        cur->next = nxt->next;
#ifdef __APC_SMA_DEBUG__
        CHECK_CANARY(nxt);
        nxt->id = -1; /* assert this or set it ? */
#endif
        RESET_CANARY(nxt);
    }
    header->nfoffset = 0;  /* Reset the next fit search marker */

    return size;
}
/* }}} */

The core files can be found here: http://php-apc.sourcearchive.com/documentation/3.0.18/files.html

cballou