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:
apc_delete
apc_cache_user_delete
remove_slot
free_slot
apc_sma_free
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
2010-05-21 18:47:36