X-Git-Url: http://ftp.carnet.hr/carnet-debian/scm?p=php5-apc.git;a=blobdiff_plain;f=apc_cache.c;fp=apc_cache.c;h=0000000000000000000000000000000000000000;hp=a837871d08e753e27883496f3859afcebf02a178;hb=a34ea625346aecd2d2b227b8ca4f114eaa607213;hpb=b0895ef8074c6dd090f9b0a65d58179941541756 diff --git a/apc_cache.c b/apc_cache.c deleted file mode 100644 index a837871..0000000 --- a/apc_cache.c +++ /dev/null @@ -1,1114 +0,0 @@ -/* - +----------------------------------------------------------------------+ - | APC | - +----------------------------------------------------------------------+ - | Copyright (c) 2008 The PHP Group | - +----------------------------------------------------------------------+ - | This source file is subject to version 3.01 of the PHP license, | - | that is bundled with this package in the file LICENSE, and is | - | available through the world-wide-web at the following url: | - | http://www.php.net/license/3_01.txt | - | If you did not receive a copy of the PHP license and are unable to | - | obtain it through the world-wide-web, please send a note to | - | license@php.net so we can mail you a copy immediately. | - +----------------------------------------------------------------------+ - | Authors: Daniel Cowgill | - | Rasmus Lerdorf | - | Arun C. Murthy | - | Gopal Vijayaraghavan | - +----------------------------------------------------------------------+ - - This software was contributed to PHP by Community Connect Inc. in 2002 - and revised in 2005 by Yahoo! Inc. to add support for PHP 5.1. - Future revisions and derivatives of this source code must acknowledge - Community Connect Inc. as the original contributor of this module by - leaving this note intact in the source code. - - All other licensing and usage conditions are those of the PHP Group. - - */ - -/* $Id: apc_cache.c,v 3.145.2.4 2008/05/11 18:57:00 rasmus Exp $ */ - -#include "apc_cache.h" -#include "apc_lock.h" -#include "apc_sma.h" -#include "apc_globals.h" -#include "SAPI.h" -#include "ext/standard/php_var.h" -#include "ext/standard/php_smart_str.h" - -/* TODO: rehash when load factor exceeds threshold */ - -#define CHECK(p) { if ((p) == NULL) return NULL; } - -/* {{{ locking macros */ -#define CREATE_LOCK(lock) apc_lck_create(NULL, 0, 1, lock) -#define DESTROY_LOCK(c) apc_lck_destroy(c->header->lock) -#define LOCK(c) { HANDLE_BLOCK_INTERRUPTIONS(); apc_lck_lock(c->header->lock); } -#define RDLOCK(c) { HANDLE_BLOCK_INTERRUPTIONS(); apc_lck_rdlock(c->header->lock); } -#define UNLOCK(c) { apc_lck_unlock(c->header->lock); HANDLE_UNBLOCK_INTERRUPTIONS(); } -/* }}} */ - -/* {{{ key_equals */ -#define key_equals(a, b) (a.inode==b.inode && a.device==b.device) -/* }}} */ - -/* {{{ hash */ -static unsigned int hash(apc_cache_key_t key) -{ - return key.data.file.device + key.data.file.inode; -} -/* }}} */ - -/* {{{ string_nhash_8 */ -static unsigned int string_nhash_8(const char *s, size_t len) -{ - register const unsigned int *iv = (const unsigned int *)s; - register unsigned int h = 0; - register const unsigned int *e = (const unsigned int *)(s + len - (len % sizeof(unsigned int))); - - for(;iv> ((8*sizeof(unsigned int)) - 7)); - } - s = (const char *)iv; - for(len %= sizeof(unsigned int);len;len--) { - h += *(s++); - } - h ^= (h >> 13); - h ^= (h >> 7); - return h; -} -/* }}} */ - -/* {{{ make_slot */ -slot_t* make_slot(apc_cache_key_t key, apc_cache_entry_t* value, slot_t* next, time_t t) -{ - slot_t* p = apc_sma_malloc(sizeof(slot_t)); - if (!p) return NULL; - - if(value->type == APC_CACHE_ENTRY_USER) { - char *identifier = (char*) apc_xstrdup(key.data.user.identifier, apc_sma_malloc); - if (!identifier) { - apc_sma_free(p); - return NULL; - } - key.data.user.identifier = identifier; - } else if(key.type == APC_CACHE_KEY_FPFILE) { - char *fullpath = (char*) apc_xstrdup(key.data.fpfile.fullpath, apc_sma_malloc); - if (!fullpath) { - apc_sma_free(p); - return NULL; - } - key.data.fpfile.fullpath = fullpath; - } - p->key = key; - p->value = value; - p->next = next; - p->num_hits = 0; - p->creation_time = t; - p->access_time = t; - p->deletion_time = 0; - return p; -} -/* }}} */ - -/* {{{ 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); -} -/* }}} */ - -/* {{{ 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; - } -} -/* }}} */ - -/* {{{ process_pending_removals */ -static void process_pending_removals(apc_cache_t* cache) -{ - slot_t** slot; - time_t now; - - /* This function scans the list of removed cache entries and deletes any - * entry whose reference count is zero (indicating that it is no longer - * being executed) or that has been on the pending list for more than - * cache->gc_ttl seconds (we issue a warning in the latter case). - */ - - if (!cache->header->deleted_list) - return; - - slot = &cache->header->deleted_list; - now = time(0); - - while (*slot != NULL) { - int gc_sec = cache->gc_ttl ? (now - (*slot)->deletion_time) : 0; - - if ((*slot)->value->ref_count <= 0 || gc_sec > cache->gc_ttl) { - slot_t* dead = *slot; - - if (dead->value->ref_count > 0) { - switch(dead->value->type) { - case APC_CACHE_ENTRY_FILE: - apc_log(APC_WARNING, "GC cache entry '%s' (dev=%d ino=%d) " - "was on gc-list for %d seconds", dead->value->data.file.filename, - dead->key.data.file.device, dead->key.data.file.inode, gc_sec); - break; - case APC_CACHE_ENTRY_USER: - apc_log(APC_WARNING, "GC cache entry '%s' " - "was on gc-list for %d seconds", dead->value->data.user.info, gc_sec); - break; - } - } - *slot = dead->next; - free_slot(dead); - } - else { - slot = &(*slot)->next; - } - } -} -/* }}} */ - -/* {{{ prevent_garbage_collection */ -static void prevent_garbage_collection(apc_cache_entry_t* entry) -{ - /* set reference counts on zend objects to an arbitrarily high value to - * prevent garbage collection after execution */ - - enum { BIG_VALUE = 1000 }; - - if(entry->data.file.op_array) { - entry->data.file.op_array->refcount[0] = BIG_VALUE; - } - if (entry->data.file.functions) { - int i; - apc_function_t* fns = entry->data.file.functions; - for (i=0; fns[i].function != NULL; i++) { - *(fns[i].function->op_array.refcount) = BIG_VALUE; - } - } - if (entry->data.file.classes) { - int i; - apc_class_t* classes = entry->data.file.classes; - for (i=0; classes[i].class_entry != NULL; i++) { -#ifdef ZEND_ENGINE_2 - classes[i].class_entry->refcount = BIG_VALUE; -#else - classes[i].class_entry->refcount[0] = BIG_VALUE; -#endif - } - } -} -/* }}} */ - -/* {{{ apc_cache_create */ -apc_cache_t* apc_cache_create(int size_hint, int gc_ttl, int ttl) -{ - apc_cache_t* cache; - int cache_size; - int num_slots; - int i; - - num_slots = size_hint > 0 ? size_hint*2 : 2000; - - cache = (apc_cache_t*) apc_emalloc(sizeof(apc_cache_t)); - cache_size = sizeof(cache_header_t) + num_slots*sizeof(slot_t*); - - cache->shmaddr = apc_sma_malloc(cache_size); - if(!cache->shmaddr) { - apc_eprint("Unable to allocate shared memory for cache structures. (Perhaps your shared memory size isn't large enough?). "); - } - memset(cache->shmaddr, 0, cache_size); - - cache->header = (cache_header_t*) cache->shmaddr; - cache->header->num_hits = 0; - cache->header->num_misses = 0; - cache->header->deleted_list = NULL; - cache->header->start_time = time(NULL); - cache->header->expunges = 0; - cache->header->busy = 0; - - cache->slots = (slot_t**) (((char*) cache->shmaddr) + sizeof(cache_header_t)); - cache->num_slots = num_slots; - cache->gc_ttl = gc_ttl; - cache->ttl = ttl; - CREATE_LOCK(cache->header->lock); -#if NONBLOCKING_LOCK_AVAILABLE - CREATE_LOCK(cache->header->wrlock); -#endif - for (i = 0; i < num_slots; i++) { - cache->slots[i] = NULL; - } - - return cache; -} -/* }}} */ - -/* {{{ apc_cache_destroy */ -void apc_cache_destroy(apc_cache_t* cache) -{ - DESTROY_LOCK(cache); - apc_efree(cache); -} -/* }}} */ - -/* {{{ apc_cache_clear */ -void apc_cache_clear(apc_cache_t* cache) -{ - int i; - - if(!cache) return; - - LOCK(cache); - cache->header->busy = 1; - cache->header->num_hits = 0; - cache->header->num_misses = 0; - cache->header->start_time = time(NULL); - cache->header->expunges = 0; - - for (i = 0; i < cache->num_slots; i++) { - slot_t* p = cache->slots[i]; - while (p) { - remove_slot(cache, &p); - } - cache->slots[i] = NULL; - } - - cache->header->busy = 0; - UNLOCK(cache); -} -/* }}} */ - -/* {{{ apc_cache_expunge */ -void apc_cache_expunge(apc_cache_t* cache, time_t t) -{ - int i; - - if(!cache) return; - - if(!cache->ttl) { - /* - * If cache->ttl is not set, we wipe out the entire cache when - * we run out of space. - */ - LOCK(cache); - cache->header->busy = 1; - cache->header->expunges++; - for (i = 0; i < cache->num_slots; i++) { - slot_t* p = cache->slots[i]; - while (p) { - remove_slot(cache, &p); - } - cache->slots[i] = NULL; - } - cache->header->busy = 0; - UNLOCK(cache); - } else { - slot_t **p; - - /* - * If the ttl for the cache is set we walk through and delete stale - * entries. For the user cache that is slightly confusing since - * we have the individual entry ttl's we can look at, but that would be - * too much work. So if you want the user cache expunged, set a high - * default apc.user_ttl and still provide a specific ttl for each entry - * on insert - */ - - LOCK(cache); - cache->header->busy = 1; - cache->header->expunges++; - for (i = 0; i < cache->num_slots; i++) { - p = &cache->slots[i]; - while(*p) { - /* - * For the user cache we look at the individual entry ttl values - * and if not set fall back to the default ttl for the user cache - */ - if((*p)->value->type == APC_CACHE_ENTRY_USER) { - if((*p)->value->data.user.ttl) { - if((*p)->creation_time + (*p)->value->data.user.ttl < t) { - remove_slot(cache, p); - continue; - } - } else if(cache->ttl) { - if((*p)->creation_time + cache->ttl < t) { - remove_slot(cache, p); - continue; - } - } - } else if((*p)->access_time < (t - cache->ttl)) { - remove_slot(cache, p); - continue; - } - p = &(*p)->next; - } - } - cache->header->busy = 0; - UNLOCK(cache); - } -} -/* }}} */ - -/* {{{ apc_cache_insert */ -int apc_cache_insert(apc_cache_t* cache, - apc_cache_key_t key, - apc_cache_entry_t* value, - time_t t) -{ - slot_t** slot; - - if (!value) { - return 0; - } - -#ifdef __DEBUG_APC__ - fprintf(stderr,"Inserting [%s]\n", value->data.file.filename); -#endif - - LOCK(cache); - process_pending_removals(cache); - - if(key.type == APC_CACHE_KEY_FILE) slot = &cache->slots[hash(key) % cache->num_slots]; - else slot = &cache->slots[string_nhash_8(key.data.fpfile.fullpath, key.data.fpfile.fullpath_len) % cache->num_slots]; - - while(*slot) { - if(key.type == (*slot)->key.type) { - if(key.type == APC_CACHE_KEY_FILE) { - if(key_equals((*slot)->key.data.file, key.data.file)) { - /* If existing slot for the same device+inode is different, remove it and insert the new version */ - if ((*slot)->key.mtime != key.mtime) { - remove_slot(cache, slot); - break; - } - UNLOCK(cache); - return 0; - } else if(cache->ttl && (*slot)->access_time < (t - cache->ttl)) { - remove_slot(cache, slot); - continue; - } - } else { /* APC_CACHE_KEY_FPFILE */ - if(!memcmp((*slot)->key.data.fpfile.fullpath, key.data.fpfile.fullpath, key.data.fpfile.fullpath_len+1)) { - /* Hrm.. it's already here, remove it and insert new one */ - remove_slot(cache, slot); - break; - } else if(cache->ttl && (*slot)->access_time < (t - cache->ttl)) { - remove_slot(cache, slot); - continue; - } - } - } - slot = &(*slot)->next; - } - - if ((*slot = make_slot(key, value, *slot, t)) == NULL) { - UNLOCK(cache); - return -1; - } - - cache->header->mem_size += value->mem_size; - cache->header->num_entries++; - cache->header->num_inserts++; - - UNLOCK(cache); - return 1; -} -/* }}} */ - -/* {{{ apc_cache_user_insert */ -int apc_cache_user_insert(apc_cache_t* cache, apc_cache_key_t key, apc_cache_entry_t* value, time_t t, int exclusive TSRMLS_DC) -{ - slot_t** slot; - size_t* mem_size_ptr = NULL; - - if (!value) { - return 0; - } - - LOCK(cache); - process_pending_removals(cache); - - slot = &cache->slots[string_nhash_8(key.data.user.identifier, key.data.user.identifier_len) % cache->num_slots]; - - if (APCG(mem_size_ptr) != NULL) { - mem_size_ptr = APCG(mem_size_ptr); - APCG(mem_size_ptr) = NULL; - } - - while (*slot) { - if (!memcmp((*slot)->key.data.user.identifier, key.data.user.identifier, key.data.user.identifier_len)) { - /* - * At this point we have found the user cache entry. If we are doing - * an exclusive insert (apc_add) we are going to bail right away if - * the user entry already exists and it has no ttl, or - * there is a ttl and the entry has not timed out yet. - */ - if(exclusive && ( !(*slot)->value->data.user.ttl || - ( (*slot)->value->data.user.ttl && ((*slot)->creation_time + (*slot)->value->data.user.ttl) >= t ) - ) ) { - UNLOCK(cache); - return 0; - } - remove_slot(cache, slot); - break; - } else - /* - * This is a bit nasty. The idea here is to do runtime cleanup of the linked list of - * slot entries so we don't always have to skip past a bunch of stale entries. We check - * for staleness here and get rid of them by first checking to see if the cache has a global - * access ttl on it and removing entries that haven't been accessed for ttl seconds and secondly - * we see if the entry has a hard ttl on it and remove it if it has been around longer than its ttl - */ - if((cache->ttl && (*slot)->access_time < (t - cache->ttl)) || - ((*slot)->value->data.user.ttl && ((*slot)->creation_time + (*slot)->value->data.user.ttl) < t)) { - remove_slot(cache, slot); - continue; - } - slot = &(*slot)->next; - } - - if (mem_size_ptr != NULL) { - APCG(mem_size_ptr) = mem_size_ptr; - } - - if ((*slot = make_slot(key, value, *slot, t)) == NULL) { - UNLOCK(cache); - return 0; - } - if (APCG(mem_size_ptr) != NULL) { - value->mem_size = *APCG(mem_size_ptr); - cache->header->mem_size += *APCG(mem_size_ptr); - } - cache->header->num_entries++; - cache->header->num_inserts++; - - UNLOCK(cache); - return 1; -} -/* }}} */ - -/* {{{ apc_cache_find_slot */ -slot_t* apc_cache_find_slot(apc_cache_t* cache, apc_cache_key_t key, time_t t) -{ - slot_t** slot; - volatile slot_t* retval = NULL; - - LOCK(cache); - if(key.type == APC_CACHE_KEY_FILE) slot = &cache->slots[hash(key) % cache->num_slots]; - else slot = &cache->slots[string_nhash_8(key.data.fpfile.fullpath, key.data.fpfile.fullpath_len) % cache->num_slots]; - - while (*slot) { - if(key.type == (*slot)->key.type) { - if(key.type == APC_CACHE_KEY_FILE) { - if(key_equals((*slot)->key.data.file, key.data.file)) { - if((*slot)->key.mtime != key.mtime) { - remove_slot(cache, slot); - cache->header->num_misses++; - UNLOCK(cache); - return NULL; - } - (*slot)->num_hits++; - (*slot)->value->ref_count++; - (*slot)->access_time = t; - prevent_garbage_collection((*slot)->value); - cache->header->num_hits++; - retval = *slot; - UNLOCK(cache); - return (slot_t*)retval; - } - } else { /* APC_CACHE_KEY_FPFILE */ - if(!memcmp((*slot)->key.data.fpfile.fullpath, key.data.fpfile.fullpath, key.data.fpfile.fullpath_len+1)) { - /* TTL Check ? */ - (*slot)->num_hits++; - (*slot)->value->ref_count++; - (*slot)->access_time = t; - prevent_garbage_collection((*slot)->value); - cache->header->num_hits++; - retval = *slot; - UNLOCK(cache); - return (slot_t*)retval; - } - } - } - slot = &(*slot)->next; - } - cache->header->num_misses++; - UNLOCK(cache); - return NULL; -} -/* }}} */ - -/* {{{ apc_cache_find */ -apc_cache_entry_t* apc_cache_find(apc_cache_t* cache, apc_cache_key_t key, time_t t) -{ - slot_t * slot = apc_cache_find_slot(cache, key, t); - return (slot) ? slot->value : NULL; -} -/* }}} */ - -/* {{{ apc_cache_user_find */ -apc_cache_entry_t* apc_cache_user_find(apc_cache_t* cache, char *strkey, int keylen, time_t t) -{ - slot_t** slot; - volatile apc_cache_entry_t* value = NULL; - - LOCK(cache); - - slot = &cache->slots[string_nhash_8(strkey, keylen) % cache->num_slots]; - - while (*slot) { - if (!memcmp((*slot)->key.data.user.identifier, strkey, keylen)) { - /* Check to make sure this entry isn't expired by a hard TTL */ - if((*slot)->value->data.user.ttl && ((*slot)->creation_time + (*slot)->value->data.user.ttl) < t) { - remove_slot(cache, slot); - UNLOCK(cache); - return NULL; - } - /* Otherwise we are fine, increase counters and return the cache entry */ - (*slot)->num_hits++; - (*slot)->value->ref_count++; - (*slot)->access_time = t; - - cache->header->num_hits++; - value = (*slot)->value; - UNLOCK(cache); - return (apc_cache_entry_t*)value; - } - slot = &(*slot)->next; - } - - UNLOCK(cache); - return NULL; -} -/* }}} */ - -/* {{{ 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; -} -/* }}} */ - -/* {{{ apc_cache_release */ -void apc_cache_release(apc_cache_t* cache, apc_cache_entry_t* entry) -{ - LOCK(cache); - entry->ref_count--; - UNLOCK(cache); -} -/* }}} */ - -/* {{{ apc_cache_make_file_key */ -int apc_cache_make_file_key(apc_cache_key_t* key, - const char* filename, - const char* include_path, - time_t t - TSRMLS_DC) -{ - struct stat *tmp_buf=NULL; - struct apc_fileinfo_t fileinfo = { {0}, }; - int len; - - assert(key != NULL); - - if (!filename || !SG(request_info).path_translated) { -#ifdef __DEBUG_APC__ - fprintf(stderr,"No filename and no path_translated - bailing\n"); -#endif - return 0; - } - - len = strlen(filename); - if(APCG(fpstat)==0) { - if(IS_ABSOLUTE_PATH(filename,len)) { - key->data.fpfile.fullpath = filename; - key->data.fpfile.fullpath_len = len; - key->mtime = t; - key->type = APC_CACHE_KEY_FPFILE; - } else { - if (apc_search_paths(filename, include_path, &fileinfo) != 0) { - apc_wprint("apc failed to locate %s - bailing", filename); - return 0; - } - - if(!realpath(fileinfo.fullpath, APCG(canon_path))) { - apc_wprint("realpath failed to canonicalize %s - bailing", filename); - return 0; - } - - key->data.fpfile.fullpath = APCG(canon_path); - key->data.fpfile.fullpath_len = strlen(APCG(canon_path)); - key->mtime = t; - key->type = APC_CACHE_KEY_FPFILE; - } - return 1; - } - - if(!strcmp(SG(request_info).path_translated, filename)) { - tmp_buf = sapi_get_stat(TSRMLS_C); /* Apache has already done this stat() for us */ - } - if(tmp_buf) { - fileinfo.st_buf.sb = *tmp_buf; - } else { - if (apc_search_paths(filename, include_path, &fileinfo) != 0) { -#ifdef __DEBUG_APC__ - fprintf(stderr,"Stat failed %s - bailing (%s) (%d)\n",filename,SG(request_info).path_translated); -#endif - return 0; - } - } - - if(APCG(max_file_size) < fileinfo.st_buf.sb.st_size) { -#ifdef __DEBUG_APC__ - fprintf(stderr,"File is too big %s (%d - %ld) - bailing\n",filename,t,fileinfo.st_buf.sb.st_size); -#endif - return 0; - } - - /* - * This is a bit of a hack. - * - * Here I am checking to see if the file is at least 2 seconds old. - * The idea is that if the file is currently being written to then its - * mtime is going to match or at most be 1 second off of the current - * request time and we want to avoid caching files that have not been - * completely written. Of course, people should be using atomic - * mechanisms to push files onto live web servers, but adding this - * tiny safety is easier than educating the world. This is now - * configurable, but the default is still 2 seconds. - */ - if(APCG(file_update_protection) && (t - fileinfo.st_buf.sb.st_mtime < APCG(file_update_protection))) { -#ifdef __DEBUG_APC__ - fprintf(stderr,"File is too new %s (%d - %d) - bailing\n",filename,t,fileinfo.st_buf.sb.st_mtime); -#endif - return 0; - } - - key->data.file.device = fileinfo.st_buf.sb.st_dev; - key->data.file.inode = fileinfo.st_buf.sb.st_ino; - /* - * If working with content management systems that like to munge the mtime, - * it might be appropriate to key off of the ctime to be immune to systems - * that try to backdate a template. If the mtime is set to something older - * than the previous mtime of a template we will obviously never see this - * "older" template. At some point the Smarty templating system did this. - * I generally disagree with using the ctime here because you lose the - * ability to warm up new content by saving it to a temporary file, hitting - * it once to cache it and then renaming it into its permanent location so - * set the apc.stat_ctime=true to enable this check. - */ - if(APCG(stat_ctime)) { - key->mtime = (fileinfo.st_buf.sb.st_ctime > fileinfo.st_buf.sb.st_mtime) ? fileinfo.st_buf.sb.st_ctime : fileinfo.st_buf.sb.st_mtime; - } else { - key->mtime = fileinfo.st_buf.sb.st_mtime; - } - key->type = APC_CACHE_KEY_FILE; - return 1; -} -/* }}} */ - -/* {{{ apc_cache_make_user_key */ -int apc_cache_make_user_key(apc_cache_key_t* key, char* identifier, int identifier_len, const time_t t) -{ - assert(key != NULL); - - if (!identifier) - return 0; - - key->data.user.identifier = identifier; - key->data.user.identifier_len = identifier_len; - key->mtime = t; - key->type = APC_CACHE_KEY_USER; - return 1; -} -/* }}} */ - -/* {{{ apc_cache_make_file_entry */ -apc_cache_entry_t* apc_cache_make_file_entry(const char* filename, - zend_op_array* op_array, - apc_function_t* functions, - apc_class_t* classes) -{ - apc_cache_entry_t* entry; - - entry = (apc_cache_entry_t*) apc_sma_malloc(sizeof(apc_cache_entry_t)); - if (!entry) return NULL; - - entry->data.file.filename = apc_xstrdup(filename, apc_sma_malloc); - if(!entry->data.file.filename) { -#ifdef __DEBUG_APC__ - fprintf(stderr,"apc_cache_make_file_entry: entry->data.file.filename is NULL - bailing\n"); -#endif - apc_sma_free(entry); - return NULL; - } -#ifdef __DEBUG_APC__ - fprintf(stderr,"apc_cache_make_file_entry: entry->data.file.filename is [%s]\n",entry->data.file.filename); -#endif - entry->data.file.op_array = op_array; - entry->data.file.functions = functions; - entry->data.file.classes = classes; - entry->type = APC_CACHE_ENTRY_FILE; - entry->ref_count = 0; - entry->mem_size = 0; - return entry; -} -/* }}} */ - -/* {{{ apc_cache_store_zval */ -zval* apc_cache_store_zval(zval* dst, const zval* src, apc_malloc_t allocate, apc_free_t deallocate) -{ - smart_str buf = {0}; - php_serialize_data_t var_hash; - TSRMLS_FETCH(); - - if((src->type & ~IS_CONSTANT_INDEX) == IS_OBJECT) { - if(!dst) { - CHECK(dst = (zval*) allocate(sizeof(zval))); - } - - PHP_VAR_SERIALIZE_INIT(var_hash); - php_var_serialize(&buf, (zval**)&src, &var_hash TSRMLS_CC); - PHP_VAR_SERIALIZE_DESTROY(var_hash); - - dst->type = IS_NULL; /* in case we fail */ - if(buf.c) { - dst->type = src->type & ~IS_CONSTANT_INDEX; - dst->value.str.len = buf.len; - CHECK(dst->value.str.val = apc_xmemcpy(buf.c, buf.len+1, allocate)); - dst->type = src->type; - smart_str_free(&buf); - } - return dst; - } else { - - /* Maintain a list of zvals we've copied to properly handle recursive structures */ - HashTable *old = APCG(copied_zvals); - APCG(copied_zvals) = emalloc(sizeof(HashTable)); - zend_hash_init(APCG(copied_zvals), 0, NULL, NULL, 0); - - dst = apc_copy_zval(dst, src, allocate, deallocate); - - if(APCG(copied_zvals)) { - zend_hash_destroy(APCG(copied_zvals)); - efree(APCG(copied_zvals)); - } - - APCG(copied_zvals) = old; - - return dst; - } -} -/* }}} */ - -/* {{{ apc_cache_fetch_zval */ -zval* apc_cache_fetch_zval(zval* dst, const zval* src, apc_malloc_t allocate, apc_free_t deallocate) -{ - TSRMLS_FETCH(); - if((src->type & ~IS_CONSTANT_INDEX) == IS_OBJECT) { - php_unserialize_data_t var_hash; - const unsigned char *p = (unsigned char*)Z_STRVAL_P(src); - - PHP_VAR_UNSERIALIZE_INIT(var_hash); - if(!php_var_unserialize(&dst, &p, p + Z_STRLEN_P(src), &var_hash TSRMLS_CC)) { - PHP_VAR_UNSERIALIZE_DESTROY(var_hash); - zval_dtor(dst); - php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Error at offset %ld of %d bytes", (long)((char*)p - Z_STRVAL_P(src)), Z_STRLEN_P(src)); - dst->type = IS_NULL; - } - PHP_VAR_UNSERIALIZE_DESTROY(var_hash); - return dst; - } else { - - /* Maintain a list of zvals we've copied to properly handle recursive structures */ - HashTable *old = APCG(copied_zvals); - APCG(copied_zvals) = emalloc(sizeof(HashTable)); - zend_hash_init(APCG(copied_zvals), 0, NULL, NULL, 0); - - dst = apc_copy_zval(dst, src, allocate, deallocate); - - if(APCG(copied_zvals)) { - zend_hash_destroy(APCG(copied_zvals)); - efree(APCG(copied_zvals)); - } - - APCG(copied_zvals) = old; - - return dst; - } -} -/* }}} */ - -/* {{{ apc_cache_free_zval */ -void apc_cache_free_zval(zval* src, apc_free_t deallocate) -{ - TSRMLS_FETCH(); - if ((src->type & ~IS_CONSTANT_INDEX) == IS_OBJECT) { - if (src->value.str.val) { - deallocate(src->value.str.val); - } - deallocate(src); - } else { - /* Maintain a list of zvals we've copied to properly handle recursive structures */ - HashTable *old = APCG(copied_zvals); - APCG(copied_zvals) = emalloc(sizeof(HashTable)); - zend_hash_init(APCG(copied_zvals), 0, NULL, NULL, 0); - - apc_free_zval(src, deallocate); - - if(APCG(copied_zvals)) { - zend_hash_destroy(APCG(copied_zvals)); - efree(APCG(copied_zvals)); - } - - APCG(copied_zvals) = old; - } -} -/* }}} */ - -/* {{{ apc_cache_make_user_entry */ -apc_cache_entry_t* apc_cache_make_user_entry(const char* info, int info_len, const zval* val, const unsigned int ttl) -{ - apc_cache_entry_t* entry; - - entry = (apc_cache_entry_t*) apc_sma_malloc(sizeof(apc_cache_entry_t)); - if (!entry) return NULL; - - entry->data.user.info = apc_xmemcpy(info, info_len, apc_sma_malloc); - entry->data.user.info_len = info_len; - if(!entry->data.user.info) { - apc_sma_free(entry); - return NULL; - } - entry->data.user.val = apc_cache_store_zval(NULL, val, apc_sma_malloc, apc_sma_free); - if(!entry->data.user.val) { - apc_sma_free(entry->data.user.info); - apc_sma_free(entry); - return NULL; - } - INIT_PZVAL(entry->data.user.val); - entry->data.user.ttl = ttl; - entry->type = APC_CACHE_ENTRY_USER; - entry->ref_count = 0; - entry->mem_size = 0; - return entry; -} -/* }}} */ - -/* {{{ apc_cache_free_entry */ -void apc_cache_free_entry(apc_cache_entry_t* entry) -{ - if (entry != NULL) { - assert(entry->ref_count == 0); - switch(entry->type) { - case APC_CACHE_ENTRY_FILE: - apc_sma_free(entry->data.file.filename); - apc_free_op_array(entry->data.file.op_array, apc_sma_free); - apc_free_functions(entry->data.file.functions, apc_sma_free); - apc_free_classes(entry->data.file.classes, apc_sma_free); - break; - case APC_CACHE_ENTRY_USER: - apc_sma_free(entry->data.user.info); - apc_cache_free_zval(entry->data.user.val, apc_sma_free); - break; - } - apc_sma_free(entry); - } -} -/* }}} */ - -/* {{{ apc_cache_info */ -apc_cache_info_t* apc_cache_info(apc_cache_t* cache, zend_bool limited) -{ - apc_cache_info_t* info; - slot_t* p; - int i; - - if(!cache) return NULL; - - LOCK(cache); - - info = (apc_cache_info_t*) apc_emalloc(sizeof(apc_cache_info_t)); - if(!info) { - UNLOCK(cache); - return NULL; - } - info->num_slots = cache->num_slots; - info->ttl = cache->ttl; - info->num_hits = cache->header->num_hits; - info->num_misses = cache->header->num_misses; - info->list = NULL; - info->deleted_list = NULL; - info->start_time = cache->header->start_time; - info->expunges = cache->header->expunges; - info->mem_size = cache->header->mem_size; - info->num_entries = cache->header->num_entries; - info->num_inserts = cache->header->num_inserts; - - if(!limited) { - /* For each hashtable slot */ - for (i = 0; i < info->num_slots; i++) { - p = cache->slots[i]; - for (; p != NULL; p = p->next) { - apc_cache_link_t* link = (apc_cache_link_t*) apc_emalloc(sizeof(apc_cache_link_t)); - - if(p->value->type == APC_CACHE_ENTRY_FILE) { - link->data.file.filename = apc_xstrdup(p->value->data.file.filename, apc_emalloc); - link->data.file.device = p->key.data.file.device; - link->data.file.inode = p->key.data.file.inode; - link->type = APC_CACHE_ENTRY_FILE; - } else if(p->value->type == APC_CACHE_ENTRY_USER) { - link->data.user.info = apc_xmemcpy(p->value->data.user.info, p->value->data.user.info_len, apc_emalloc); - link->data.user.ttl = p->value->data.user.ttl; - link->type = APC_CACHE_ENTRY_USER; - } - link->num_hits = p->num_hits; - link->mtime = p->key.mtime; - link->creation_time = p->creation_time; - link->deletion_time = p->deletion_time; - link->access_time = p->access_time; - link->ref_count = p->value->ref_count; - link->mem_size = p->value->mem_size; - link->next = info->list; - info->list = link; - } - } - - /* For each slot pending deletion */ - for (p = cache->header->deleted_list; p != NULL; p = p->next) { - apc_cache_link_t* link = (apc_cache_link_t*) apc_emalloc(sizeof(apc_cache_link_t)); - - if(p->value->type == APC_CACHE_ENTRY_FILE) { - link->data.file.filename = apc_xstrdup(p->value->data.file.filename, apc_emalloc); - if(p->key.type == APC_CACHE_KEY_FILE) { - link->data.file.device = p->key.data.file.device; - link->data.file.inode = p->key.data.file.inode; - } else { /* This is a no-stat fullpath file entry */ - link->data.file.device = 0; - link->data.file.inode = 0; - } - link->type = APC_CACHE_ENTRY_FILE; - } else if(p->value->type == APC_CACHE_ENTRY_USER) { - link->data.user.info = apc_xmemcpy(p->value->data.user.info, p->value->data.user.info_len, apc_emalloc); - link->data.user.ttl = p->value->data.user.ttl; - link->type = APC_CACHE_ENTRY_USER; - } - link->num_hits = p->num_hits; - link->mtime = p->key.mtime; - link->creation_time = p->creation_time; - link->deletion_time = p->deletion_time; - link->access_time = p->access_time; - link->ref_count = p->value->ref_count; - link->mem_size = p->value->mem_size; - link->next = info->deleted_list; - info->deleted_list = link; - } - } - - UNLOCK(cache); - return info; -} -/* }}} */ - -/* {{{ apc_cache_free_info */ -void apc_cache_free_info(apc_cache_info_t* info) -{ - apc_cache_link_t* p = info->list; - apc_cache_link_t* q = NULL; - while (p != NULL) { - q = p; - p = p->next; - if(q->type == APC_CACHE_ENTRY_FILE) apc_efree(q->data.file.filename); - else if(q->type == APC_CACHE_ENTRY_USER) apc_efree(q->data.user.info); - apc_efree(q); - } - p = info->deleted_list; - while (p != NULL) { - q = p; - p = p->next; - if(q->type == APC_CACHE_ENTRY_FILE) apc_efree(q->data.file.filename); - else if(q->type == APC_CACHE_ENTRY_USER) apc_efree(q->data.user.info); - apc_efree(q); - } - apc_efree(info); -} -/* }}} */ - -/* {{{ apc_cache_unlock */ -void apc_cache_unlock(apc_cache_t* cache) -{ - UNLOCK(cache); -} -/* }}} */ - -/* {{{ apc_cache_busy */ -zend_bool apc_cache_busy(apc_cache_t* cache) -{ - return cache->header->busy; -} -/* }}} */ - -#if NONBLOCKING_LOCK_AVAILABLE -/* {{{ apc_cache_write_lock */ -zend_bool apc_cache_write_lock(apc_cache_t* cache) -{ - return apc_lck_nb_lock(cache->header->wrlock); -} -/* }}} */ - -/* {{{ apc_cache_write_unlock */ -void apc_cache_write_unlock(apc_cache_t* cache) -{ - apc_lck_unlock(cache->header->wrlock); -} -/* }}} */ -#endif - -/* - * Local variables: - * tab-width: 4 - * c-basic-offset: 4 - * End: - * vim600: expandtab sw=4 ts=4 sts=4 fdm=marker - * vim<600: expandtab sw=4 ts=4 sts=4 - */