2 +----------------------------------------------------------------------+
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2008 The PHP Group |
6 +----------------------------------------------------------------------+
7 | This source file is subject to version 3.01 of the PHP license, |
8 | that is bundled with this package in the file LICENSE, and is |
9 | available through the world-wide-web at the following url: |
10 | http://www.php.net/license/3_01.txt |
11 | If you did not receive a copy of the PHP license and are unable to |
12 | obtain it through the world-wide-web, please send a note to |
13 | license@php.net so we can mail you a copy immediately. |
14 +----------------------------------------------------------------------+
15 | Authors: Daniel Cowgill <dcowgill@communityconnect.com> |
16 | Rasmus Lerdorf <rasmus@php.net> |
17 | Arun C. Murthy <arunc@yahoo-inc.com> |
18 | Gopal Vijayaraghavan <gopalv@yahoo-inc.com> |
19 +----------------------------------------------------------------------+
21 This software was contributed to PHP by Community Connect Inc. in 2002
22 and revised in 2005 by Yahoo! Inc. to add support for PHP 5.1.
23 Future revisions and derivatives of this source code must acknowledge
24 Community Connect Inc. as the original contributor of this module by
25 leaving this note intact in the source code.
27 All other licensing and usage conditions are those of the PHP Group.
31 /* $Id: apc_cache.c,v 3.145.2.4 2008/05/11 18:57:00 rasmus Exp $ */
33 #include "apc_cache.h"
36 #include "apc_globals.h"
38 #include "ext/standard/php_var.h"
39 #include "ext/standard/php_smart_str.h"
41 /* TODO: rehash when load factor exceeds threshold */
43 #define CHECK(p) { if ((p) == NULL) return NULL; }
45 /* {{{ locking macros */
46 #define CREATE_LOCK(lock) apc_lck_create(NULL, 0, 1, lock)
47 #define DESTROY_LOCK(c) apc_lck_destroy(c->header->lock)
48 #define LOCK(c) { HANDLE_BLOCK_INTERRUPTIONS(); apc_lck_lock(c->header->lock); }
49 #define RDLOCK(c) { HANDLE_BLOCK_INTERRUPTIONS(); apc_lck_rdlock(c->header->lock); }
50 #define UNLOCK(c) { apc_lck_unlock(c->header->lock); HANDLE_UNBLOCK_INTERRUPTIONS(); }
54 #define key_equals(a, b) (a.inode==b.inode && a.device==b.device)
58 static unsigned int hash(apc_cache_key_t key)
60 return key.data.file.device + key.data.file.inode;
64 /* {{{ string_nhash_8 */
65 static unsigned int string_nhash_8(const char *s, size_t len)
67 register const unsigned int *iv = (const unsigned int *)s;
68 register unsigned int h = 0;
69 register const unsigned int *e = (const unsigned int *)(s + len - (len % sizeof(unsigned int)));
73 h = (h << 7) | (h >> ((8*sizeof(unsigned int)) - 7));
76 for(len %= sizeof(unsigned int);len;len--) {
86 slot_t* make_slot(apc_cache_key_t key, apc_cache_entry_t* value, slot_t* next, time_t t)
88 slot_t* p = apc_sma_malloc(sizeof(slot_t));
91 if(value->type == APC_CACHE_ENTRY_USER) {
92 char *identifier = (char*) apc_xstrdup(key.data.user.identifier, apc_sma_malloc);
97 key.data.user.identifier = identifier;
98 } else if(key.type == APC_CACHE_KEY_FPFILE) {
99 char *fullpath = (char*) apc_xstrdup(key.data.fpfile.fullpath, apc_sma_malloc);
104 key.data.fpfile.fullpath = fullpath;
110 p->creation_time = t;
112 p->deletion_time = 0;
118 static void free_slot(slot_t* slot)
120 if(slot->value->type == APC_CACHE_ENTRY_USER) {
121 apc_sma_free((char *)slot->key.data.user.identifier);
122 } else if(slot->key.type == APC_CACHE_KEY_FPFILE) {
123 apc_sma_free((char *)slot->key.data.fpfile.fullpath);
125 apc_cache_free_entry(slot->value);
130 /* {{{ remove_slot */
131 static void remove_slot(apc_cache_t* cache, slot_t** slot)
133 slot_t* dead = *slot;
134 *slot = (*slot)->next;
136 cache->header->mem_size -= dead->value->mem_size;
137 cache->header->num_entries--;
138 if (dead->value->ref_count <= 0) {
142 dead->next = cache->header->deleted_list;
143 dead->deletion_time = time(0);
144 cache->header->deleted_list = dead;
149 /* {{{ process_pending_removals */
150 static void process_pending_removals(apc_cache_t* cache)
155 /* This function scans the list of removed cache entries and deletes any
156 * entry whose reference count is zero (indicating that it is no longer
157 * being executed) or that has been on the pending list for more than
158 * cache->gc_ttl seconds (we issue a warning in the latter case).
161 if (!cache->header->deleted_list)
164 slot = &cache->header->deleted_list;
167 while (*slot != NULL) {
168 int gc_sec = cache->gc_ttl ? (now - (*slot)->deletion_time) : 0;
170 if ((*slot)->value->ref_count <= 0 || gc_sec > cache->gc_ttl) {
171 slot_t* dead = *slot;
173 if (dead->value->ref_count > 0) {
174 switch(dead->value->type) {
175 case APC_CACHE_ENTRY_FILE:
176 apc_log(APC_WARNING, "GC cache entry '%s' (dev=%d ino=%d) "
177 "was on gc-list for %d seconds", dead->value->data.file.filename,
178 dead->key.data.file.device, dead->key.data.file.inode, gc_sec);
180 case APC_CACHE_ENTRY_USER:
181 apc_log(APC_WARNING, "GC cache entry '%s' "
182 "was on gc-list for %d seconds", dead->value->data.user.info, gc_sec);
190 slot = &(*slot)->next;
196 /* {{{ prevent_garbage_collection */
197 static void prevent_garbage_collection(apc_cache_entry_t* entry)
199 /* set reference counts on zend objects to an arbitrarily high value to
200 * prevent garbage collection after execution */
202 enum { BIG_VALUE = 1000 };
204 if(entry->data.file.op_array) {
205 entry->data.file.op_array->refcount[0] = BIG_VALUE;
207 if (entry->data.file.functions) {
209 apc_function_t* fns = entry->data.file.functions;
210 for (i=0; fns[i].function != NULL; i++) {
211 *(fns[i].function->op_array.refcount) = BIG_VALUE;
214 if (entry->data.file.classes) {
216 apc_class_t* classes = entry->data.file.classes;
217 for (i=0; classes[i].class_entry != NULL; i++) {
219 classes[i].class_entry->refcount = BIG_VALUE;
221 classes[i].class_entry->refcount[0] = BIG_VALUE;
228 /* {{{ apc_cache_create */
229 apc_cache_t* apc_cache_create(int size_hint, int gc_ttl, int ttl)
236 num_slots = size_hint > 0 ? size_hint*2 : 2000;
238 cache = (apc_cache_t*) apc_emalloc(sizeof(apc_cache_t));
239 cache_size = sizeof(cache_header_t) + num_slots*sizeof(slot_t*);
241 cache->shmaddr = apc_sma_malloc(cache_size);
242 if(!cache->shmaddr) {
243 apc_eprint("Unable to allocate shared memory for cache structures. (Perhaps your shared memory size isn't large enough?). ");
245 memset(cache->shmaddr, 0, cache_size);
247 cache->header = (cache_header_t*) cache->shmaddr;
248 cache->header->num_hits = 0;
249 cache->header->num_misses = 0;
250 cache->header->deleted_list = NULL;
251 cache->header->start_time = time(NULL);
252 cache->header->expunges = 0;
253 cache->header->busy = 0;
255 cache->slots = (slot_t**) (((char*) cache->shmaddr) + sizeof(cache_header_t));
256 cache->num_slots = num_slots;
257 cache->gc_ttl = gc_ttl;
259 CREATE_LOCK(cache->header->lock);
260 #if NONBLOCKING_LOCK_AVAILABLE
261 CREATE_LOCK(cache->header->wrlock);
263 for (i = 0; i < num_slots; i++) {
264 cache->slots[i] = NULL;
271 /* {{{ apc_cache_destroy */
272 void apc_cache_destroy(apc_cache_t* cache)
279 /* {{{ apc_cache_clear */
280 void apc_cache_clear(apc_cache_t* cache)
287 cache->header->busy = 1;
288 cache->header->num_hits = 0;
289 cache->header->num_misses = 0;
290 cache->header->start_time = time(NULL);
291 cache->header->expunges = 0;
293 for (i = 0; i < cache->num_slots; i++) {
294 slot_t* p = cache->slots[i];
296 remove_slot(cache, &p);
298 cache->slots[i] = NULL;
301 cache->header->busy = 0;
306 /* {{{ apc_cache_expunge */
307 void apc_cache_expunge(apc_cache_t* cache, time_t t)
315 * If cache->ttl is not set, we wipe out the entire cache when
316 * we run out of space.
319 cache->header->busy = 1;
320 cache->header->expunges++;
321 for (i = 0; i < cache->num_slots; i++) {
322 slot_t* p = cache->slots[i];
324 remove_slot(cache, &p);
326 cache->slots[i] = NULL;
328 cache->header->busy = 0;
334 * If the ttl for the cache is set we walk through and delete stale
335 * entries. For the user cache that is slightly confusing since
336 * we have the individual entry ttl's we can look at, but that would be
337 * too much work. So if you want the user cache expunged, set a high
338 * default apc.user_ttl and still provide a specific ttl for each entry
343 cache->header->busy = 1;
344 cache->header->expunges++;
345 for (i = 0; i < cache->num_slots; i++) {
346 p = &cache->slots[i];
349 * For the user cache we look at the individual entry ttl values
350 * and if not set fall back to the default ttl for the user cache
352 if((*p)->value->type == APC_CACHE_ENTRY_USER) {
353 if((*p)->value->data.user.ttl) {
354 if((*p)->creation_time + (*p)->value->data.user.ttl < t) {
355 remove_slot(cache, p);
358 } else if(cache->ttl) {
359 if((*p)->creation_time + cache->ttl < t) {
360 remove_slot(cache, p);
364 } else if((*p)->access_time < (t - cache->ttl)) {
365 remove_slot(cache, p);
371 cache->header->busy = 0;
377 /* {{{ apc_cache_insert */
378 int apc_cache_insert(apc_cache_t* cache,
380 apc_cache_entry_t* value,
390 fprintf(stderr,"Inserting [%s]\n", value->data.file.filename);
394 process_pending_removals(cache);
396 if(key.type == APC_CACHE_KEY_FILE) slot = &cache->slots[hash(key) % cache->num_slots];
397 else slot = &cache->slots[string_nhash_8(key.data.fpfile.fullpath, key.data.fpfile.fullpath_len) % cache->num_slots];
400 if(key.type == (*slot)->key.type) {
401 if(key.type == APC_CACHE_KEY_FILE) {
402 if(key_equals((*slot)->key.data.file, key.data.file)) {
403 /* If existing slot for the same device+inode is different, remove it and insert the new version */
404 if ((*slot)->key.mtime != key.mtime) {
405 remove_slot(cache, slot);
410 } else if(cache->ttl && (*slot)->access_time < (t - cache->ttl)) {
411 remove_slot(cache, slot);
414 } else { /* APC_CACHE_KEY_FPFILE */
415 if(!memcmp((*slot)->key.data.fpfile.fullpath, key.data.fpfile.fullpath, key.data.fpfile.fullpath_len+1)) {
416 /* Hrm.. it's already here, remove it and insert new one */
417 remove_slot(cache, slot);
419 } else if(cache->ttl && (*slot)->access_time < (t - cache->ttl)) {
420 remove_slot(cache, slot);
425 slot = &(*slot)->next;
428 if ((*slot = make_slot(key, value, *slot, t)) == NULL) {
433 cache->header->mem_size += value->mem_size;
434 cache->header->num_entries++;
435 cache->header->num_inserts++;
442 /* {{{ apc_cache_user_insert */
443 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)
446 size_t* mem_size_ptr = NULL;
453 process_pending_removals(cache);
455 slot = &cache->slots[string_nhash_8(key.data.user.identifier, key.data.user.identifier_len) % cache->num_slots];
457 if (APCG(mem_size_ptr) != NULL) {
458 mem_size_ptr = APCG(mem_size_ptr);
459 APCG(mem_size_ptr) = NULL;
463 if (!memcmp((*slot)->key.data.user.identifier, key.data.user.identifier, key.data.user.identifier_len)) {
465 * At this point we have found the user cache entry. If we are doing
466 * an exclusive insert (apc_add) we are going to bail right away if
467 * the user entry already exists and it has no ttl, or
468 * there is a ttl and the entry has not timed out yet.
470 if(exclusive && ( !(*slot)->value->data.user.ttl ||
471 ( (*slot)->value->data.user.ttl && ((*slot)->creation_time + (*slot)->value->data.user.ttl) >= t )
476 remove_slot(cache, slot);
480 * This is a bit nasty. The idea here is to do runtime cleanup of the linked list of
481 * slot entries so we don't always have to skip past a bunch of stale entries. We check
482 * for staleness here and get rid of them by first checking to see if the cache has a global
483 * access ttl on it and removing entries that haven't been accessed for ttl seconds and secondly
484 * we see if the entry has a hard ttl on it and remove it if it has been around longer than its ttl
486 if((cache->ttl && (*slot)->access_time < (t - cache->ttl)) ||
487 ((*slot)->value->data.user.ttl && ((*slot)->creation_time + (*slot)->value->data.user.ttl) < t)) {
488 remove_slot(cache, slot);
491 slot = &(*slot)->next;
494 if (mem_size_ptr != NULL) {
495 APCG(mem_size_ptr) = mem_size_ptr;
498 if ((*slot = make_slot(key, value, *slot, t)) == NULL) {
502 if (APCG(mem_size_ptr) != NULL) {
503 value->mem_size = *APCG(mem_size_ptr);
504 cache->header->mem_size += *APCG(mem_size_ptr);
506 cache->header->num_entries++;
507 cache->header->num_inserts++;
514 /* {{{ apc_cache_find_slot */
515 slot_t* apc_cache_find_slot(apc_cache_t* cache, apc_cache_key_t key, time_t t)
518 volatile slot_t* retval = NULL;
521 if(key.type == APC_CACHE_KEY_FILE) slot = &cache->slots[hash(key) % cache->num_slots];
522 else slot = &cache->slots[string_nhash_8(key.data.fpfile.fullpath, key.data.fpfile.fullpath_len) % cache->num_slots];
525 if(key.type == (*slot)->key.type) {
526 if(key.type == APC_CACHE_KEY_FILE) {
527 if(key_equals((*slot)->key.data.file, key.data.file)) {
528 if((*slot)->key.mtime != key.mtime) {
529 remove_slot(cache, slot);
530 cache->header->num_misses++;
535 (*slot)->value->ref_count++;
536 (*slot)->access_time = t;
537 prevent_garbage_collection((*slot)->value);
538 cache->header->num_hits++;
541 return (slot_t*)retval;
543 } else { /* APC_CACHE_KEY_FPFILE */
544 if(!memcmp((*slot)->key.data.fpfile.fullpath, key.data.fpfile.fullpath, key.data.fpfile.fullpath_len+1)) {
547 (*slot)->value->ref_count++;
548 (*slot)->access_time = t;
549 prevent_garbage_collection((*slot)->value);
550 cache->header->num_hits++;
553 return (slot_t*)retval;
557 slot = &(*slot)->next;
559 cache->header->num_misses++;
565 /* {{{ apc_cache_find */
566 apc_cache_entry_t* apc_cache_find(apc_cache_t* cache, apc_cache_key_t key, time_t t)
568 slot_t * slot = apc_cache_find_slot(cache, key, t);
569 return (slot) ? slot->value : NULL;
573 /* {{{ apc_cache_user_find */
574 apc_cache_entry_t* apc_cache_user_find(apc_cache_t* cache, char *strkey, int keylen, time_t t)
577 volatile apc_cache_entry_t* value = NULL;
581 slot = &cache->slots[string_nhash_8(strkey, keylen) % cache->num_slots];
584 if (!memcmp((*slot)->key.data.user.identifier, strkey, keylen)) {
585 /* Check to make sure this entry isn't expired by a hard TTL */
586 if((*slot)->value->data.user.ttl && ((*slot)->creation_time + (*slot)->value->data.user.ttl) < t) {
587 remove_slot(cache, slot);
591 /* Otherwise we are fine, increase counters and return the cache entry */
593 (*slot)->value->ref_count++;
594 (*slot)->access_time = t;
596 cache->header->num_hits++;
597 value = (*slot)->value;
599 return (apc_cache_entry_t*)value;
601 slot = &(*slot)->next;
609 /* {{{ apc_cache_user_delete */
610 int apc_cache_user_delete(apc_cache_t* cache, char *strkey, int keylen)
616 slot = &cache->slots[string_nhash_8(strkey, keylen) % cache->num_slots];
619 if (!memcmp((*slot)->key.data.user.identifier, strkey, keylen)) {
620 remove_slot(cache, slot);
624 slot = &(*slot)->next;
632 /* {{{ apc_cache_release */
633 void apc_cache_release(apc_cache_t* cache, apc_cache_entry_t* entry)
641 /* {{{ apc_cache_make_file_key */
642 int apc_cache_make_file_key(apc_cache_key_t* key,
643 const char* filename,
644 const char* include_path,
648 struct stat *tmp_buf=NULL;
649 struct apc_fileinfo_t fileinfo = { {0}, };
654 if (!filename || !SG(request_info).path_translated) {
656 fprintf(stderr,"No filename and no path_translated - bailing\n");
661 len = strlen(filename);
662 if(APCG(fpstat)==0) {
663 if(IS_ABSOLUTE_PATH(filename,len)) {
664 key->data.fpfile.fullpath = filename;
665 key->data.fpfile.fullpath_len = len;
667 key->type = APC_CACHE_KEY_FPFILE;
669 if (apc_search_paths(filename, include_path, &fileinfo) != 0) {
670 apc_wprint("apc failed to locate %s - bailing", filename);
674 if(!realpath(fileinfo.fullpath, APCG(canon_path))) {
675 apc_wprint("realpath failed to canonicalize %s - bailing", filename);
679 key->data.fpfile.fullpath = APCG(canon_path);
680 key->data.fpfile.fullpath_len = strlen(APCG(canon_path));
682 key->type = APC_CACHE_KEY_FPFILE;
687 if(!strcmp(SG(request_info).path_translated, filename)) {
688 tmp_buf = sapi_get_stat(TSRMLS_C); /* Apache has already done this stat() for us */
691 fileinfo.st_buf.sb = *tmp_buf;
693 if (apc_search_paths(filename, include_path, &fileinfo) != 0) {
695 fprintf(stderr,"Stat failed %s - bailing (%s) (%d)\n",filename,SG(request_info).path_translated);
701 if(APCG(max_file_size) < fileinfo.st_buf.sb.st_size) {
703 fprintf(stderr,"File is too big %s (%d - %ld) - bailing\n",filename,t,fileinfo.st_buf.sb.st_size);
709 * This is a bit of a hack.
711 * Here I am checking to see if the file is at least 2 seconds old.
712 * The idea is that if the file is currently being written to then its
713 * mtime is going to match or at most be 1 second off of the current
714 * request time and we want to avoid caching files that have not been
715 * completely written. Of course, people should be using atomic
716 * mechanisms to push files onto live web servers, but adding this
717 * tiny safety is easier than educating the world. This is now
718 * configurable, but the default is still 2 seconds.
720 if(APCG(file_update_protection) && (t - fileinfo.st_buf.sb.st_mtime < APCG(file_update_protection))) {
722 fprintf(stderr,"File is too new %s (%d - %d) - bailing\n",filename,t,fileinfo.st_buf.sb.st_mtime);
727 key->data.file.device = fileinfo.st_buf.sb.st_dev;
728 key->data.file.inode = fileinfo.st_buf.sb.st_ino;
730 * If working with content management systems that like to munge the mtime,
731 * it might be appropriate to key off of the ctime to be immune to systems
732 * that try to backdate a template. If the mtime is set to something older
733 * than the previous mtime of a template we will obviously never see this
734 * "older" template. At some point the Smarty templating system did this.
735 * I generally disagree with using the ctime here because you lose the
736 * ability to warm up new content by saving it to a temporary file, hitting
737 * it once to cache it and then renaming it into its permanent location so
738 * set the apc.stat_ctime=true to enable this check.
740 if(APCG(stat_ctime)) {
741 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;
743 key->mtime = fileinfo.st_buf.sb.st_mtime;
745 key->type = APC_CACHE_KEY_FILE;
750 /* {{{ apc_cache_make_user_key */
751 int apc_cache_make_user_key(apc_cache_key_t* key, char* identifier, int identifier_len, const time_t t)
758 key->data.user.identifier = identifier;
759 key->data.user.identifier_len = identifier_len;
761 key->type = APC_CACHE_KEY_USER;
766 /* {{{ apc_cache_make_file_entry */
767 apc_cache_entry_t* apc_cache_make_file_entry(const char* filename,
768 zend_op_array* op_array,
769 apc_function_t* functions,
770 apc_class_t* classes)
772 apc_cache_entry_t* entry;
774 entry = (apc_cache_entry_t*) apc_sma_malloc(sizeof(apc_cache_entry_t));
775 if (!entry) return NULL;
777 entry->data.file.filename = apc_xstrdup(filename, apc_sma_malloc);
778 if(!entry->data.file.filename) {
780 fprintf(stderr,"apc_cache_make_file_entry: entry->data.file.filename is NULL - bailing\n");
786 fprintf(stderr,"apc_cache_make_file_entry: entry->data.file.filename is [%s]\n",entry->data.file.filename);
788 entry->data.file.op_array = op_array;
789 entry->data.file.functions = functions;
790 entry->data.file.classes = classes;
791 entry->type = APC_CACHE_ENTRY_FILE;
792 entry->ref_count = 0;
798 /* {{{ apc_cache_store_zval */
799 zval* apc_cache_store_zval(zval* dst, const zval* src, apc_malloc_t allocate, apc_free_t deallocate)
802 php_serialize_data_t var_hash;
805 if((src->type & ~IS_CONSTANT_INDEX) == IS_OBJECT) {
807 CHECK(dst = (zval*) allocate(sizeof(zval)));
810 PHP_VAR_SERIALIZE_INIT(var_hash);
811 php_var_serialize(&buf, (zval**)&src, &var_hash TSRMLS_CC);
812 PHP_VAR_SERIALIZE_DESTROY(var_hash);
814 dst->type = IS_NULL; /* in case we fail */
816 dst->type = src->type & ~IS_CONSTANT_INDEX;
817 dst->value.str.len = buf.len;
818 CHECK(dst->value.str.val = apc_xmemcpy(buf.c, buf.len+1, allocate));
819 dst->type = src->type;
820 smart_str_free(&buf);
825 /* Maintain a list of zvals we've copied to properly handle recursive structures */
826 HashTable *old = APCG(copied_zvals);
827 APCG(copied_zvals) = emalloc(sizeof(HashTable));
828 zend_hash_init(APCG(copied_zvals), 0, NULL, NULL, 0);
830 dst = apc_copy_zval(dst, src, allocate, deallocate);
832 if(APCG(copied_zvals)) {
833 zend_hash_destroy(APCG(copied_zvals));
834 efree(APCG(copied_zvals));
837 APCG(copied_zvals) = old;
844 /* {{{ apc_cache_fetch_zval */
845 zval* apc_cache_fetch_zval(zval* dst, const zval* src, apc_malloc_t allocate, apc_free_t deallocate)
848 if((src->type & ~IS_CONSTANT_INDEX) == IS_OBJECT) {
849 php_unserialize_data_t var_hash;
850 const unsigned char *p = (unsigned char*)Z_STRVAL_P(src);
852 PHP_VAR_UNSERIALIZE_INIT(var_hash);
853 if(!php_var_unserialize(&dst, &p, p + Z_STRLEN_P(src), &var_hash TSRMLS_CC)) {
854 PHP_VAR_UNSERIALIZE_DESTROY(var_hash);
856 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));
859 PHP_VAR_UNSERIALIZE_DESTROY(var_hash);
863 /* Maintain a list of zvals we've copied to properly handle recursive structures */
864 HashTable *old = APCG(copied_zvals);
865 APCG(copied_zvals) = emalloc(sizeof(HashTable));
866 zend_hash_init(APCG(copied_zvals), 0, NULL, NULL, 0);
868 dst = apc_copy_zval(dst, src, allocate, deallocate);
870 if(APCG(copied_zvals)) {
871 zend_hash_destroy(APCG(copied_zvals));
872 efree(APCG(copied_zvals));
875 APCG(copied_zvals) = old;
882 /* {{{ apc_cache_free_zval */
883 void apc_cache_free_zval(zval* src, apc_free_t deallocate)
886 if ((src->type & ~IS_CONSTANT_INDEX) == IS_OBJECT) {
887 if (src->value.str.val) {
888 deallocate(src->value.str.val);
892 /* Maintain a list of zvals we've copied to properly handle recursive structures */
893 HashTable *old = APCG(copied_zvals);
894 APCG(copied_zvals) = emalloc(sizeof(HashTable));
895 zend_hash_init(APCG(copied_zvals), 0, NULL, NULL, 0);
897 apc_free_zval(src, deallocate);
899 if(APCG(copied_zvals)) {
900 zend_hash_destroy(APCG(copied_zvals));
901 efree(APCG(copied_zvals));
904 APCG(copied_zvals) = old;
909 /* {{{ apc_cache_make_user_entry */
910 apc_cache_entry_t* apc_cache_make_user_entry(const char* info, int info_len, const zval* val, const unsigned int ttl)
912 apc_cache_entry_t* entry;
914 entry = (apc_cache_entry_t*) apc_sma_malloc(sizeof(apc_cache_entry_t));
915 if (!entry) return NULL;
917 entry->data.user.info = apc_xmemcpy(info, info_len, apc_sma_malloc);
918 entry->data.user.info_len = info_len;
919 if(!entry->data.user.info) {
923 entry->data.user.val = apc_cache_store_zval(NULL, val, apc_sma_malloc, apc_sma_free);
924 if(!entry->data.user.val) {
925 apc_sma_free(entry->data.user.info);
929 INIT_PZVAL(entry->data.user.val);
930 entry->data.user.ttl = ttl;
931 entry->type = APC_CACHE_ENTRY_USER;
932 entry->ref_count = 0;
938 /* {{{ apc_cache_free_entry */
939 void apc_cache_free_entry(apc_cache_entry_t* entry)
942 assert(entry->ref_count == 0);
943 switch(entry->type) {
944 case APC_CACHE_ENTRY_FILE:
945 apc_sma_free(entry->data.file.filename);
946 apc_free_op_array(entry->data.file.op_array, apc_sma_free);
947 apc_free_functions(entry->data.file.functions, apc_sma_free);
948 apc_free_classes(entry->data.file.classes, apc_sma_free);
950 case APC_CACHE_ENTRY_USER:
951 apc_sma_free(entry->data.user.info);
952 apc_cache_free_zval(entry->data.user.val, apc_sma_free);
960 /* {{{ apc_cache_info */
961 apc_cache_info_t* apc_cache_info(apc_cache_t* cache, zend_bool limited)
963 apc_cache_info_t* info;
967 if(!cache) return NULL;
971 info = (apc_cache_info_t*) apc_emalloc(sizeof(apc_cache_info_t));
976 info->num_slots = cache->num_slots;
977 info->ttl = cache->ttl;
978 info->num_hits = cache->header->num_hits;
979 info->num_misses = cache->header->num_misses;
981 info->deleted_list = NULL;
982 info->start_time = cache->header->start_time;
983 info->expunges = cache->header->expunges;
984 info->mem_size = cache->header->mem_size;
985 info->num_entries = cache->header->num_entries;
986 info->num_inserts = cache->header->num_inserts;
989 /* For each hashtable slot */
990 for (i = 0; i < info->num_slots; i++) {
992 for (; p != NULL; p = p->next) {
993 apc_cache_link_t* link = (apc_cache_link_t*) apc_emalloc(sizeof(apc_cache_link_t));
995 if(p->value->type == APC_CACHE_ENTRY_FILE) {
996 link->data.file.filename = apc_xstrdup(p->value->data.file.filename, apc_emalloc);
997 link->data.file.device = p->key.data.file.device;
998 link->data.file.inode = p->key.data.file.inode;
999 link->type = APC_CACHE_ENTRY_FILE;
1000 } else if(p->value->type == APC_CACHE_ENTRY_USER) {
1001 link->data.user.info = apc_xmemcpy(p->value->data.user.info, p->value->data.user.info_len, apc_emalloc);
1002 link->data.user.ttl = p->value->data.user.ttl;
1003 link->type = APC_CACHE_ENTRY_USER;
1005 link->num_hits = p->num_hits;
1006 link->mtime = p->key.mtime;
1007 link->creation_time = p->creation_time;
1008 link->deletion_time = p->deletion_time;
1009 link->access_time = p->access_time;
1010 link->ref_count = p->value->ref_count;
1011 link->mem_size = p->value->mem_size;
1012 link->next = info->list;
1017 /* For each slot pending deletion */
1018 for (p = cache->header->deleted_list; p != NULL; p = p->next) {
1019 apc_cache_link_t* link = (apc_cache_link_t*) apc_emalloc(sizeof(apc_cache_link_t));
1021 if(p->value->type == APC_CACHE_ENTRY_FILE) {
1022 link->data.file.filename = apc_xstrdup(p->value->data.file.filename, apc_emalloc);
1023 if(p->key.type == APC_CACHE_KEY_FILE) {
1024 link->data.file.device = p->key.data.file.device;
1025 link->data.file.inode = p->key.data.file.inode;
1026 } else { /* This is a no-stat fullpath file entry */
1027 link->data.file.device = 0;
1028 link->data.file.inode = 0;
1030 link->type = APC_CACHE_ENTRY_FILE;
1031 } else if(p->value->type == APC_CACHE_ENTRY_USER) {
1032 link->data.user.info = apc_xmemcpy(p->value->data.user.info, p->value->data.user.info_len, apc_emalloc);
1033 link->data.user.ttl = p->value->data.user.ttl;
1034 link->type = APC_CACHE_ENTRY_USER;
1036 link->num_hits = p->num_hits;
1037 link->mtime = p->key.mtime;
1038 link->creation_time = p->creation_time;
1039 link->deletion_time = p->deletion_time;
1040 link->access_time = p->access_time;
1041 link->ref_count = p->value->ref_count;
1042 link->mem_size = p->value->mem_size;
1043 link->next = info->deleted_list;
1044 info->deleted_list = link;
1053 /* {{{ apc_cache_free_info */
1054 void apc_cache_free_info(apc_cache_info_t* info)
1056 apc_cache_link_t* p = info->list;
1057 apc_cache_link_t* q = NULL;
1061 if(q->type == APC_CACHE_ENTRY_FILE) apc_efree(q->data.file.filename);
1062 else if(q->type == APC_CACHE_ENTRY_USER) apc_efree(q->data.user.info);
1065 p = info->deleted_list;
1069 if(q->type == APC_CACHE_ENTRY_FILE) apc_efree(q->data.file.filename);
1070 else if(q->type == APC_CACHE_ENTRY_USER) apc_efree(q->data.user.info);
1077 /* {{{ apc_cache_unlock */
1078 void apc_cache_unlock(apc_cache_t* cache)
1084 /* {{{ apc_cache_busy */
1085 zend_bool apc_cache_busy(apc_cache_t* cache)
1087 return cache->header->busy;
1091 #if NONBLOCKING_LOCK_AVAILABLE
1092 /* {{{ apc_cache_write_lock */
1093 zend_bool apc_cache_write_lock(apc_cache_t* cache)
1095 return apc_lck_nb_lock(cache->header->wrlock);
1099 /* {{{ apc_cache_write_unlock */
1100 void apc_cache_write_unlock(apc_cache_t* cache)
1102 apc_lck_unlock(cache->header->wrlock);
1112 * vim600: expandtab sw=4 ts=4 sts=4 fdm=marker
1113 * vim<600: expandtab sw=4 ts=4 sts=4