2 +----------------------------------------------------------------------+
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2006 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.2 2008/03/25 18:24:57 gopalv 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)
57 static void apc_cache_expunge(apc_cache_t* cache, size_t size);
60 static unsigned int hash(apc_cache_key_t key)
62 return key.data.file.device + key.data.file.inode;
66 /* {{{ string_nhash_8 */
67 static unsigned int string_nhash_8(const char *s, size_t len)
69 register const unsigned int *iv = (const unsigned int *)s;
70 register unsigned int h = 0;
71 register const unsigned int *e = (const unsigned int *)(s + len - (len % sizeof(unsigned int)));
75 h = (h << 7) | (h >> ((8*sizeof(unsigned int)) - 7));
78 for(len %= sizeof(unsigned int);len;len--) {
88 slot_t* make_slot(apc_cache_key_t key, apc_cache_entry_t* value, slot_t* next, time_t t)
90 slot_t* p = apc_sma_malloc(sizeof(slot_t));
93 if(value->type == APC_CACHE_ENTRY_USER) {
94 char *identifier = (char*) apc_xstrdup(key.data.user.identifier, apc_sma_malloc);
99 key.data.user.identifier = identifier;
100 } else if(key.type == APC_CACHE_KEY_FPFILE) {
101 char *fullpath = (char*) apc_xstrdup(key.data.fpfile.fullpath, apc_sma_malloc);
106 key.data.fpfile.fullpath = fullpath;
112 p->creation_time = t;
114 p->deletion_time = 0;
120 static void free_slot(slot_t* slot)
122 if(slot->value->type == APC_CACHE_ENTRY_USER) {
123 apc_sma_free((char *)slot->key.data.user.identifier);
124 } else if(slot->key.type == APC_CACHE_KEY_FPFILE) {
125 apc_sma_free((char *)slot->key.data.fpfile.fullpath);
127 apc_cache_free_entry(slot->value);
132 /* {{{ remove_slot */
133 static void remove_slot(apc_cache_t* cache, slot_t** slot)
135 slot_t* dead = *slot;
136 *slot = (*slot)->next;
138 cache->header->mem_size -= dead->value->mem_size;
139 cache->header->num_entries--;
140 if (dead->value->ref_count <= 0) {
144 dead->next = cache->header->deleted_list;
145 dead->deletion_time = time(0);
146 cache->header->deleted_list = dead;
151 /* {{{ process_pending_removals */
152 static void process_pending_removals(apc_cache_t* cache)
157 /* This function scans the list of removed cache entries and deletes any
158 * entry whose reference count is zero (indicating that it is no longer
159 * being executed) or that has been on the pending list for more than
160 * cache->gc_ttl seconds (we issue a warning in the latter case).
163 if (!cache->header->deleted_list)
166 slot = &cache->header->deleted_list;
169 while (*slot != NULL) {
170 int gc_sec = cache->gc_ttl ? (now - (*slot)->deletion_time) : 0;
172 if ((*slot)->value->ref_count <= 0 || gc_sec > cache->gc_ttl) {
173 slot_t* dead = *slot;
175 if (dead->value->ref_count > 0) {
176 switch(dead->value->type) {
177 case APC_CACHE_ENTRY_FILE:
178 apc_log(APC_WARNING, "GC cache entry '%s' (dev=%d ino=%d) "
179 "was on gc-list for %d seconds", dead->value->data.file.filename,
180 dead->key.data.file.device, dead->key.data.file.inode, gc_sec);
182 case APC_CACHE_ENTRY_USER:
183 apc_log(APC_WARNING, "GC cache entry '%s' "
184 "was on gc-list for %d seconds", dead->value->data.user.info, gc_sec);
192 slot = &(*slot)->next;
198 /* {{{ prevent_garbage_collection */
199 static void prevent_garbage_collection(apc_cache_entry_t* entry)
201 /* set reference counts on zend objects to an arbitrarily high value to
202 * prevent garbage collection after execution */
204 enum { BIG_VALUE = 1000 };
206 if(entry->data.file.op_array) {
207 entry->data.file.op_array->refcount[0] = BIG_VALUE;
209 if (entry->data.file.functions) {
211 apc_function_t* fns = entry->data.file.functions;
212 for (i=0; fns[i].function != NULL; i++) {
213 *(fns[i].function->op_array.refcount) = BIG_VALUE;
216 if (entry->data.file.classes) {
218 apc_class_t* classes = entry->data.file.classes;
219 for (i=0; classes[i].class_entry != NULL; i++) {
221 classes[i].class_entry->refcount = BIG_VALUE;
223 classes[i].class_entry->refcount[0] = BIG_VALUE;
230 /* {{{ apc_cache_create */
231 apc_cache_t* apc_cache_create(int size_hint, int gc_ttl, int ttl)
238 num_slots = size_hint > 0 ? size_hint*2 : 2000;
240 cache = (apc_cache_t*) apc_emalloc(sizeof(apc_cache_t));
241 cache_size = sizeof(cache_header_t) + num_slots*sizeof(slot_t*);
243 cache->shmaddr = apc_sma_malloc(cache_size);
244 if(!cache->shmaddr) {
245 apc_eprint("Unable to allocate shared memory for cache structures. (Perhaps your shared memory size isn't large enough?). ");
247 memset(cache->shmaddr, 0, cache_size);
249 cache->header = (cache_header_t*) cache->shmaddr;
250 cache->header->num_hits = 0;
251 cache->header->num_misses = 0;
252 cache->header->deleted_list = NULL;
253 cache->header->start_time = time(NULL);
254 cache->header->expunges = 0;
255 cache->header->busy = 0;
257 cache->slots = (slot_t**) (((char*) cache->shmaddr) + sizeof(cache_header_t));
258 cache->num_slots = num_slots;
259 cache->gc_ttl = gc_ttl;
261 CREATE_LOCK(cache->header->lock);
262 #if NONBLOCKING_LOCK_AVAILABLE
263 CREATE_LOCK(cache->header->wrlock);
265 for (i = 0; i < num_slots; i++) {
266 cache->slots[i] = NULL;
268 cache->expunge_cb = apc_cache_expunge;
274 /* {{{ apc_cache_destroy */
275 void apc_cache_destroy(apc_cache_t* cache)
282 /* {{{ apc_cache_clear */
283 void apc_cache_clear(apc_cache_t* cache)
290 cache->header->busy = 1;
291 cache->header->num_hits = 0;
292 cache->header->num_misses = 0;
293 cache->header->start_time = time(NULL);
294 cache->header->expunges = 0;
296 for (i = 0; i < cache->num_slots; i++) {
297 slot_t* p = cache->slots[i];
299 remove_slot(cache, &p);
301 cache->slots[i] = NULL;
304 cache->header->busy = 0;
309 /* {{{ apc_cache_expunge */
310 static void apc_cache_expunge(apc_cache_t* cache, size_t size)
316 #if PHP_API_VERSION < 20041225
317 #if HAVE_APACHE && defined(APC_PHP4_STAT)
318 t = ((request_rec *)SG(server_context))->request_time;
323 t = sapi_get_request_time(TSRMLS_C);
330 * If cache->ttl is not set, we wipe out the entire cache when
331 * we run out of space.
334 cache->header->busy = 1;
335 cache->header->expunges++;
336 for (i = 0; i < cache->num_slots; i++) {
337 slot_t* p = cache->slots[i];
339 remove_slot(cache, &p);
341 cache->slots[i] = NULL;
343 cache->header->busy = 0;
349 * If the ttl for the cache is set we walk through and delete stale
350 * entries. For the user cache that is slightly confusing since
351 * we have the individual entry ttl's we can look at, but that would be
352 * too much work. So if you want the user cache expunged, set a high
353 * default apc.user_ttl and still provide a specific ttl for each entry
358 cache->header->busy = 1;
359 cache->header->expunges++;
360 for (i = 0; i < cache->num_slots; i++) {
361 p = &cache->slots[i];
364 * For the user cache we look at the individual entry ttl values
365 * and if not set fall back to the default ttl for the user cache
367 if((*p)->value->type == APC_CACHE_ENTRY_USER) {
368 if((*p)->value->data.user.ttl) {
369 if((*p)->creation_time + (*p)->value->data.user.ttl < t) {
370 remove_slot(cache, p);
373 } else if(cache->ttl) {
374 if((*p)->creation_time + cache->ttl < t) {
375 remove_slot(cache, p);
379 } else if((*p)->access_time < (t - cache->ttl)) {
380 remove_slot(cache, p);
386 cache->header->busy = 0;
392 /* {{{ apc_cache_insert */
393 int apc_cache_insert(apc_cache_t* cache,
395 apc_cache_entry_t* value,
405 fprintf(stderr,"Inserting [%s]\n", value->data.file.filename);
409 process_pending_removals(cache);
411 if(key.type == APC_CACHE_KEY_FILE) slot = &cache->slots[hash(key) % cache->num_slots];
412 else slot = &cache->slots[string_nhash_8(key.data.fpfile.fullpath, key.data.fpfile.fullpath_len) % cache->num_slots];
415 if(key.type == (*slot)->key.type) {
416 if(key.type == APC_CACHE_KEY_FILE) {
417 if(key_equals((*slot)->key.data.file, key.data.file)) {
418 /* If existing slot for the same device+inode is different, remove it and insert the new version */
419 if ((*slot)->key.mtime != key.mtime) {
420 remove_slot(cache, slot);
425 } else if(cache->ttl && (*slot)->access_time < (t - cache->ttl)) {
426 remove_slot(cache, slot);
429 } else { /* APC_CACHE_KEY_FPFILE */
430 if(!memcmp((*slot)->key.data.fpfile.fullpath, key.data.fpfile.fullpath, key.data.fpfile.fullpath_len+1)) {
431 /* Hrm.. it's already here, remove it and insert new one */
432 remove_slot(cache, slot);
434 } else if(cache->ttl && (*slot)->access_time < (t - cache->ttl)) {
435 remove_slot(cache, slot);
440 slot = &(*slot)->next;
443 if ((*slot = make_slot(key, value, *slot, t)) == NULL) {
448 cache->header->mem_size += value->mem_size;
449 cache->header->num_entries++;
450 cache->header->num_inserts++;
457 /* {{{ apc_cache_user_insert */
458 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)
461 size_t* mem_size_ptr = NULL;
468 process_pending_removals(cache);
470 slot = &cache->slots[string_nhash_8(key.data.user.identifier, key.data.user.identifier_len) % cache->num_slots];
472 if (APCG(mem_size_ptr) != NULL) {
473 mem_size_ptr = APCG(mem_size_ptr);
474 APCG(mem_size_ptr) = NULL;
478 if (!memcmp((*slot)->key.data.user.identifier, key.data.user.identifier, key.data.user.identifier_len)) {
480 * At this point we have found the user cache entry. If we are doing
481 * an exclusive insert (apc_add) we are going to bail right away if
482 * the user entry already exists and it has no ttl, or
483 * there is a ttl and the entry has not timed out yet.
485 if(exclusive && ( !(*slot)->value->data.user.ttl ||
486 ( (*slot)->value->data.user.ttl && ((*slot)->creation_time + (*slot)->value->data.user.ttl) >= t )
491 remove_slot(cache, slot);
495 * This is a bit nasty. The idea here is to do runtime cleanup of the linked list of
496 * slot entries so we don't always have to skip past a bunch of stale entries. We check
497 * for staleness here and get rid of them by first checking to see if the cache has a global
498 * access ttl on it and removing entries that haven't been accessed for ttl seconds and secondly
499 * we see if the entry has a hard ttl on it and remove it if it has been around longer than its ttl
501 if((cache->ttl && (*slot)->access_time < (t - cache->ttl)) ||
502 ((*slot)->value->data.user.ttl && ((*slot)->creation_time + (*slot)->value->data.user.ttl) < t)) {
503 remove_slot(cache, slot);
506 slot = &(*slot)->next;
509 if (mem_size_ptr != NULL) {
510 APCG(mem_size_ptr) = mem_size_ptr;
513 if ((*slot = make_slot(key, value, *slot, t)) == NULL) {
517 if (APCG(mem_size_ptr) != NULL) {
518 value->mem_size = *APCG(mem_size_ptr);
519 cache->header->mem_size += *APCG(mem_size_ptr);
521 cache->header->num_entries++;
522 cache->header->num_inserts++;
529 /* {{{ apc_cache_find_slot */
530 slot_t* apc_cache_find_slot(apc_cache_t* cache, apc_cache_key_t key, time_t t)
533 volatile slot_t* retval = NULL;
536 if(key.type == APC_CACHE_KEY_FILE) slot = &cache->slots[hash(key) % cache->num_slots];
537 else slot = &cache->slots[string_nhash_8(key.data.fpfile.fullpath, key.data.fpfile.fullpath_len) % cache->num_slots];
540 if(key.type == (*slot)->key.type) {
541 if(key.type == APC_CACHE_KEY_FILE) {
542 if(key_equals((*slot)->key.data.file, key.data.file)) {
543 if((*slot)->key.mtime != key.mtime) {
544 remove_slot(cache, slot);
545 cache->header->num_misses++;
550 (*slot)->value->ref_count++;
551 (*slot)->access_time = t;
552 prevent_garbage_collection((*slot)->value);
553 cache->header->num_hits++;
556 return (slot_t*)retval;
558 } else { /* APC_CACHE_KEY_FPFILE */
559 if(!memcmp((*slot)->key.data.fpfile.fullpath, key.data.fpfile.fullpath, key.data.fpfile.fullpath_len+1)) {
562 (*slot)->value->ref_count++;
563 (*slot)->access_time = t;
564 prevent_garbage_collection((*slot)->value);
565 cache->header->num_hits++;
568 return (slot_t*)retval;
572 slot = &(*slot)->next;
574 cache->header->num_misses++;
580 /* {{{ apc_cache_find */
581 apc_cache_entry_t* apc_cache_find(apc_cache_t* cache, apc_cache_key_t key, time_t t)
583 slot_t * slot = apc_cache_find_slot(cache, key, t);
584 return (slot) ? slot->value : NULL;
588 /* {{{ apc_cache_user_find */
589 apc_cache_entry_t* apc_cache_user_find(apc_cache_t* cache, char *strkey, int keylen, time_t t)
592 volatile apc_cache_entry_t* value = NULL;
596 slot = &cache->slots[string_nhash_8(strkey, keylen) % cache->num_slots];
599 if (!memcmp((*slot)->key.data.user.identifier, strkey, keylen)) {
600 /* Check to make sure this entry isn't expired by a hard TTL */
601 if((*slot)->value->data.user.ttl && ((*slot)->creation_time + (*slot)->value->data.user.ttl) < t) {
602 remove_slot(cache, slot);
606 /* Otherwise we are fine, increase counters and return the cache entry */
608 (*slot)->value->ref_count++;
609 (*slot)->access_time = t;
611 cache->header->num_hits++;
612 value = (*slot)->value;
614 return (apc_cache_entry_t*)value;
616 slot = &(*slot)->next;
624 /* {{{ apc_cache_user_delete */
625 int apc_cache_user_delete(apc_cache_t* cache, char *strkey, int keylen)
631 slot = &cache->slots[string_nhash_8(strkey, keylen) % cache->num_slots];
634 if (!memcmp((*slot)->key.data.user.identifier, strkey, keylen)) {
635 remove_slot(cache, slot);
639 slot = &(*slot)->next;
647 /* {{{ apc_cache_release */
648 void apc_cache_release(apc_cache_t* cache, apc_cache_entry_t* entry)
656 /* {{{ apc_cache_make_file_key */
657 int apc_cache_make_file_key(apc_cache_key_t* key,
658 const char* filename,
659 const char* include_path,
663 struct stat *tmp_buf=NULL;
664 struct apc_fileinfo_t fileinfo = { {0}, };
669 if (!filename || !SG(request_info).path_translated) {
671 fprintf(stderr,"No filename and no path_translated - bailing\n");
676 len = strlen(filename);
677 if(APCG(fpstat)==0) {
678 if(IS_ABSOLUTE_PATH(filename,len)) {
679 key->data.fpfile.fullpath = filename;
680 key->data.fpfile.fullpath_len = len;
682 key->type = APC_CACHE_KEY_FPFILE;
684 if (apc_search_paths(filename, include_path, &fileinfo) != 0) {
685 apc_wprint("apc failed to locate %s - bailing", filename);
689 if(!realpath(fileinfo.fullpath, APCG(canon_path))) {
690 apc_wprint("realpath failed to canonicalize %s - bailing", filename);
694 key->data.fpfile.fullpath = APCG(canon_path);
695 key->data.fpfile.fullpath_len = strlen(APCG(canon_path));
697 key->type = APC_CACHE_KEY_FPFILE;
702 if(!strcmp(SG(request_info).path_translated, filename)) {
703 tmp_buf = sapi_get_stat(TSRMLS_C); /* Apache has already done this stat() for us */
706 fileinfo.st_buf.sb = *tmp_buf;
708 if (apc_search_paths(filename, include_path, &fileinfo) != 0) {
710 fprintf(stderr,"Stat failed %s - bailing (%s) (%d)\n",filename,SG(request_info).path_translated);
716 if(APCG(max_file_size) < fileinfo.st_buf.sb.st_size) {
718 fprintf(stderr,"File is too big %s (%d - %ld) - bailing\n",filename,t,fileinfo.st_buf.sb.st_size);
724 * This is a bit of a hack.
726 * Here I am checking to see if the file is at least 2 seconds old.
727 * The idea is that if the file is currently being written to then its
728 * mtime is going to match or at most be 1 second off of the current
729 * request time and we want to avoid caching files that have not been
730 * completely written. Of course, people should be using atomic
731 * mechanisms to push files onto live web servers, but adding this
732 * tiny safety is easier than educating the world. This is now
733 * configurable, but the default is still 2 seconds.
735 if(APCG(file_update_protection) && (t - fileinfo.st_buf.sb.st_mtime < APCG(file_update_protection))) {
737 fprintf(stderr,"File is too new %s (%d - %d) - bailing\n",filename,t,fileinfo.st_buf.sb.st_mtime);
742 key->data.file.device = fileinfo.st_buf.sb.st_dev;
743 key->data.file.inode = fileinfo.st_buf.sb.st_ino;
745 * If working with content management systems that like to munge the mtime,
746 * it might be appropriate to key off of the ctime to be immune to systems
747 * that try to backdate a template. If the mtime is set to something older
748 * than the previous mtime of a template we will obviously never see this
749 * "older" template. At some point the Smarty templating system did this.
750 * I generally disagree with using the ctime here because you lose the
751 * ability to warm up new content by saving it to a temporary file, hitting
752 * it once to cache it and then renaming it into its permanent location so
753 * set the apc.stat_ctime=true to enable this check.
755 if(APCG(stat_ctime)) {
756 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;
758 key->mtime = fileinfo.st_buf.sb.st_mtime;
760 key->type = APC_CACHE_KEY_FILE;
765 /* {{{ apc_cache_make_user_key */
766 int apc_cache_make_user_key(apc_cache_key_t* key, char* identifier, int identifier_len, const time_t t)
773 key->data.user.identifier = identifier;
774 key->data.user.identifier_len = identifier_len;
776 key->type = APC_CACHE_KEY_USER;
781 /* {{{ apc_cache_make_file_entry */
782 apc_cache_entry_t* apc_cache_make_file_entry(const char* filename,
783 zend_op_array* op_array,
784 apc_function_t* functions,
785 apc_class_t* classes)
787 apc_cache_entry_t* entry;
789 entry = (apc_cache_entry_t*) apc_sma_malloc(sizeof(apc_cache_entry_t));
790 if (!entry) return NULL;
792 entry->data.file.filename = apc_xstrdup(filename, apc_sma_malloc);
793 if(!entry->data.file.filename) {
795 fprintf(stderr,"apc_cache_make_file_entry: entry->data.file.filename is NULL - bailing\n");
801 fprintf(stderr,"apc_cache_make_file_entry: entry->data.file.filename is [%s]\n",entry->data.file.filename);
803 entry->data.file.op_array = op_array;
804 entry->data.file.functions = functions;
805 entry->data.file.classes = classes;
806 entry->type = APC_CACHE_ENTRY_FILE;
807 entry->ref_count = 0;
813 /* {{{ apc_cache_store_zval */
814 zval* apc_cache_store_zval(zval* dst, const zval* src, apc_malloc_t allocate, apc_free_t deallocate)
817 php_serialize_data_t var_hash;
820 if((src->type & ~IS_CONSTANT_INDEX) == IS_OBJECT) {
822 CHECK(dst = (zval*) allocate(sizeof(zval)));
825 PHP_VAR_SERIALIZE_INIT(var_hash);
826 php_var_serialize(&buf, (zval**)&src, &var_hash TSRMLS_CC);
827 PHP_VAR_SERIALIZE_DESTROY(var_hash);
829 dst->type = IS_NULL; /* in case we fail */
831 dst->type = src->type & ~IS_CONSTANT_INDEX;
832 dst->value.str.len = buf.len;
833 CHECK(dst->value.str.val = apc_xmemcpy(buf.c, buf.len+1, allocate));
834 dst->type = src->type;
835 smart_str_free(&buf);
840 /* Maintain a list of zvals we've copied to properly handle recursive structures */
841 HashTable *old = APCG(copied_zvals);
842 APCG(copied_zvals) = emalloc(sizeof(HashTable));
843 zend_hash_init(APCG(copied_zvals), 0, NULL, NULL, 0);
845 dst = apc_copy_zval(dst, src, allocate, deallocate);
847 if(APCG(copied_zvals)) {
848 zend_hash_destroy(APCG(copied_zvals));
849 efree(APCG(copied_zvals));
852 APCG(copied_zvals) = old;
859 /* {{{ apc_cache_fetch_zval */
860 zval* apc_cache_fetch_zval(zval* dst, const zval* src, apc_malloc_t allocate, apc_free_t deallocate)
863 if((src->type & ~IS_CONSTANT_INDEX) == IS_OBJECT) {
864 php_unserialize_data_t var_hash;
865 const unsigned char *p = (unsigned char*)Z_STRVAL_P(src);
867 PHP_VAR_UNSERIALIZE_INIT(var_hash);
868 if(!php_var_unserialize(&dst, &p, p + Z_STRLEN_P(src), &var_hash TSRMLS_CC)) {
869 PHP_VAR_UNSERIALIZE_DESTROY(var_hash);
871 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));
874 PHP_VAR_UNSERIALIZE_DESTROY(var_hash);
878 /* Maintain a list of zvals we've copied to properly handle recursive structures */
879 HashTable *old = APCG(copied_zvals);
880 APCG(copied_zvals) = emalloc(sizeof(HashTable));
881 zend_hash_init(APCG(copied_zvals), 0, NULL, NULL, 0);
883 dst = apc_copy_zval(dst, src, allocate, deallocate);
885 if(APCG(copied_zvals)) {
886 zend_hash_destroy(APCG(copied_zvals));
887 efree(APCG(copied_zvals));
890 APCG(copied_zvals) = old;
897 /* {{{ apc_cache_free_zval */
898 void apc_cache_free_zval(zval* src, apc_free_t deallocate)
901 if ((src->type & ~IS_CONSTANT_INDEX) == IS_OBJECT) {
902 if (src->value.str.val) {
903 deallocate(src->value.str.val);
907 /* Maintain a list of zvals we've copied to properly handle recursive structures */
908 HashTable *old = APCG(copied_zvals);
909 APCG(copied_zvals) = emalloc(sizeof(HashTable));
910 zend_hash_init(APCG(copied_zvals), 0, NULL, NULL, 0);
912 apc_free_zval(src, deallocate);
914 if(APCG(copied_zvals)) {
915 zend_hash_destroy(APCG(copied_zvals));
916 efree(APCG(copied_zvals));
919 APCG(copied_zvals) = old;
924 /* {{{ apc_cache_make_user_entry */
925 apc_cache_entry_t* apc_cache_make_user_entry(const char* info, int info_len, const zval* val, const unsigned int ttl)
927 apc_cache_entry_t* entry;
929 entry = (apc_cache_entry_t*) apc_sma_malloc(sizeof(apc_cache_entry_t));
930 if (!entry) return NULL;
932 entry->data.user.info = apc_xmemcpy(info, info_len, apc_sma_malloc);
933 entry->data.user.info_len = info_len;
934 if(!entry->data.user.info) {
938 entry->data.user.val = apc_cache_store_zval(NULL, val, apc_sma_malloc, apc_sma_free);
939 if(!entry->data.user.val) {
940 apc_sma_free(entry->data.user.info);
944 INIT_PZVAL(entry->data.user.val);
945 entry->data.user.ttl = ttl;
946 entry->type = APC_CACHE_ENTRY_USER;
947 entry->ref_count = 0;
953 /* {{{ apc_cache_free_entry */
954 void apc_cache_free_entry(apc_cache_entry_t* entry)
957 assert(entry->ref_count == 0);
958 switch(entry->type) {
959 case APC_CACHE_ENTRY_FILE:
960 apc_sma_free(entry->data.file.filename);
961 apc_free_op_array(entry->data.file.op_array, apc_sma_free);
962 apc_free_functions(entry->data.file.functions, apc_sma_free);
963 apc_free_classes(entry->data.file.classes, apc_sma_free);
965 case APC_CACHE_ENTRY_USER:
966 apc_sma_free(entry->data.user.info);
967 apc_cache_free_zval(entry->data.user.val, apc_sma_free);
975 /* {{{ apc_cache_info */
976 apc_cache_info_t* apc_cache_info(apc_cache_t* cache, zend_bool limited)
978 apc_cache_info_t* info;
982 if(!cache) return NULL;
986 info = (apc_cache_info_t*) apc_emalloc(sizeof(apc_cache_info_t));
991 info->num_slots = cache->num_slots;
992 info->ttl = cache->ttl;
993 info->num_hits = cache->header->num_hits;
994 info->num_misses = cache->header->num_misses;
996 info->deleted_list = NULL;
997 info->start_time = cache->header->start_time;
998 info->expunges = cache->header->expunges;
999 info->mem_size = cache->header->mem_size;
1000 info->num_entries = cache->header->num_entries;
1001 info->num_inserts = cache->header->num_inserts;
1004 /* For each hashtable slot */
1005 for (i = 0; i < info->num_slots; i++) {
1006 p = cache->slots[i];
1007 for (; p != NULL; p = p->next) {
1008 apc_cache_link_t* link = (apc_cache_link_t*) apc_emalloc(sizeof(apc_cache_link_t));
1010 if(p->value->type == APC_CACHE_ENTRY_FILE) {
1011 link->data.file.filename = apc_xstrdup(p->value->data.file.filename, apc_emalloc);
1012 link->data.file.device = p->key.data.file.device;
1013 link->data.file.inode = p->key.data.file.inode;
1014 link->type = APC_CACHE_ENTRY_FILE;
1015 } else if(p->value->type == APC_CACHE_ENTRY_USER) {
1016 link->data.user.info = apc_xmemcpy(p->value->data.user.info, p->value->data.user.info_len, apc_emalloc);
1017 link->data.user.ttl = p->value->data.user.ttl;
1018 link->type = APC_CACHE_ENTRY_USER;
1020 link->num_hits = p->num_hits;
1021 link->mtime = p->key.mtime;
1022 link->creation_time = p->creation_time;
1023 link->deletion_time = p->deletion_time;
1024 link->access_time = p->access_time;
1025 link->ref_count = p->value->ref_count;
1026 link->mem_size = p->value->mem_size;
1027 link->next = info->list;
1032 /* For each slot pending deletion */
1033 for (p = cache->header->deleted_list; p != NULL; p = p->next) {
1034 apc_cache_link_t* link = (apc_cache_link_t*) apc_emalloc(sizeof(apc_cache_link_t));
1036 if(p->value->type == APC_CACHE_ENTRY_FILE) {
1037 link->data.file.filename = apc_xstrdup(p->value->data.file.filename, apc_emalloc);
1038 if(p->key.type == APC_CACHE_KEY_FILE) {
1039 link->data.file.device = p->key.data.file.device;
1040 link->data.file.inode = p->key.data.file.inode;
1041 } else { /* This is a no-stat fullpath file entry */
1042 link->data.file.device = 0;
1043 link->data.file.inode = 0;
1045 link->type = APC_CACHE_ENTRY_FILE;
1046 } else if(p->value->type == APC_CACHE_ENTRY_USER) {
1047 link->data.user.info = apc_xmemcpy(p->value->data.user.info, p->value->data.user.info_len, apc_emalloc);
1048 link->data.user.ttl = p->value->data.user.ttl;
1049 link->type = APC_CACHE_ENTRY_USER;
1051 link->num_hits = p->num_hits;
1052 link->mtime = p->key.mtime;
1053 link->creation_time = p->creation_time;
1054 link->deletion_time = p->deletion_time;
1055 link->access_time = p->access_time;
1056 link->ref_count = p->value->ref_count;
1057 link->mem_size = p->value->mem_size;
1058 link->next = info->deleted_list;
1059 info->deleted_list = link;
1068 /* {{{ apc_cache_free_info */
1069 void apc_cache_free_info(apc_cache_info_t* info)
1071 apc_cache_link_t* p = info->list;
1072 apc_cache_link_t* q = NULL;
1076 if(q->type == APC_CACHE_ENTRY_FILE) apc_efree(q->data.file.filename);
1077 else if(q->type == APC_CACHE_ENTRY_USER) apc_efree(q->data.user.info);
1080 p = info->deleted_list;
1084 if(q->type == APC_CACHE_ENTRY_FILE) apc_efree(q->data.file.filename);
1085 else if(q->type == APC_CACHE_ENTRY_USER) apc_efree(q->data.user.info);
1092 /* {{{ apc_cache_unlock */
1093 void apc_cache_unlock(apc_cache_t* cache)
1099 /* {{{ apc_cache_busy */
1100 zend_bool apc_cache_busy(apc_cache_t* cache)
1102 return cache->header->busy;
1106 #if NONBLOCKING_LOCK_AVAILABLE
1107 /* {{{ apc_cache_write_lock */
1108 zend_bool apc_cache_write_lock(apc_cache_t* cache)
1110 return apc_lck_nb_lock(cache->header->wrlock);
1114 /* {{{ apc_cache_write_unlock */
1115 void apc_cache_write_unlock(apc_cache_t* cache)
1117 apc_lck_unlock(cache->header->wrlock);
1127 * vim600: expandtab sw=4 ts=4 sts=4 fdm=marker
1128 * vim<600: expandtab sw=4 ts=4 sts=4