2
+----------------------------------------------------------------------+
4
+----------------------------------------------------------------------+
5
| Copyright (c) 2006-2011 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 328743 2012-12-12 07:58:32Z ab $ */
33
#include "apc_cache.h"
35
#include "apc_globals.h"
36
#include "php_scandir.h"
40
#include "ext/standard/md5.h"
41
#include "ext/standard/php_var.h"
42
#include "ext/standard/php_smart_str.h"
44
typedef void* (*ht_copy_fun_t)(void*, void*, apc_context_t* TSRMLS_DC);
45
typedef int (*ht_check_copy_fun_t)(Bucket*, va_list);
47
#define CHECK(p) { if ((p) == NULL) return NULL; }
49
static APC_HOTSPOT zval* my_copy_zval(zval* dst, const zval* src, apc_context_t* ctxt TSRMLS_DC);
50
static HashTable* my_copy_hashtable_ex(HashTable*, HashTable* TSRMLS_DC, ht_copy_fun_t, int, apc_context_t*, ht_check_copy_fun_t, ...);
51
#define my_copy_hashtable( dst, src, copy_fn, holds_ptr, ctxt) \
52
my_copy_hashtable_ex(dst, src TSRMLS_CC, copy_fn, holds_ptr, ctxt, NULL)
55
static int const primes[] = {
80
static int make_prime(int n)
82
int *k = (int*)primes;
84
if((*k) > n) return *k;
92
apc_cache_slot_t* make_slot(apc_cache_t* cache, apc_cache_key_t *key, apc_cache_entry_t* value, apc_cache_slot_t* next, time_t t TSRMLS_DC)
94
apc_cache_slot_t* p = NULL;
97
if ((p = value->pool->palloc(value->pool, sizeof(apc_cache_slot_t) TSRMLS_CC))) {
100
char* strkey = (char*) apc_pmemcpy(
102
value->pool TSRMLS_CC
113
/* set slot relation */
116
/* set slot defaults */
129
static void free_slot(apc_cache_slot_t* slot TSRMLS_DC)
131
/* destroy slot pool */
133
slot->value->pool TSRMLS_CC);
137
/* {{{ apc_cache_hash_slot
138
Note: These calculations can and should be done outside of a lock */
139
static void apc_cache_hash_slot(apc_cache_t* cache,
144
(*hash) = zend_inline_hash_func(str, len);
145
(*slot) = (*hash) % (cache->nslots);
148
/* {{{ apc_cache_remove_slot */
149
PHP_APCU_API void apc_cache_remove_slot(apc_cache_t* cache, apc_cache_slot_t** slot TSRMLS_DC)
151
apc_cache_slot_t* dead = *slot;
153
/* think here is safer */
154
*slot = (*slot)->next;
156
/* adjust header info */
157
if (cache->header->mem_size)
158
cache->header->mem_size -= dead->value->mem_size;
160
if (cache->header->nentries)
161
cache->header->nentries--;
163
/* remove if there are no references */
164
if (dead->value->ref_count <= 0) {
165
free_slot(dead TSRMLS_CC);
167
/* add to gc if there are still refs */
168
dead->next = cache->header->gc;
169
dead->dtime = time(0);
170
cache->header->gc = dead;
175
/* {{{ apc_cache_gc */
176
PHP_APCU_API void apc_cache_gc(apc_cache_t* cache TSRMLS_DC)
178
/* This function scans the list of removed cache entries and deletes any
179
* entry whose reference count is zero or that has been on the gc
180
* list for more than cache->gc_ttl seconds
181
* (we issue a warning in the latter case).
183
if (!cache || !cache->header->gc) {
188
apc_cache_slot_t** slot = &cache->header->gc;
190
while (*slot != NULL) {
191
time_t now = time(0);
192
time_t gc_sec = cache->gc_ttl ? (now - (*slot)->dtime) : 0;
194
if (!(*slot)->value->ref_count || gc_sec > (time_t)cache->gc_ttl) {
195
apc_cache_slot_t* dead = *slot;
197
/* good ol' whining */
198
if (dead->value->ref_count > 0) {
200
"GC cache entry '%s' was on gc-list for %d seconds" TSRMLS_CC,
201
dead->key.str, gc_sec
216
slot = &(*slot)->next;
223
/* {{{ php serializer */
224
PHP_APCU_API int APC_SERIALIZER_NAME(php) (APC_SERIALIZER_ARGS)
226
smart_str strbuf = {0};
227
php_serialize_data_t var_hash;
228
PHP_VAR_SERIALIZE_INIT(var_hash);
229
php_var_serialize(&strbuf, (zval**)&value, &var_hash TSRMLS_CC);
230
PHP_VAR_SERIALIZE_DESTROY(var_hash);
232
*buf = (unsigned char*)strbuf.c;
233
*buf_len = strbuf.len;
234
smart_str_0(&strbuf);
240
/* {{{ php unserializer */
241
PHP_APCU_API int APC_UNSERIALIZER_NAME(php) (APC_UNSERIALIZER_ARGS)
243
const unsigned char *tmp = buf;
244
php_unserialize_data_t var_hash;
245
PHP_VAR_UNSERIALIZE_INIT(var_hash);
246
if(!php_var_unserialize(value, &tmp, buf + buf_len, &var_hash TSRMLS_CC)) {
247
PHP_VAR_UNSERIALIZE_DESTROY(var_hash);
249
php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Error at offset %ld of %ld bytes", (long)(tmp - buf), (long)buf_len);
250
(*value)->type = IS_NULL;
253
PHP_VAR_UNSERIALIZE_DESTROY(var_hash);
257
/* {{{ apc_cache_create */
258
PHP_APCU_API apc_cache_t* apc_cache_create(apc_sma_t* sma, apc_serializer_t* serializer, int size_hint, int gc_ttl, int ttl, long smart, zend_bool defend TSRMLS_DC) {
263
/* calculate number of slots */
264
nslots = make_prime(size_hint > 0 ? size_hint : 2000);
266
/* allocate pointer by normal means */
267
cache = (apc_cache_t*) apc_emalloc(sizeof(apc_cache_t) TSRMLS_CC);
269
/* calculate cache size for shm allocation */
270
cache_size = sizeof(apc_cache_header_t) + nslots*sizeof(apc_cache_slot_t*);
273
cache->shmaddr = sma->smalloc(cache_size TSRMLS_CC);
274
if(!cache->shmaddr) {
275
apc_error("Unable to allocate shared memory for cache structures. (Perhaps your shared memory size isn't large enough?). " TSRMLS_CC);
280
memset(cache->shmaddr, 0, cache_size);
282
/* set default header */
283
cache->header = (apc_cache_header_t*) cache->shmaddr;
285
cache->header->nhits = 0;
286
cache->header->nmisses = 0;
287
cache->header->nentries = 0;
288
cache->header->nexpunges = 0;
289
cache->header->gc = NULL;
290
cache->header->stime = time(NULL);
291
cache->header->state |= APC_CACHE_ST_NONE;
293
/* set cache options */
294
cache->slots = (apc_cache_slot_t**) (((char*) cache->shmaddr) + sizeof(apc_cache_header_t));
296
cache->serializer = serializer;
297
cache->nslots = nslots;
298
cache->gc_ttl = gc_ttl;
300
cache->smart = smart;
301
cache->defend = defend;
304
CREATE_LOCK(&cache->header->lock);
307
memset(cache->slots, 0, sizeof(apc_cache_slot_t*)*nslots);
312
/* {{{ apc_cache_store */
313
PHP_APCU_API zend_bool apc_cache_store(apc_cache_t* cache, char *strkey, zend_uint keylen, const zval *val, const zend_uint ttl, const zend_bool exclusive TSRMLS_DC) {
314
apc_cache_entry_t *entry;
317
apc_context_t ctxt={0,};
322
/* initialize a context suitable for making an insert */
323
if (apc_cache_make_context(cache, &ctxt, APC_CONTEXT_SHARE, APC_SMALL_POOL, APC_COPY_IN, 0 TSRMLS_CC)) {
325
/* initialize the key for insertion */
326
if (apc_cache_make_key(&key, strkey, keylen TSRMLS_CC)) {
328
/* run cache defense */
329
if (!apc_cache_defense(cache, &key TSRMLS_CC)) {
331
/* initialize the entry for insertion */
332
if ((entry = apc_cache_make_entry(&ctxt, &key, val, ttl TSRMLS_CC))) {
334
/* execute an insertion */
335
if (apc_cache_insert(cache, key, entry, &ctxt, t, exclusive TSRMLS_CC)) {
342
/* in any case of failure the context should be destroyed */
344
apc_cache_destroy_context(&ctxt TSRMLS_CC);
351
/* {{{ data_unserialize */
352
static zval* data_unserialize(const char *filename TSRMLS_DC)
357
char *contents, *tmp;
359
php_unserialize_data_t var_hash = {0,};
361
if(VCWD_STAT(filename, &sb) == -1) {
365
fp = fopen(filename, "rb");
367
len = sizeof(char)*sb.st_size;
369
tmp = contents = malloc(len);
376
if(fread(contents, 1, len, fp) < 1) {
382
MAKE_STD_ZVAL(retval);
384
PHP_VAR_UNSERIALIZE_INIT(var_hash);
386
/* I wish I could use json */
387
if(!php_var_unserialize(&retval, (const unsigned char**)&tmp, (const unsigned char*)(contents+len), &var_hash TSRMLS_CC)) {
389
zval_ptr_dtor(&retval);
393
PHP_VAR_UNSERIALIZE_DESTROY(var_hash);
401
static int apc_load_data(apc_cache_t* cache, const char *data_file TSRMLS_DC)
404
char key[MAXPATHLEN] = {0,};
405
unsigned int key_len;
408
p = strrchr(data_file, DEFAULT_SLASH);
411
strlcpy(key, p+1, sizeof(key));
412
p = strrchr(key, '.');
416
key_len = strlen(key)+1;
418
data = data_unserialize(data_file TSRMLS_CC);
420
apc_cache_store(cache, key, key_len, data, 0, 1 TSRMLS_CC);
429
/* {{{ apc_cache_preload shall load the prepared data files in path into the specified cache */
430
PHP_APCU_API zend_bool apc_cache_preload(apc_cache_t* cache, const char *path TSRMLS_DC)
433
zend_bool result = 0;
434
char file[MAXPATHLEN]={0,};
437
struct dirent **namelist = NULL;
439
if ((ndir = php_scandir(path, &namelist, 0, php_alphasort)) > 0) {
440
for (i = 0; i < ndir; i++) {
441
/* check for extension */
442
if (!(p = strrchr(namelist[i]->d_name, '.'))
443
|| (p && strcmp(p, ".data"))) {
448
snprintf(file, MAXPATHLEN, "%s%c%s",
449
path, DEFAULT_SLASH, namelist[i]->d_name);
451
if(apc_load_data(cache, file TSRMLS_CC)) {
460
apc_error("Cannot load data from apc.preload_path=%s in thread-safe mode" TSRMLS_CC, path);
465
/* {{{ apc_cache_release */
466
PHP_APCU_API void apc_cache_release(apc_cache_t* cache, apc_cache_entry_t* entry TSRMLS_DC)
472
/* {{{ apc_cache_destroy */
473
PHP_APCU_API void apc_cache_destroy(apc_cache_t* cache TSRMLS_DC)
480
DESTROY_LOCK(&cache->header->lock);
482
/* XXX this is definitely a leak, but freeing this causes all the apache
483
children to freeze. It might be because the segment is shared between
484
several processes. To figure out is how to free this safely. */
485
/*apc_sma_free(cache->shmaddr);*/
486
apc_efree(cache TSRMLS_CC);
490
/* {{{ apc_cache_real_expunge */
491
PHP_APCU_API void apc_cache_real_expunge(apc_cache_t* cache TSRMLS_DC) {
492
/* increment counter */
493
cache->header->nexpunges++;
499
for (i = 0; i < cache->nslots; i++) {
500
apc_cache_slot_t* p = cache->slots[i];
502
apc_cache_remove_slot(cache, &p TSRMLS_CC);
504
cache->slots[i] = NULL;
508
/* set new time so counters make sense */
509
cache->header->stime = apc_time();
512
cache->header->ninserts = 0;
513
cache->header->nentries = 0;
514
cache->header->nhits = 0;
515
cache->header->nmisses = 0;
518
memset(&cache->header->lastkey, 0, sizeof(apc_cache_key_t));
521
/* {{{ apc_cache_clear */
522
PHP_APCU_API void apc_cache_clear(apc_cache_t* cache TSRMLS_DC)
524
/* check there is a cache and it is not busy */
525
if(!cache || apc_cache_busy(cache TSRMLS_CC)) {
530
APC_LOCK(cache->header);
533
cache->header->state |= APC_CACHE_ST_BUSY;
536
apc_cache_real_expunge(cache TSRMLS_CC);
539
cache->header->stime = apc_time();
540
cache->header->nexpunges = 0;
543
cache->header->state &= ~APC_CACHE_ST_BUSY;
546
APC_UNLOCK(cache->header);
550
/* {{{ apc_cache_default_expunge */
551
PHP_APCU_API void apc_cache_default_expunge(apc_cache_t* cache, size_t size TSRMLS_DC)
554
size_t suitable = 0L;
555
size_t available = 0L;
559
/* check there is a cache, and it is not busy */
560
if(!cache || apc_cache_busy(cache TSRMLS_CC)) {
564
/* get the lock for header */
565
APC_LOCK(cache->header);
567
/* update state in header */
568
cache->header->state |= APC_CACHE_ST_BUSY;
570
/* make suitable selection */
571
suitable = (cache->smart > 0L) ? (size_t) (cache->smart * size) : (size_t) (cache->sma->size/2);
574
apc_cache_gc(cache TSRMLS_CC);
577
available = cache->sma->get_avail_mem();
579
/* perform expunge processing */
582
/* check it is necessary to expunge */
583
if (available < suitable) {
584
apc_cache_real_expunge(cache TSRMLS_CC);
587
apc_cache_slot_t **slot;
589
/* check that expunge is necessary */
590
if (available < suitable) {
594
for (i = 0; i < cache->nslots; i++) {
595
slot = &cache->slots[i];
598
* Entry TTL has precedence over cache TTL
600
if((*slot)->value->ttl) {
601
if((time_t) ((*slot)->ctime + (*slot)->value->ttl) < t) {
602
apc_cache_remove_slot(cache, slot TSRMLS_CC);
605
} else if(cache->ttl) {
606
if((time_t) ((*slot)->ctime + cache->ttl) < t) {
607
apc_cache_remove_slot(cache, slot TSRMLS_CC);
613
slot = &(*slot)->next;
617
/* if the cache now has space, then reset last key */
618
if (cache->sma->get_avail_size(size)) {
620
memset(&cache->header->lastkey, 0, sizeof(apc_cache_key_t));
622
/* with not enough space left in cache, we are forced to expunge */
623
apc_cache_real_expunge(cache TSRMLS_CC);
629
cache->header->state &= ~APC_CACHE_ST_BUSY;
632
APC_UNLOCK(cache->header);
636
/* {{{ apc_cache_make_context */
637
PHP_APCU_API zend_bool apc_cache_make_context(apc_cache_t* cache,
638
apc_context_t* context,
639
apc_context_type context_type,
640
apc_pool_type pool_type,
641
apc_copy_type copy_type,
642
uint force_update TSRMLS_DC) {
643
switch (context_type) {
644
case APC_CONTEXT_SHARE: {
645
return apc_cache_make_context_ex(
648
(apc_malloc_t) cache->sma->smalloc,
651
cache->sma->unprotect,
652
pool_type, copy_type, force_update TSRMLS_CC
656
case APC_CONTEXT_NOSHARE: {
657
return apc_cache_make_context_ex(
660
apc_php_malloc, apc_php_free, NULL, NULL,
661
pool_type, copy_type, force_update TSRMLS_CC
665
case APC_CONTEXT_NONE:
666
/* never used, just to make gcc warning free */
673
/* {{{ apc_cache_make_context_ex */
674
PHP_APCU_API zend_bool apc_cache_make_context_ex(apc_context_t* context,
675
apc_serializer_t* serializer,
676
apc_malloc_t _malloc,
678
apc_protect_t _protect,
679
apc_unprotect_t _unprotect,
680
apc_pool_type pool_type,
681
apc_copy_type copy_type,
682
uint force_update TSRMLS_DC) {
683
/* attempt to create the pool */
684
context->pool = apc_pool_create(
685
pool_type, _malloc, _free, _protect, _unprotect TSRMLS_CC
688
if (!context->pool) {
689
apc_warning("Unable to allocate memory for pool." TSRMLS_CC);
693
/* set context information */
694
context->serializer = serializer;
695
context->copy = copy_type;
696
context->force_update = force_update;
698
/* set this to avoid memory errors */
699
memset(&context->copied, 0, sizeof(HashTable));
704
/* {{{ apc_context_destroy */
705
PHP_APCU_API zend_bool apc_cache_destroy_context(apc_context_t* context TSRMLS_DC) {
706
if (!context->pool) {
710
apc_pool_destroy(context->pool TSRMLS_CC);
715
/* {{{ apc_cache_insert */
716
PHP_APCU_API zend_bool apc_cache_insert(apc_cache_t* cache,
718
apc_cache_entry_t* value,
721
zend_bool exclusive TSRMLS_DC)
723
zend_bool result = 0;
730
/* check we are able to deal with this request */
731
if (!cache || apc_cache_busy(cache TSRMLS_CC)) {
736
APC_LOCK(cache->header);
738
/* process deleted list */
739
apc_cache_gc(cache TSRMLS_CC);
741
/* make the insertion */
743
apc_cache_slot_t** slot;
746
* select appropriate slot ...
748
slot = &cache->slots[key.h % cache->nslots];
752
/* check for a match by hash and string */
753
if (((*slot)->key.h == key.h) && (!memcmp((*slot)->key.str, key.str, key.len))) {
756
* At this point we have found the user cache entry. If we are doing
757
* an exclusive insert (apc_add) we are going to bail right away if
758
* the user entry already exists and it has no ttl, or
759
* there is a ttl and the entry has not timed out yet.
762
if (!(*slot)->value->ttl || (time_t) ((*slot)->ctime + (*slot)->value->ttl) >= t) {
766
apc_cache_remove_slot(cache, slot TSRMLS_CC);
771
* This is a bit nasty. The idea here is to do runtime cleanup of the linked list of
772
* slot entries so we don't always have to skip past a bunch of stale entries. We check
773
* for staleness here and get rid of them by first checking to see if the cache has a global
774
* access ttl on it and removing entries that haven't been accessed for ttl seconds and secondly
775
* we see if the entry has a hard ttl on it and remove it if it has been around longer than its ttl
777
if((cache->ttl && (time_t)(*slot)->atime < (t - (time_t)cache->ttl)) ||
778
((*slot)->value->ttl && (time_t) ((*slot)->ctime + (*slot)->value->ttl) < t)) {
779
apc_cache_remove_slot(cache, slot TSRMLS_CC);
784
slot = &(*slot)->next;
787
if ((*slot = make_slot(cache, &key, value, *slot, t TSRMLS_CC)) != NULL) {
788
/* set value size from pool size */
789
value->mem_size = ctxt->pool->size;
791
cache->header->mem_size += ctxt->pool->size;
792
cache->header->nentries++;
793
cache->header->ninserts++;
799
/* unlock and return succesfull */
800
APC_UNLOCK(cache->header);
806
APC_UNLOCK(cache->header);
812
/* {{{ apc_cache_find */
813
PHP_APCU_API apc_cache_entry_t* apc_cache_find(apc_cache_t* cache, char *strkey, zend_uint keylen, time_t t TSRMLS_DC)
815
/* check we are able to deal with the request */
816
if(!cache || apc_cache_busy(cache TSRMLS_CC)) {
820
/* we only declare a volatile we need */
822
apc_cache_slot_t** slot;
825
volatile apc_cache_entry_t* value = NULL;
827
/* calculate hash and slot */
828
apc_cache_hash_slot(cache, strkey, keylen, &h, &s);
830
/* read lock header */
831
APC_RLOCK(cache->header);
834
slot = &cache->slots[s];
837
/* check for a matching key by has and identifier */
838
if ((h == (*slot)->key.h) && !memcmp((*slot)->key.str, strkey, keylen)) {
840
/* Check to make sure this entry isn't expired by a hard TTL */
841
if((*slot)->value->ttl && (time_t) ((*slot)->ctime + (*slot)->value->ttl) < t) {
842
/* increment misses on cache */
843
cache->header->nmisses++;
846
APC_RUNLOCK(cache->header);
851
/* Otherwise we are fine, increase counters and return the cache entry */
853
(*slot)->value->ref_count++;
856
/* set cache num hits */
857
cache->header->nhits++;
860
value = (*slot)->value;
863
APC_RUNLOCK(cache->header);
865
return (apc_cache_entry_t*)value;
869
slot = &(*slot)->next;
872
/* not found, so increment misses */
873
cache->header->nmisses++;
876
APC_RUNLOCK(cache->header);
883
/* {{{ apc_cache_fetch */
884
PHP_APCU_API zend_bool apc_cache_fetch(apc_cache_t* cache, char* strkey, zend_uint keylen, time_t t, zval **dst TSRMLS_DC)
886
apc_cache_entry_t *entry;
890
if ((entry = apc_cache_find(cache, strkey, keylen, t TSRMLS_CC))) {
891
/* context for copying out */
892
apc_context_t ctxt = {0, };
894
/* create unpool context */
895
if (apc_cache_make_context(cache, &ctxt, APC_CONTEXT_NOSHARE, APC_UNPOOL, APC_COPY_OUT, 0 TSRMLS_CC)) {
897
/* copy to destination */
898
apc_cache_fetch_zval(&ctxt, *dst, entry->val TSRMLS_CC);
902
cache, entry TSRMLS_CC);
904
/* destroy context */
905
apc_cache_destroy_context(&ctxt TSRMLS_CC );
915
/* {{{ apc_cache_exists */
916
PHP_APCU_API apc_cache_entry_t* apc_cache_exists(apc_cache_t* cache, char *strkey, zend_uint keylen, time_t t TSRMLS_DC)
918
if(apc_cache_busy(cache TSRMLS_CC))
920
/* cache cleanup in progress */
924
/* we only declare volatiles we need */
926
apc_cache_slot_t** slot;
928
volatile apc_cache_entry_t* value = NULL;
931
/* get hash and slot */
932
apc_cache_hash_slot(cache, strkey, keylen, &h, &s);
934
/* read lock header */
935
APC_RLOCK(cache->header);
938
slot = &cache->slots[s];
941
/* check for match by hash and identifier */
942
if ((h == (*slot)->key.h) &&
943
!memcmp((*slot)->key.str, strkey, keylen)) {
945
/* Check to make sure this entry isn't expired by a hard TTL */
946
if((*slot)->value->ttl && (time_t) ((*slot)->ctime + (*slot)->value->ttl) < t) {
947
/* marked as a miss */
948
cache->header->nmisses++;
951
APC_RUNLOCK(cache->header);
956
/* Return the cache entry ptr */
957
value = (*slot)->value;
960
APC_RUNLOCK(cache->header);
962
return (apc_cache_entry_t*)value;
965
slot = &(*slot)->next;
969
APC_RUNLOCK(cache->header);
976
/* {{{ apc_cache_update */
977
PHP_APCU_API zend_bool apc_cache_update(apc_cache_t* cache, char *strkey, zend_uint keylen, apc_cache_updater_t updater, void* data TSRMLS_DC)
979
apc_cache_slot_t** slot;
981
zend_bool retval = 0;
984
if(apc_cache_busy(cache TSRMLS_CC))
986
/* cannot service request right now */
991
apc_cache_hash_slot(cache, strkey, keylen, &h, &s);
994
APC_LOCK(cache->header);
997
slot = &cache->slots[s];
1000
/* check for a match by hash and identifier */
1001
if ((h == (*slot)->key.h) &&
1002
!memcmp((*slot)->key.str, strkey, keylen)) {
1003
/* attempt to perform update */
1004
switch(Z_TYPE_P((*slot)->value->val)) {
1006
case IS_CONSTANT_AST:
1009
if(cache->serializer) {
1019
/* executing update */
1021
cache, (*slot)->value, data);
1023
/* set modified time */
1024
(*slot)->key.mtime = apc_time();
1029
APC_UNLOCK(cache->header);
1035
slot = &(*slot)->next;
1039
APC_UNLOCK(cache->header);
1045
/* {{{ apc_cache_delete */
1046
PHP_APCU_API zend_bool apc_cache_delete(apc_cache_t* cache, char *strkey, zend_uint keylen TSRMLS_DC)
1048
apc_cache_slot_t** slot;
1056
/* calculate hash and slot */
1057
apc_cache_hash_slot(cache, strkey, keylen, &h, &s);
1060
APC_LOCK(cache->header);
1063
slot = &cache->slots[s];
1066
/* check for a match by hash and identifier */
1067
if ((h == (*slot)->key.h) &&
1068
!memcmp((*slot)->key.str, strkey, keylen)) {
1069
/* executing removal */
1070
apc_cache_remove_slot(
1071
cache, slot TSRMLS_CC);
1075
/* continue locking */
1076
slot = &(*slot)->next;
1080
APC_UNLOCK(cache->header);
1085
/* unlock deleted */
1086
APC_UNLOCK(cache->header);
1092
/* {{{ apc_cache_make_key */
1093
PHP_APCU_API zend_bool apc_cache_make_key(apc_cache_key_t* key, char* str, zend_ulong len TSRMLS_DC)
1095
assert(key != NULL);
1102
len = strlen(str) + 1;
1106
key->h = zend_inline_hash_func((char *)key->str, key->len);
1107
key->mtime = apc_time();
1113
/* {{{ my_serialize_object */
1114
static zval* my_serialize_object(zval* dst, const zval* src, apc_context_t* ctxt TSRMLS_DC)
1116
smart_str buf = {0};
1117
apc_pool* pool = ctxt->pool;
1118
apc_serialize_t serialize = APC_SERIALIZER_NAME(php);
1119
void *config = NULL;
1121
if(ctxt->serializer) {
1122
serialize = ctxt->serializer->serialize;
1123
config = (ctxt->serializer->config != NULL) ? ctxt->serializer->config : ctxt;
1126
if(serialize((unsigned char**)&buf.c, &buf.len, src, config TSRMLS_CC)) {
1127
dst->type = src->type;
1128
dst->value.str.len = buf.len;
1129
CHECK(dst->value.str.val = apc_pmemcpy(buf.c, (buf.len + 1), pool TSRMLS_CC));
1133
smart_str_free(&buf);
1140
/* {{{ my_unserialize_object */
1141
static zval* my_unserialize_object(zval* dst, const zval* src, apc_context_t* ctxt TSRMLS_DC)
1143
apc_unserialize_t unserialize = APC_UNSERIALIZER_NAME(php);
1144
unsigned char *p = (unsigned char*)Z_STRVAL_P(src);
1145
void *config = NULL;
1147
if(ctxt->serializer) {
1148
unserialize = ctxt->serializer->unserialize;
1149
config = (ctxt->serializer->config != NULL) ? ctxt->serializer->config : ctxt;
1152
if(unserialize(&dst, p, Z_STRLEN_P(src), config TSRMLS_CC)) {
1156
dst->type = IS_NULL;
1163
/* {{{ my_copy_hashtable_ex */
1164
static APC_HOTSPOT HashTable* my_copy_hashtable_ex(HashTable* dst,
1165
HashTable* src TSRMLS_DC,
1166
ht_copy_fun_t copy_fn,
1168
apc_context_t* ctxt,
1169
ht_check_copy_fun_t check_fn,
1172
Bucket* curr = NULL;
1173
Bucket* prev = NULL;
1174
Bucket* newp = NULL;
1176
apc_pool* pool = ctxt->pool;
1178
assert(src != NULL);
1181
CHECK(dst = (HashTable*) pool->palloc(pool, sizeof(src[0]) TSRMLS_CC));
1184
memcpy(dst, src, sizeof(src[0]));
1186
/* allocate buckets for the new hashtable */
1187
CHECK((dst->arBuckets = pool->palloc(pool, (dst->nTableSize * sizeof(Bucket*)) TSRMLS_CC)));
1189
memset(dst->arBuckets, 0, dst->nTableSize * sizeof(Bucket*));
1190
dst->pInternalPointer = NULL;
1191
dst->pListHead = NULL;
1193
for (curr = src->pListHead; curr != NULL; curr = curr->pListNext) {
1194
int n = curr->h % dst->nTableSize;
1198
va_start(args, check_fn);
1200
/* Call the check_fn to see if the current bucket
1201
* needs to be copied out
1203
if(!check_fn(curr, args)) {
1204
dst->nNumOfElements--;
1212
/* create a copy of the bucket 'curr' */
1213
#ifdef ZEND_ENGINE_2_4
1214
if (!curr->nKeyLength) {
1215
CHECK((newp = (Bucket*) apc_pmemcpy(curr, sizeof(Bucket), pool TSRMLS_CC)));
1216
} else if (IS_INTERNED(curr->arKey)) {
1217
CHECK((newp = (Bucket*) apc_pmemcpy(curr, sizeof(Bucket), pool TSRMLS_CC)));
1219
/* I repeat, this is ugly */
1220
CHECK((newp = (Bucket*) apc_pmemcpy(curr, sizeof(Bucket) + curr->nKeyLength, pool TSRMLS_CC)));
1221
newp->arKey = (const char*)(newp+1);
1224
CHECK((newp = (Bucket*) apc_pmemcpy(curr,
1225
(sizeof(Bucket) + curr->nKeyLength - 1),
1229
/* insert 'newp' into the linked list at its hashed index */
1230
if (dst->arBuckets[n]) {
1231
newp->pNext = dst->arBuckets[n];
1233
newp->pNext->pLast = newp;
1235
newp->pNext = newp->pLast = NULL;
1238
dst->arBuckets[n] = newp;
1240
/* copy the bucket data using our 'copy_fn' callback function */
1241
CHECK((newp->pData = copy_fn(NULL, curr->pData, ctxt TSRMLS_CC)));
1244
memcpy(&newp->pDataPtr, newp->pData, sizeof(void*));
1247
newp->pDataPtr = NULL;
1250
/* insert 'newp' into the table-thread linked list */
1251
newp->pListLast = prev;
1252
newp->pListNext = NULL;
1255
prev->pListNext = newp;
1259
dst->pListHead = newp;
1266
dst->pListTail = newp;
1268
zend_hash_internal_pointer_reset(dst);
1275
/* {{{ my_copy_zval_ptr */
1276
static zval** my_copy_zval_ptr(zval** dst, const zval** src, apc_context_t* ctxt TSRMLS_DC)
1279
apc_pool* pool = ctxt->pool;
1280
int usegc = (ctxt->copy == APC_COPY_OUT);
1282
assert(src != NULL);
1285
CHECK(dst = (zval**) pool->palloc(pool, sizeof(zval*) TSRMLS_CC));
1292
CHECK((dst[0] = (zval*) pool->palloc(pool, sizeof(zval) TSRMLS_CC)));
1295
CHECK((dst_new = my_copy_zval(*dst, *src, ctxt TSRMLS_CC)));
1297
if(dst_new != *dst) {
1308
/* {{{ my_copy_zval */
1309
static APC_HOTSPOT zval* my_copy_zval(zval* dst, const zval* src, apc_context_t* ctxt TSRMLS_DC)
1312
apc_pool* pool = ctxt->pool;
1314
assert(dst != NULL);
1315
assert(src != NULL);
1317
memcpy(dst, src, sizeof(src[0]));
1319
if(ctxt->copied.nTableSize) {
1320
if(zend_hash_index_find(&ctxt->copied, (ulong)src, (void**)&tmp) == SUCCESS) {
1321
if(Z_ISREF_P((zval*)src)) {
1322
Z_SET_ISREF_PP(tmp);
1328
zend_hash_index_update(&ctxt->copied, (ulong)src, (void**)&dst, sizeof(zval*), NULL);
1331
if(ctxt->copy == APC_COPY_OUT || ctxt->copy == APC_COPY_IN) {
1332
/* deep copies are refcount(1), but moved up for recursive
1333
* arrays, which end up being add_ref'd during its copy. */
1334
Z_SET_REFCOUNT_P(dst, 1);
1335
Z_UNSET_ISREF_P(dst);
1337
/* code uses refcount=2 for consts */
1338
Z_SET_REFCOUNT_P(dst, Z_REFCOUNT_P((zval*)src));
1339
Z_SET_ISREF_TO_P(dst, Z_ISREF_P((zval*)src));
1342
switch (src->type & IS_CONSTANT_TYPE_MASK) {
1352
if (src->value.str.val) {
1353
CHECK(dst->value.str.val = apc_pmemcpy(src->value.str.val,
1354
src->value.str.len+1,
1360
case IS_CONSTANT_AST:
1361
if(ctxt->serializer == NULL) {
1363
CHECK(dst->value.ht =
1364
my_copy_hashtable(NULL,
1366
(ht_copy_fun_t) my_copy_zval_ptr,
1371
/* fall through to object case */
1376
dst->type = IS_NULL;
1377
if(ctxt->copy == APC_COPY_IN) {
1378
dst = my_serialize_object(dst, src, ctxt TSRMLS_CC);
1379
} else if(ctxt->copy == APC_COPY_OUT) {
1380
dst = my_unserialize_object(dst, src, ctxt TSRMLS_CC);
1383
#ifdef ZEND_ENGINE_2_4
1385
/* XXX implement this */
1398
/* {{{ apc_copy_zval */
1399
PHP_APCU_API zval* apc_copy_zval(zval* dst, const zval* src, apc_context_t* ctxt TSRMLS_DC)
1401
apc_pool* pool = ctxt->pool;
1403
assert(src != NULL);
1406
if(ctxt->copy == APC_COPY_OUT) {
1410
CHECK(dst = (zval*) pool->palloc(pool, sizeof(zval) TSRMLS_CC));
1414
CHECK(dst = my_copy_zval(dst, src, ctxt TSRMLS_CC));
1419
/* {{{ apc_cache_store_zval */
1420
PHP_APCU_API zval* apc_cache_store_zval(zval* dst, const zval* src, apc_context_t* ctxt TSRMLS_DC)
1422
if (Z_TYPE_P(src) == IS_ARRAY) {
1423
/* Maintain a list of zvals we've copied to properly handle recursive structures */
1424
zend_hash_init(&ctxt->copied, 0, NULL, NULL, 0);
1425
dst = apc_copy_zval(dst, src, ctxt TSRMLS_CC);
1426
zend_hash_destroy(&ctxt->copied);
1427
ctxt->copied.nTableSize=0;
1429
dst = apc_copy_zval(dst, src, ctxt TSRMLS_CC);
1437
/* {{{ apc_cache_fetch_zval */
1438
PHP_APCU_API zval* apc_cache_fetch_zval(apc_context_t* ctxt, zval* dst, const zval* src TSRMLS_DC)
1440
if (Z_TYPE_P(src) == IS_ARRAY) {
1441
/* Maintain a list of zvals we've copied to properly handle recursive structures */
1442
zend_hash_init(&ctxt->copied, 0, NULL, NULL, 0);
1443
dst = apc_copy_zval(dst, src, ctxt TSRMLS_CC);
1444
zend_hash_destroy(&ctxt->copied);
1445
ctxt->copied.nTableSize=0;
1447
dst = apc_copy_zval(dst, src, ctxt TSRMLS_CC);
1455
/* {{{ apc_cache_make_entry */
1456
PHP_APCU_API apc_cache_entry_t* apc_cache_make_entry(apc_context_t* ctxt, apc_cache_key_t *key, const zval* val, const unsigned int ttl TSRMLS_DC)
1458
apc_cache_entry_t* entry;
1459
apc_pool* pool = ctxt->pool;
1461
entry = (apc_cache_entry_t*) pool->palloc(pool, sizeof(apc_cache_entry_t) TSRMLS_CC);
1466
/* set key for serializer */
1469
entry->val = apc_cache_store_zval(NULL, val, ctxt TSRMLS_CC);
1474
INIT_PZVAL(entry->val);
1476
entry->ref_count = 0;
1477
entry->mem_size = 0;
1483
/* {{{ apc_cache_link_info */
1484
static zval* apc_cache_link_info(apc_cache_t *cache, apc_cache_slot_t* p TSRMLS_DC)
1488
ALLOC_INIT_ZVAL(link);
1496
add_assoc_stringl(link, "key", (char*) p->key.str, p->key.len-1, 1);
1497
add_assoc_long(link, "ttl", (long)p->value->ttl);
1499
add_assoc_double(link, "nhits", (double)p->nhits);
1500
add_assoc_long(link, "mtime", p->key.mtime);
1501
add_assoc_long(link, "ctime", p->ctime);
1502
add_assoc_long(link, "dtime", p->dtime);
1503
add_assoc_long(link, "atime", p->atime);
1504
add_assoc_long(link, "ref_count", p->value->ref_count);
1505
add_assoc_long(link, "mem_size", p->value->mem_size);
1511
/* {{{ apc_cache_info */
1512
PHP_APCU_API zval* apc_cache_info(apc_cache_t* cache, zend_bool limited TSRMLS_DC)
1518
apc_cache_slot_t* p;
1525
ALLOC_INIT_ZVAL(info);
1527
/* read lock header */
1528
APC_RLOCK(cache->header);
1531
add_assoc_long(info, "nslots", cache->nslots);
1532
add_assoc_long(info, "ttl", cache->ttl);
1534
add_assoc_double(info, "nhits", (double)cache->header->nhits);
1535
add_assoc_double(info, "nmisses", (double)cache->header->nmisses);
1536
add_assoc_double(info, "ninserts", (double)cache->header->ninserts);
1537
add_assoc_long(info, "nentries", cache->header->nentries);
1538
add_assoc_double(info, "nexpunges", (double)cache->header->nexpunges);
1540
add_assoc_long(info, "stime", cache->header->stime);
1541
add_assoc_double(info, "mem_size", (double)cache->header->mem_size);
1543
#ifdef MULTIPART_EVENT_FORMDATA
1544
add_assoc_long(info, "file_upload_progress", 1);
1546
add_assoc_long(info, "file_upload_progress", 0);
1549
add_assoc_stringl(info, "memory_type", "mmap", sizeof("mmap")-1, 1);
1551
add_assoc_stringl(info, "memory_type", "IPC shared", sizeof("IPC shared")-1, 1);
1555
/* For each hashtable slot */
1556
ALLOC_INIT_ZVAL(list);
1559
ALLOC_INIT_ZVAL(slots);
1562
for (i = 0; i < cache->nslots; i++) {
1563
p = cache->slots[i];
1565
for (; p != NULL; p = p->next) {
1566
zval *link = apc_cache_link_info(cache, p TSRMLS_CC);
1567
add_next_index_zval(list, link);
1571
add_index_long(slots, (ulong)i, j);
1575
/* For each slot pending deletion */
1576
ALLOC_INIT_ZVAL(gc);
1579
for (p = cache->header->gc; p != NULL; p = p->next) {
1580
zval *link = apc_cache_link_info(cache, p TSRMLS_CC);
1581
add_next_index_zval(gc, link);
1584
add_assoc_zval(info, "cache_list", list);
1585
add_assoc_zval(info, "deleted_list", gc);
1586
add_assoc_zval(info, "slot_distribution", slots);
1590
APC_RUNLOCK(cache->header);
1597
fetches information about the key provided
1599
PHP_APCU_API zval* apc_cache_stat(apc_cache_t* cache,
1601
zend_uint keylen TSRMLS_DC) {
1603
apc_cache_slot_t** slot;
1606
/* calculate hash and slot */
1607
apc_cache_hash_slot(cache, strkey, keylen, &h, &s);
1609
/* allocate stat buffer */
1610
ALLOC_INIT_ZVAL(stat);
1612
/* read lock header */
1613
APC_RLOCK(cache->header);
1616
slot = &cache->slots[s];
1619
/* check for a matching key by has and identifier */
1620
if ((h == (*slot)->key.h) && !memcmp((*slot)->key.str, strkey, keylen)) {
1623
add_assoc_long(stat, "hits", (*slot)->nhits);
1624
add_assoc_long(stat, "atime", (*slot)->atime);
1625
add_assoc_long(stat, "mtime", (*slot)->key.mtime);
1626
add_assoc_long(stat, "ctime", (*slot)->ctime);
1627
add_assoc_long(stat, "dtime", (*slot)->dtime);
1628
add_assoc_long(stat, "ttl", (*slot)->value->ttl);
1629
add_assoc_long(stat, "refs", (*slot)->value->ref_count);
1635
slot = &(*slot)->next;
1638
APC_RUNLOCK(cache->header);
1643
/* {{{ apc_cache_busy */
1644
PHP_APCU_API zend_bool apc_cache_busy(apc_cache_t* cache TSRMLS_DC)
1646
return (cache->header->state & APC_CACHE_ST_BUSY);
1650
/* {{{ apc_cache_defense */
1651
PHP_APCU_API zend_bool apc_cache_defense(apc_cache_t* cache, apc_cache_key_t* key TSRMLS_DC)
1653
zend_bool result = 0;
1655
/* in ZTS mode, we use the current TSRM context to determine the owner */
1657
# define FROM_DIFFERENT_THREAD(k) ((key->owner = TSRMLS_C) != (k)->owner)
1659
# define FROM_DIFFERENT_THREAD(k) ((key->owner = getpid()) != (k)->owner)
1662
/* only continue if slam defense is enabled */
1663
if (cache->defend) {
1665
/* for copy of locking key struct */
1666
apc_cache_key_t *last = &cache->header->lastkey;
1668
/* check the hash and length match */
1669
if(last->h == key->h && last->len == key->len) {
1671
/* check the time ( last second considered slam ) and context */
1672
if(last->mtime == key->mtime &&
1673
FROM_DIFFERENT_THREAD(last)) {
1675
/* potential cache slam */
1677
"Potential cache slam averted for key '%s'" TSRMLS_CC, key->str);
1680
/* sets enough information for an educated guess, but is not exact */
1682
last->len = key->len;
1683
last->mtime = apc_time();
1685
/* required to tell contexts apart */
1687
last->owner = TSRMLS_C;
1689
last->owner = getpid();
1699
/* {{{ apc_cache_serializer */
1700
PHP_APCU_API void apc_cache_serializer(apc_cache_t* cache, const char* name TSRMLS_DC) {
1701
if (cache && !cache->serializer) {
1702
cache->serializer = apc_find_serializer(name TSRMLS_CC);
1711
* vim>600: expandtab sw=4 ts=4 sts=4 fdm=marker
1712
* vim<600: expandtab sw=4 ts=4 sts=4