/* {{{ apc_cache_real_expunge */ PHP_APCU_API void apc_cache_real_expunge(apc_cache_t* cache) { /* increment counter */ cache->header->nexpunges++; /* expunge */ { zend_ulong i; for (i = 0; i < cache->nslots; i++) { apc_cache_slot_t* p = cache->slots[i]; while (p) { apc_cache_remove_slot(cache, &p); } cache->slots[i] = NULL; } } /* set new time so counters make sense */ cache->header->stime = apc_time(); /* reset counters */ cache->header->ninserts = 0; cache->header->nentries = 0; cache->header->nhits = 0; cache->header->nmisses = 0; /* resets lastkey */ memset(&cache->header->lastkey, 0, sizeof(apc_cache_key_t)); } /* }}} */
/* {{{ apc_cache_clear */ PHP_APCU_API void apc_cache_clear(apc_cache_t* cache) { /* check there is a cache and it is not busy */ if(!cache || apc_cache_busy(cache)) { return; } /* lock header */ APC_LOCK(cache->header); /* set busy */ cache->header->state |= APC_CACHE_ST_BUSY; /* expunge cache */ apc_cache_real_expunge(cache); /* set info */ cache->header->stime = apc_time(); cache->header->nexpunges = 0; /* unset busy */ cache->header->state &= ~APC_CACHE_ST_BUSY; /* unlock header */ APC_UNLOCK(cache->header); }
/* {{{ apc_iterator_fetch_active */ static int apc_iterator_fetch_active(apc_iterator_t *iterator) { int count=0; apc_cache_slot_t **slot; apc_iterator_item_t *item; time_t t; t = apc_time(); while (apc_stack_size(iterator->stack) > 0) { apc_iterator_item_dtor(apc_stack_pop(iterator->stack)); } php_apc_try(APC_RLOCK(apc_user_cache->header), { while(count <= iterator->chunk_size && iterator->slot_idx < apc_user_cache->nslots) { slot = &apc_user_cache->slots[iterator->slot_idx]; while(*slot) { if (apc_iterator_check_expiry(apc_user_cache, slot, t)) { if (apc_iterator_search_match(iterator, slot)) { count++; item = apc_iterator_item_ctor(iterator, slot); if (item) { apc_stack_push(iterator->stack, item); } } } slot = &(*slot)->next; } iterator->slot_idx++; } }, {
/* {{{ apc_cache_make_key */ PHP_APCU_API zend_bool apc_cache_make_key(apc_cache_key_t* key, zend_string *str) { assert(key != NULL); if (!str) { return 0; } key->str = str; key->mtime = apc_time(); return 1; }
/* {{{ apc_cache_defense */ PHP_APCU_API zend_bool apc_cache_defense(apc_cache_t* cache, apc_cache_key_t* key) { zend_bool result = 0; #ifdef ZTS # define FROM_DIFFERENT_THREAD(k) ((key->owner = TSRMLS_CACHE) != (k)->owner) #else # define FROM_DIFFERENT_THREAD(k) ((key->owner = getpid()) != (k)->owner) #endif /* only continue if slam defense is enabled */ if (cache->defend) { /* for copy of locking key struct */ apc_cache_key_t *last = &cache->header->lastkey; if (!last->str) { return 0; } /* check the hash and length match */ if(ZSTR_HASH(last->str) == ZSTR_HASH(key->str) && ZSTR_LEN(last->str) == ZSTR_LEN(key->str)) { /* check the time ( last second considered slam ) and context */ if(last->mtime == key->mtime && FROM_DIFFERENT_THREAD(last)) { /* potential cache slam */ apc_debug( "Potential cache slam averted for key '%s'", key->str); result = 1; } else { /* sets enough information for an educated guess, but is not exact */ last->str = key->str; last->mtime = apc_time(); /* required to tell contexts apart */ #ifdef ZTS last->owner = TSRMLS_CACHE; #else last->owner = getpid(); #endif } } } return result; }
/* {{{ apc_cache_store */ PHP_APCU_API zend_bool apc_cache_store(apc_cache_t* cache, zend_string *strkey, const zval *val, const int32_t ttl, const zend_bool exclusive) { apc_cache_entry_t *entry; apc_cache_key_t key; time_t t; apc_context_t ctxt={0,}; zend_bool ret = 0; t = apc_time(); /* initialize a context suitable for making an insert */ if (apc_cache_make_context(cache, &ctxt, APC_CONTEXT_SHARE, APC_SMALL_POOL, APC_COPY_IN, 0)) { /* initialize the key for insertion */ if (apc_cache_make_key(&key, strkey)) { /* run cache defense */ if (!apc_cache_defense(cache, &key)) { /* initialize the entry for insertion */ if ((entry = apc_cache_make_entry(&ctxt, &key, val, ttl))) { /* execute an insertion */ if (apc_cache_insert(cache, &key, entry, &ctxt, t, exclusive)) { ret = 1; } } } } /* in any case of failure the context should be destroyed */ if (!ret) { apc_cache_destroy_context(&ctxt); } } return ret; } /* }}} */
/* {{{ apc_cache_default_expunge */ PHP_APCU_API void apc_cache_default_expunge(apc_cache_t* cache, size_t size) { time_t t; size_t suitable = 0L; size_t available = 0L; t = apc_time(); /* check there is a cache, and it is not busy */ if(!cache || apc_cache_busy(cache)) { return; } /* get the lock for header */ APC_LOCK(cache->header); /* update state in header */ cache->header->state |= APC_CACHE_ST_BUSY; /* make suitable selection */ suitable = (cache->smart > 0L) ? (size_t) (cache->smart * size) : (size_t) (cache->sma->size/2); /* gc */ apc_cache_gc(cache); /* get available */ available = cache->sma->get_avail_mem(); /* perform expunge processing */ if(!cache->ttl) { /* check it is necessary to expunge */ if (available < suitable) { apc_cache_real_expunge(cache); } } else { apc_cache_slot_t **slot; /* check that expunge is necessary */ if (available < suitable) { zend_ulong i; /* look for junk */ for (i = 0; i < cache->nslots; i++) { slot = &cache->slots[i]; while (*slot) { /* * Entry TTL has precedence over cache TTL */ if((*slot)->value->ttl) { if((time_t) ((*slot)->ctime + (*slot)->value->ttl) < t) { apc_cache_remove_slot(cache, slot); continue; } } else if(cache->ttl) { if((time_t) ((*slot)->ctime + cache->ttl) < t) { apc_cache_remove_slot(cache, slot); continue; } } /* grab next slot */ slot = &(*slot)->next; } } /* if the cache now has space, then reset last key */ if (cache->sma->get_avail_size(size)) { /* wipe lastkey */ memset(&cache->header->lastkey, 0, sizeof(apc_cache_key_t)); } else { /* with not enough space left in cache, we are forced to expunge */ apc_cache_real_expunge(cache); } } } /* we are done */ cache->header->state &= ~APC_CACHE_ST_BUSY; /* unlock header */ APC_UNLOCK(cache->header); }
/* {{{ apc_cache_update */ PHP_APCU_API zend_bool apc_cache_update(apc_cache_t* cache, zend_string *key, apc_cache_updater_t updater, void* data) { apc_cache_slot_t** slot; apc_cache_entry_t tmp_entry; zend_bool retval = 0; zend_ulong h, s; if(apc_cache_busy(cache)) { /* cannot service request right now */ return 0; } /* calculate hash */ apc_cache_hash_slot(cache, key, &h, &s); /* lock header */ APC_LOCK(cache->header); /* find head */ slot = &cache->slots[s]; while (*slot) { /* check for a match by hash and identifier */ if ((h == ZSTR_HASH((*slot)->key.str)) && memcmp(ZSTR_VAL((*slot)->key.str), ZSTR_VAL(key), ZSTR_LEN(key)) == SUCCESS) { /* attempt to perform update */ switch(Z_TYPE((*slot)->value->val)) { case IS_ARRAY: case IS_OBJECT: { if(cache->serializer) { retval = 0; break; } } /* break intentionally omitted */ default: { /* executing update */ retval = updater(cache, (*slot)->value, data); /* set modified time */ (*slot)->key.mtime = apc_time(); } break; } /* unlock header */ APC_UNLOCK(cache->header); return retval; } /* set next slot */ slot = &(*slot)->next; } /* unlock header */ APC_UNLOCK(cache->header); /* failed to find matching entry, create it */ ZVAL_LONG(&tmp_entry.val, 0); updater(cache, &tmp_entry, data); if(apc_cache_store(cache, key, &tmp_entry.val, 0, 0)) { return 1; } return 0; }