~ubuntu-branches/ubuntu/vivid/php-apcu/vivid

« back to all changes in this revision

Viewing changes to apcu-4.0.4/apc_cache.c

  • Committer: Package Import Robot
  • Author(s): Ondřej Surý
  • Date: 2014-06-24 10:52:52 UTC
  • mfrom: (7.1.4 sid)
  • Revision ID: package-import@ubuntu.com-20140624105252-vsqs1r4og4ykucqg
Tags: 4.0.6-1
* New upstream version 4.0.6
* Remove PHP 5.6 support patch - merged upstream

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/*
2
 
  +----------------------------------------------------------------------+
3
 
  | APC                                                                  |
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
 
  +----------------------------------------------------------------------+
20
 
 
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.
26
 
 
27
 
   All other licensing and usage conditions are those of the PHP Group.
28
 
 
29
 
 */
30
 
 
31
 
/* $Id: apc_cache.c 328743 2012-12-12 07:58:32Z ab $ */
32
 
 
33
 
#include "apc_cache.h"
34
 
#include "apc_sma.h"
35
 
#include "apc_globals.h"
36
 
#include "php_scandir.h"
37
 
#include "SAPI.h"
38
 
#include "TSRM.h"
39
 
#include "php_main.h"
40
 
#include "ext/standard/md5.h"
41
 
#include "ext/standard/php_var.h"
42
 
#include "ext/standard/php_smart_str.h"
43
 
 
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);
46
 
 
47
 
#define CHECK(p) { if ((p) == NULL) return NULL; }
48
 
 
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)
53
 
 
54
 
/* {{{ make_prime */
55
 
static int const primes[] = {
56
 
  257, /*   256 */
57
 
  521, /*   512 */
58
 
 1031, /*  1024 */
59
 
 2053, /*  2048 */
60
 
 3079, /*  3072 */
61
 
 4099, /*  4096 */
62
 
 5147, /*  5120 */
63
 
 6151, /*  6144 */
64
 
 7177, /*  7168 */
65
 
 8209, /*  8192 */
66
 
 9221, /*  9216 */
67
 
10243, /* 10240 */
68
 
11273, /* 11264 */
69
 
12289, /* 12288 */
70
 
13313, /* 13312 */
71
 
14341, /* 14336 */
72
 
15361, /* 15360 */
73
 
16411, /* 16384 */
74
 
17417, /* 17408 */
75
 
18433, /* 18432 */
76
 
19457, /* 19456 */
77
 
0      /* sentinel */
78
 
};
79
 
 
80
 
static int make_prime(int n)
81
 
{
82
 
    int *k = (int*)primes; 
83
 
    while(*k) {
84
 
        if((*k) > n) return *k;
85
 
        k++;
86
 
    }
87
 
    return *(k-1);
88
 
}
89
 
/* }}} */
90
 
 
91
 
/* {{{ make_slot */
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)
93
 
{
94
 
        apc_cache_slot_t* p = NULL;
95
 
        
96
 
        /* allocate slot */
97
 
        if ((p = value->pool->palloc(value->pool, sizeof(apc_cache_slot_t) TSRMLS_CC))) {       
98
 
                
99
 
                /* copy identifier */
100
 
                char* strkey = (char*) apc_pmemcpy(
101
 
                        key->str, key->len, 
102
 
                        value->pool TSRMLS_CC
103
 
                );
104
 
 
105
 
                if (strkey) {
106
 
                        /* set idenfieir */
107
 
                        key->str = strkey;
108
 
                        
109
 
                        /* set slot data */
110
 
                        p->key = key[0];
111
 
                        p->value = value;
112
 
                        
113
 
                        /* set slot relation */
114
 
                        p->next = next;
115
 
                        
116
 
                        /* set slot defaults */
117
 
                        p->nhits = 0;
118
 
                        p->ctime = t;
119
 
                        p->atime = t;
120
 
                        p->dtime = 0;
121
 
                }
122
 
        }
123
 
 
124
 
    return p;
125
 
}
126
 
/* }}} */
127
 
 
128
 
/* {{{ free_slot */
129
 
static void free_slot(apc_cache_slot_t* slot TSRMLS_DC)
130
 
{
131
 
        /* destroy slot pool */
132
 
    apc_pool_destroy(
133
 
                slot->value->pool TSRMLS_CC);
134
 
}
135
 
/* }}} */
136
 
 
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, 
140
 
                                char *str,
141
 
                                zend_uint len, 
142
 
                                zend_ulong* hash, 
143
 
                                zend_ulong* slot) {
144
 
        (*hash) = zend_inline_hash_func(str, len);
145
 
        (*slot) = (*hash) % (cache->nslots);
146
 
} /* }}} */
147
 
 
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)
150
 
{
151
 
    apc_cache_slot_t* dead = *slot;
152
 
    
153
 
    /* think here is safer */
154
 
        *slot = (*slot)->next;
155
 
 
156
 
        /* adjust header info */
157
 
        if (cache->header->mem_size)
158
 
        cache->header->mem_size -= dead->value->mem_size;
159
 
 
160
 
    if (cache->header->nentries)
161
 
                cache->header->nentries--;
162
 
        
163
 
        /* remove if there are no references */
164
 
    if (dead->value->ref_count <= 0) {
165
 
        free_slot(dead TSRMLS_CC);
166
 
    } else {
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;
171
 
    }
172
 
}
173
 
/* }}} */
174
 
 
175
 
/* {{{ apc_cache_gc */
176
 
PHP_APCU_API void apc_cache_gc(apc_cache_t* cache TSRMLS_DC)
177
 
{
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).
182
 
     */
183
 
        if (!cache || !cache->header->gc) {
184
 
                return;
185
 
        }
186
 
 
187
 
    {
188
 
                apc_cache_slot_t** slot = &cache->header->gc;
189
 
 
190
 
                while (*slot != NULL) {
191
 
                        time_t now = time(0);
192
 
                        time_t gc_sec = cache->gc_ttl ? (now - (*slot)->dtime) : 0;
193
 
 
194
 
                        if (!(*slot)->value->ref_count || gc_sec > (time_t)cache->gc_ttl) {
195
 
                apc_cache_slot_t* dead = *slot;
196
 
 
197
 
                                /* good ol' whining */
198
 
                            if (dead->value->ref_count > 0) {
199
 
                                apc_debug(
200
 
                                                "GC cache entry '%s' was on gc-list for %d seconds" TSRMLS_CC, 
201
 
                                                dead->key.str, gc_sec
202
 
                                        );
203
 
                            }
204
 
 
205
 
                                /* set next slot */
206
 
                            *slot = dead->next;
207
 
                        
208
 
                                /* free slot */
209
 
                            free_slot(
210
 
                                        dead TSRMLS_CC);
211
 
                        
212
 
                                /* next */
213
 
                                continue;
214
 
 
215
 
                        } else {
216
 
                                slot = &(*slot)->next;
217
 
                        }
218
 
                }
219
 
        }
220
 
}
221
 
/* }}} */
222
 
 
223
 
/* {{{ php serializer */
224
 
PHP_APCU_API int APC_SERIALIZER_NAME(php) (APC_SERIALIZER_ARGS) 
225
 
{
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);
231
 
    if(strbuf.c) {
232
 
        *buf = (unsigned char*)strbuf.c;
233
 
        *buf_len = strbuf.len;
234
 
        smart_str_0(&strbuf);
235
 
        return 1; 
236
 
    }
237
 
    return 0;
238
 
} /* }}} */
239
 
 
240
 
/* {{{ php unserializer */
241
 
PHP_APCU_API int APC_UNSERIALIZER_NAME(php) (APC_UNSERIALIZER_ARGS) 
242
 
{
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);
248
 
        zval_dtor(*value);
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;
251
 
        return 0;
252
 
    }
253
 
    PHP_VAR_UNSERIALIZE_DESTROY(var_hash);
254
 
    return 1;
255
 
} /* }}} */
256
 
 
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) {
259
 
        apc_cache_t* cache;
260
 
    int cache_size;
261
 
    int nslots;
262
 
 
263
 
        /* calculate number of slots */
264
 
    nslots = make_prime(size_hint > 0 ? size_hint : 2000);
265
 
 
266
 
        /* allocate pointer by normal means */
267
 
    cache = (apc_cache_t*) apc_emalloc(sizeof(apc_cache_t) TSRMLS_CC);
268
 
        
269
 
        /* calculate cache size for shm allocation */
270
 
    cache_size = sizeof(apc_cache_header_t) + nslots*sizeof(apc_cache_slot_t*);
271
 
 
272
 
        /* allocate shm */
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);
276
 
        return NULL;
277
 
    }
278
 
        
279
 
        /* zero shm */
280
 
    memset(cache->shmaddr, 0, cache_size);
281
 
 
282
 
        /* set default header */
283
 
    cache->header = (apc_cache_header_t*) cache->shmaddr;
284
 
        
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;
292
 
        
293
 
        /* set cache options */
294
 
    cache->slots = (apc_cache_slot_t**) (((char*) cache->shmaddr) + sizeof(apc_cache_header_t));
295
 
    cache->sma = sma;
296
 
        cache->serializer = serializer;
297
 
        cache->nslots = nslots;
298
 
    cache->gc_ttl = gc_ttl;
299
 
    cache->ttl = ttl;
300
 
        cache->smart = smart;
301
 
        cache->defend = defend;
302
 
        
303
 
        /* header lock */
304
 
        CREATE_LOCK(&cache->header->lock);
305
 
 
306
 
        /* zero slots */
307
 
    memset(cache->slots, 0, sizeof(apc_cache_slot_t*)*nslots);
308
 
 
309
 
    return cache;
310
 
} /* }}} */
311
 
 
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;
315
 
    apc_cache_key_t key;
316
 
    time_t t;
317
 
    apc_context_t ctxt={0,};
318
 
    zend_bool ret = 0;
319
 
 
320
 
    t = apc_time();
321
 
 
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)) {
324
 
 
325
 
        /* initialize the key for insertion */
326
 
        if (apc_cache_make_key(&key, strkey, keylen TSRMLS_CC)) {
327
 
 
328
 
            /* run cache defense */
329
 
            if (!apc_cache_defense(cache, &key TSRMLS_CC)) {
330
 
                
331
 
                /* initialize the entry for insertion */
332
 
                if ((entry = apc_cache_make_entry(&ctxt, &key, val, ttl TSRMLS_CC))) {
333
 
                
334
 
                    /* execute an insertion */
335
 
                    if (apc_cache_insert(cache, key, entry, &ctxt, t, exclusive TSRMLS_CC)) {
336
 
                        ret = 1;
337
 
                    }
338
 
                }
339
 
            }
340
 
        }
341
 
 
342
 
        /* in any case of failure the context should be destroyed */
343
 
        if (!ret) {
344
 
            apc_cache_destroy_context(&ctxt TSRMLS_CC);
345
 
        }
346
 
    }
347
 
 
348
 
    return ret;
349
 
} /* }}} */
350
 
 
351
 
/* {{{ data_unserialize */
352
 
static zval* data_unserialize(const char *filename TSRMLS_DC)
353
 
{
354
 
    zval* retval;
355
 
    long len = 0;
356
 
    struct stat sb;
357
 
    char *contents, *tmp;
358
 
    FILE *fp;
359
 
    php_unserialize_data_t var_hash = {0,};
360
 
 
361
 
    if(VCWD_STAT(filename, &sb) == -1) {
362
 
        return NULL;
363
 
    }
364
 
 
365
 
    fp = fopen(filename, "rb");
366
 
 
367
 
    len = sizeof(char)*sb.st_size;
368
 
 
369
 
    tmp = contents = malloc(len);
370
 
 
371
 
    if(!contents) {
372
 
        fclose(fp);
373
 
        return NULL;
374
 
    }
375
 
 
376
 
    if(fread(contents, 1, len, fp) < 1) {       
377
 
        fclose(fp);
378
 
        free(contents);
379
 
        return NULL;
380
 
    }
381
 
 
382
 
    MAKE_STD_ZVAL(retval);
383
 
 
384
 
    PHP_VAR_UNSERIALIZE_INIT(var_hash);
385
 
    
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)) {
388
 
        fclose(fp);
389
 
        zval_ptr_dtor(&retval);
390
 
        return NULL;
391
 
    }
392
 
 
393
 
    PHP_VAR_UNSERIALIZE_DESTROY(var_hash);
394
 
 
395
 
    free(contents);
396
 
    fclose(fp);
397
 
 
398
 
    return retval;
399
 
}
400
 
 
401
 
static int apc_load_data(apc_cache_t* cache, const char *data_file TSRMLS_DC)
402
 
{
403
 
    char *p;
404
 
    char key[MAXPATHLEN] = {0,};
405
 
    unsigned int key_len;
406
 
    zval *data;
407
 
 
408
 
    p = strrchr(data_file, DEFAULT_SLASH);
409
 
 
410
 
    if(p && p[1]) {
411
 
        strlcpy(key, p+1, sizeof(key));
412
 
        p = strrchr(key, '.');
413
 
 
414
 
        if(p) {
415
 
            p[0] = '\0';
416
 
            key_len = strlen(key)+1;
417
 
 
418
 
            data = data_unserialize(data_file TSRMLS_CC);
419
 
            if(data) {
420
 
                apc_cache_store(cache, key, key_len, data, 0, 1 TSRMLS_CC);
421
 
            }
422
 
            return 1;
423
 
        }
424
 
    }
425
 
 
426
 
    return 0;
427
 
}
428
 
 
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)
431
 
{
432
 
#ifndef ZTS     
433
 
        zend_bool result = 0;
434
 
        char file[MAXPATHLEN]={0,};
435
 
        int ndir, i;
436
 
        char *p = NULL;
437
 
        struct dirent **namelist = NULL;
438
 
 
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"))) {
444
 
                                free(namelist[i]);
445
 
                                continue;
446
 
                        }
447
 
 
448
 
                        snprintf(file, MAXPATHLEN, "%s%c%s",
449
 
                                    path, DEFAULT_SLASH, namelist[i]->d_name);
450
 
 
451
 
                        if(apc_load_data(cache, file TSRMLS_CC)) {
452
 
                                result = 1;
453
 
                        }
454
 
                        free(namelist[i]);
455
 
                }
456
 
                free(namelist);
457
 
        }
458
 
        return result;
459
 
#else 
460
 
        apc_error("Cannot load data from apc.preload_path=%s in thread-safe mode" TSRMLS_CC, path);
461
 
        return 0;
462
 
#endif  
463
 
} /* }}} */
464
 
 
465
 
/* {{{ apc_cache_release */
466
 
PHP_APCU_API void apc_cache_release(apc_cache_t* cache, apc_cache_entry_t* entry TSRMLS_DC)
467
 
{
468
 
    entry->ref_count--;
469
 
}
470
 
/* }}} */
471
 
 
472
 
/* {{{ apc_cache_destroy */
473
 
PHP_APCU_API void apc_cache_destroy(apc_cache_t* cache TSRMLS_DC)
474
 
{
475
 
        if (!cache) {
476
 
                return;
477
 
        }
478
 
 
479
 
        /* destroy lock */
480
 
        DESTROY_LOCK(&cache->header->lock);
481
 
 
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);
487
 
}
488
 
/* }}} */
489
 
 
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++;
494
 
    
495
 
        /* expunge */
496
 
    {
497
 
                zend_ulong i;
498
 
 
499
 
                for (i = 0; i < cache->nslots; i++) {
500
 
                    apc_cache_slot_t* p = cache->slots[i];
501
 
                    while (p) {
502
 
                        apc_cache_remove_slot(cache, &p TSRMLS_CC);
503
 
                    }
504
 
                    cache->slots[i] = NULL;
505
 
                }
506
 
        }
507
 
        
508
 
        /* set new time so counters make sense */
509
 
        cache->header->stime = apc_time();
510
 
 
511
 
        /* reset counters */
512
 
        cache->header->ninserts = 0;
513
 
        cache->header->nentries = 0;
514
 
    cache->header->nhits = 0;
515
 
    cache->header->nmisses = 0;
516
 
        
517
 
        /* resets lastkey */
518
 
        memset(&cache->header->lastkey, 0, sizeof(apc_cache_key_t));
519
 
} /* }}} */
520
 
 
521
 
/* {{{ apc_cache_clear */
522
 
PHP_APCU_API void apc_cache_clear(apc_cache_t* cache TSRMLS_DC)
523
 
{
524
 
        /* check there is a cache and it is not busy */
525
 
    if(!cache || apc_cache_busy(cache TSRMLS_CC)) {
526
 
                return;
527
 
        }
528
 
        
529
 
        /* lock header */
530
 
        APC_LOCK(cache->header);
531
 
        
532
 
        /* set busy */
533
 
        cache->header->state |= APC_CACHE_ST_BUSY;
534
 
        
535
 
        /* expunge cache */
536
 
        apc_cache_real_expunge(cache TSRMLS_CC);
537
 
 
538
 
        /* set info */
539
 
    cache->header->stime = apc_time();
540
 
    cache->header->nexpunges = 0;
541
 
        
542
 
        /* unset busy */
543
 
    cache->header->state &= ~APC_CACHE_ST_BUSY;
544
 
        
545
 
        /* unlock header */
546
 
        APC_UNLOCK(cache->header);
547
 
}
548
 
/* }}} */
549
 
 
550
 
/* {{{ apc_cache_default_expunge */
551
 
PHP_APCU_API void apc_cache_default_expunge(apc_cache_t* cache, size_t size TSRMLS_DC)
552
 
{
553
 
    time_t t;
554
 
        size_t suitable = 0L;
555
 
    size_t available = 0L;
556
 
 
557
 
    t = apc_time();
558
 
 
559
 
        /* check there is a cache, and it is not busy */
560
 
    if(!cache || apc_cache_busy(cache TSRMLS_CC)) {
561
 
                return;
562
 
        }
563
 
        
564
 
        /* get the lock for header */
565
 
        APC_LOCK(cache->header);
566
 
 
567
 
        /* update state in header */
568
 
        cache->header->state |= APC_CACHE_ST_BUSY;
569
 
 
570
 
        /* make suitable selection */
571
 
        suitable = (cache->smart > 0L) ? (size_t) (cache->smart * size) : (size_t) (cache->sma->size/2);
572
 
 
573
 
        /* gc */
574
 
    apc_cache_gc(cache TSRMLS_CC);
575
 
 
576
 
    /* get available */
577
 
        available = cache->sma->get_avail_mem();
578
 
 
579
 
        /* perform expunge processing */
580
 
    if(!cache->ttl) {
581
 
 
582
 
                /* check it is necessary to expunge */
583
 
                if (available < suitable) {
584
 
                        apc_cache_real_expunge(cache TSRMLS_CC);
585
 
                }
586
 
    } else {
587
 
                apc_cache_slot_t **slot;
588
 
 
589
 
                /* check that expunge is necessary */
590
 
        if (available < suitable) {
591
 
                        zend_ulong i;
592
 
 
593
 
                        /* look for junk */
594
 
                    for (i = 0; i < cache->nslots; i++) {
595
 
                        slot = &cache->slots[i];
596
 
                        while (*slot) {
597
 
                            /*
598
 
                             * Entry TTL has precedence over cache TTL
599
 
                             */
600
 
                            if((*slot)->value->ttl) {
601
 
                                if((time_t) ((*slot)->ctime + (*slot)->value->ttl) < t) {
602
 
                                    apc_cache_remove_slot(cache, slot TSRMLS_CC);
603
 
                                    continue;
604
 
                                }
605
 
                            } else if(cache->ttl) {
606
 
                                if((time_t) ((*slot)->ctime + cache->ttl) < t) {
607
 
                                    apc_cache_remove_slot(cache, slot TSRMLS_CC);
608
 
                                    continue;
609
 
                                }
610
 
                            }
611
 
                                        
612
 
                                        /* grab next slot */
613
 
                                        slot = &(*slot)->next;              
614
 
                        }
615
 
                    }
616
 
 
617
 
                        /* if the cache now has space, then reset last key */
618
 
                    if (cache->sma->get_avail_size(size)) {
619
 
                        /* wipe lastkey */
620
 
                                memset(&cache->header->lastkey, 0, sizeof(apc_cache_key_t));
621
 
                    } else {
622
 
                                /* with not enough space left in cache, we are forced to expunge */
623
 
                                apc_cache_real_expunge(cache TSRMLS_CC);
624
 
                        }
625
 
        }
626
 
    }
627
 
 
628
 
        /* we are done */
629
 
        cache->header->state &= ~APC_CACHE_ST_BUSY;
630
 
 
631
 
        /* unlock header */
632
 
        APC_UNLOCK(cache->header);
633
 
}
634
 
/* }}} */
635
 
 
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(
646
 
                                context,
647
 
                cache->serializer,
648
 
                                (apc_malloc_t) cache->sma->smalloc,
649
 
                cache->sma->sfree,
650
 
                cache->sma->protect,
651
 
                cache->sma->unprotect,
652
 
                                pool_type, copy_type, force_update TSRMLS_CC
653
 
                        );
654
 
                } break;
655
 
                
656
 
                case APC_CONTEXT_NOSHARE: {
657
 
                        return apc_cache_make_context_ex(
658
 
                                context,
659
 
                cache->serializer,
660
 
                                apc_php_malloc, apc_php_free, NULL, NULL,
661
 
                                pool_type, copy_type, force_update TSRMLS_CC
662
 
                        );
663
 
                } break;
664
 
 
665
 
                case APC_CONTEXT_NONE:
666
 
                        /* never used, just to make gcc warning free */
667
 
                        break;
668
 
        }
669
 
 
670
 
        return 0;
671
 
} /* }}} */
672
 
 
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, 
677
 
                                                 apc_free_t _free, 
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
686
 
        );
687
 
 
688
 
        if (!context->pool) {
689
 
                apc_warning("Unable to allocate memory for pool." TSRMLS_CC);
690
 
                return 0;
691
 
        }
692
 
 
693
 
        /* set context information */
694
 
        context->serializer = serializer;
695
 
        context->copy = copy_type;
696
 
        context->force_update = force_update;
697
 
 
698
 
        /* set this to avoid memory errors */
699
 
        memset(&context->copied, 0, sizeof(HashTable));
700
 
 
701
 
        return 1;
702
 
} /* }}} */
703
 
 
704
 
/* {{{ apc_context_destroy */
705
 
PHP_APCU_API zend_bool apc_cache_destroy_context(apc_context_t* context TSRMLS_DC) {
706
 
    if (!context->pool) {
707
 
        return 0;
708
 
    }
709
 
 
710
 
    apc_pool_destroy(context->pool TSRMLS_CC);
711
 
 
712
 
    return 1;
713
 
} /* }}} */
714
 
 
715
 
/* {{{ apc_cache_insert */
716
 
PHP_APCU_API zend_bool apc_cache_insert(apc_cache_t* cache, 
717
 
                                        apc_cache_key_t key, 
718
 
                                        apc_cache_entry_t* value, 
719
 
                                        apc_context_t* ctxt, 
720
 
                                        time_t t, 
721
 
                                        zend_bool exclusive TSRMLS_DC)
722
 
{
723
 
    zend_bool result = 0;
724
 
 
725
 
        /* at least */
726
 
        if (!value) {
727
 
                return result;
728
 
        }
729
 
        
730
 
        /* check we are able to deal with this request */
731
 
        if (!cache || apc_cache_busy(cache TSRMLS_CC)) {
732
 
                return result;
733
 
        }
734
 
 
735
 
        /* lock header */
736
 
        APC_LOCK(cache->header);
737
 
        
738
 
        /* process deleted list  */
739
 
    apc_cache_gc(cache TSRMLS_CC);
740
 
 
741
 
        /* make the insertion */        
742
 
        {
743
 
                apc_cache_slot_t** slot;
744
 
 
745
 
                /*
746
 
                * select appropriate slot ...
747
 
                */
748
 
                slot = &cache->slots[key.h % cache->nslots];
749
 
 
750
 
                while (*slot) {
751
 
                        
752
 
                        /* check for a match by hash and string */
753
 
                    if (((*slot)->key.h == key.h) && (!memcmp((*slot)->key.str, key.str, key.len))) {
754
 
 
755
 
                        /* 
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.
760
 
                         */
761
 
                        if(exclusive) {
762
 
                    if (!(*slot)->value->ttl || (time_t) ((*slot)->ctime + (*slot)->value->ttl) >= t) {
763
 
                        goto nothing;
764
 
                    }
765
 
                        }
766
 
                apc_cache_remove_slot(cache, slot TSRMLS_CC);
767
 
                        break;
768
 
                    } else 
769
 
 
770
 
                    /* 
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
776
 
                     */
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);
780
 
                        continue;
781
 
                    }
782
 
                
783
 
                        /* set next slot */
784
 
            slot = &(*slot)->next;      
785
 
                }
786
 
 
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;
790
 
 
791
 
            cache->header->mem_size += ctxt->pool->size;
792
 
            cache->header->nentries++;
793
 
            cache->header->ninserts++;
794
 
                } else {
795
 
                        goto nothing;
796
 
                }
797
 
        }
798
 
 
799
 
    /* unlock and return succesfull */  
800
 
    APC_UNLOCK(cache->header);
801
 
 
802
 
    return 1;
803
 
 
804
 
    /* bail */
805
 
nothing:
806
 
    APC_UNLOCK(cache->header);
807
 
 
808
 
    return 0;
809
 
}
810
 
/* }}} */
811
 
 
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)
814
 
{
815
 
        /* check we are able to deal with the request */
816
 
    if(!cache || apc_cache_busy(cache TSRMLS_CC)) {
817
 
        return NULL;
818
 
    }
819
 
 
820
 
        /* we only declare a volatile we need */
821
 
    {
822
 
        apc_cache_slot_t** slot;
823
 
                zend_ulong h, s;
824
 
 
825
 
        volatile apc_cache_entry_t* value = NULL;
826
 
        
827
 
                /* calculate hash and slot */
828
 
                apc_cache_hash_slot(cache, strkey, keylen, &h, &s);
829
 
 
830
 
        /* read lock header */
831
 
                APC_RLOCK(cache->header);
832
 
 
833
 
                /* find head */
834
 
                slot = &cache->slots[s];
835
 
 
836
 
                while (*slot) {
837
 
                        /* check for a matching key by has and identifier */
838
 
                    if ((h == (*slot)->key.h) && !memcmp((*slot)->key.str, strkey, keylen)) {
839
 
 
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++;
844
 
 
845
 
                                        /* unlock header */                     
846
 
                                        APC_RUNLOCK(cache->header);
847
 
                            
848
 
                            return NULL;
849
 
                        }
850
 
 
851
 
                        /* Otherwise we are fine, increase counters and return the cache entry */
852
 
                        (*slot)->nhits++;
853
 
                        (*slot)->value->ref_count++;
854
 
                        (*slot)->atime = t;
855
 
                        
856
 
                                /* set cache num hits */
857
 
                                cache->header->nhits++;
858
 
                        
859
 
                                /* grab value */
860
 
                        value = (*slot)->value;
861
 
 
862
 
                                /* unlock header */                     
863
 
                                APC_RUNLOCK(cache->header);
864
 
                        
865
 
                        return (apc_cache_entry_t*)value;
866
 
                    }
867
 
 
868
 
                        /* next */
869
 
                    slot = &(*slot)->next;              
870
 
                }
871
 
                
872
 
                /* not found, so increment misses */
873
 
                cache->header->nmisses++;
874
 
 
875
 
                /* unlock header */
876
 
                APC_RUNLOCK(cache->header);
877
 
    }
878
 
 
879
 
    return NULL;
880
 
}
881
 
/* }}} */
882
 
 
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) 
885
 
{
886
 
        apc_cache_entry_t *entry;
887
 
        zend_bool ret = 0;
888
 
        
889
 
        /* find the entry */
890
 
        if ((entry = apc_cache_find(cache, strkey, keylen, t TSRMLS_CC))) {
891
 
        /* context for copying out */
892
 
                apc_context_t ctxt = {0, };
893
 
 
894
 
                /* create unpool context */
895
 
                if (apc_cache_make_context(cache, &ctxt, APC_CONTEXT_NOSHARE, APC_UNPOOL, APC_COPY_OUT, 0 TSRMLS_CC)) {
896
 
 
897
 
                        /* copy to destination */
898
 
                        apc_cache_fetch_zval(&ctxt, *dst, entry->val TSRMLS_CC);
899
 
 
900
 
                        /* release entry */
901
 
                        apc_cache_release(
902
 
                                cache, entry TSRMLS_CC);
903
 
 
904
 
                        /* destroy context */
905
 
                        apc_cache_destroy_context(&ctxt TSRMLS_CC );
906
 
 
907
 
                        /* set result */
908
 
                        ret = 1;
909
 
                }
910
 
        }
911
 
        
912
 
        return ret;
913
 
} /* }}} */
914
 
 
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)
917
 
{
918
 
    if(apc_cache_busy(cache TSRMLS_CC))
919
 
    {
920
 
        /* cache cleanup in progress */ 
921
 
        return NULL;
922
 
    }
923
 
 
924
 
        /* we only declare volatiles we need */
925
 
        {
926
 
                apc_cache_slot_t** slot;
927
 
        
928
 
                volatile apc_cache_entry_t* value = NULL;
929
 
                zend_ulong h, s;
930
 
 
931
 
        /* get hash and slot */
932
 
                apc_cache_hash_slot(cache, strkey, keylen, &h, &s);
933
 
 
934
 
        /* read lock header */
935
 
                APC_RLOCK(cache->header);       
936
 
 
937
 
                /* find head */
938
 
                slot = &cache->slots[s];
939
 
 
940
 
                while (*slot) {
941
 
                        /* check for match by hash and identifier */
942
 
                    if ((h == (*slot)->key.h) &&
943
 
                        !memcmp((*slot)->key.str, strkey, keylen)) {
944
 
 
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++;
949
 
 
950
 
                                        /* unlock header */
951
 
                                        APC_RUNLOCK(cache->header);
952
 
 
953
 
                            return NULL;
954
 
                        }
955
 
 
956
 
                        /* Return the cache entry ptr */
957
 
                        value = (*slot)->value;
958
 
                        
959
 
                                /* unlock header */
960
 
                                APC_RUNLOCK(cache->header);
961
 
                                        
962
 
                        return (apc_cache_entry_t*)value;
963
 
                    }
964
 
 
965
 
                        slot = &(*slot)->next;  
966
 
                }
967
 
 
968
 
                /* unlock header */
969
 
                APC_RUNLOCK(cache->header);
970
 
        }
971
 
 
972
 
    return NULL;
973
 
}
974
 
/* }}} */
975
 
 
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)
978
 
{
979
 
    apc_cache_slot_t** slot;
980
 
        
981
 
    zend_bool retval = 0;
982
 
    zend_ulong h, s;
983
 
 
984
 
    if(apc_cache_busy(cache TSRMLS_CC))
985
 
    {
986
 
        /* cannot service request right now */ 
987
 
        return 0;
988
 
    }
989
 
 
990
 
    /* calculate hash */
991
 
    apc_cache_hash_slot(cache, strkey, keylen, &h, &s);
992
 
        
993
 
        /* lock header */
994
 
        APC_LOCK(cache->header);
995
 
 
996
 
        /* find head */
997
 
    slot = &cache->slots[s];
998
 
 
999
 
    while (*slot) {
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)) {
1005
 
                case IS_ARRAY:
1006
 
                case IS_CONSTANT_AST:
1007
 
                case IS_OBJECT:
1008
 
                {
1009
 
                    if(cache->serializer) {
1010
 
                        retval = 0;
1011
 
                        break;
1012
 
                    } else {
1013
 
                        /* fall through */
1014
 
                    }
1015
 
                }
1016
 
                /* fall through */
1017
 
                default:
1018
 
                {
1019
 
                                        /* executing update */
1020
 
                    retval = updater(
1021
 
                                                cache, (*slot)->value, data);
1022
 
 
1023
 
                                        /* set modified time */
1024
 
                    (*slot)->key.mtime = apc_time();
1025
 
                }
1026
 
                break;
1027
 
            }
1028
 
                        /* unlock header */
1029
 
                        APC_UNLOCK(cache->header);
1030
 
 
1031
 
            return retval;
1032
 
        }
1033
 
 
1034
 
                /* set next slot */
1035
 
        slot = &(*slot)->next;
1036
 
        }
1037
 
        
1038
 
        /* unlock header */
1039
 
        APC_UNLOCK(cache->header);
1040
 
 
1041
 
    return 0;
1042
 
}
1043
 
/* }}} */
1044
 
 
1045
 
/* {{{ apc_cache_delete */
1046
 
PHP_APCU_API zend_bool apc_cache_delete(apc_cache_t* cache, char *strkey, zend_uint keylen TSRMLS_DC)
1047
 
{
1048
 
    apc_cache_slot_t** slot;
1049
 
        
1050
 
    zend_ulong h, s;
1051
 
 
1052
 
        if (!cache) {
1053
 
                return 1;
1054
 
        }
1055
 
 
1056
 
    /* calculate hash and slot */
1057
 
    apc_cache_hash_slot(cache, strkey, keylen, &h, &s);
1058
 
 
1059
 
        /* lock cache */
1060
 
        APC_LOCK(cache->header);        
1061
 
        
1062
 
        /* find head */
1063
 
    slot = &cache->slots[s];
1064
 
 
1065
 
    while (*slot) {
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);
1072
 
                        goto deleted;
1073
 
        }
1074
 
                
1075
 
                /* continue locking */
1076
 
                slot = &(*slot)->next;      
1077
 
    }
1078
 
        
1079
 
        /* unlock header */
1080
 
        APC_UNLOCK(cache->header);
1081
 
        
1082
 
        return 0;
1083
 
 
1084
 
deleted:
1085
 
        /* unlock deleted */
1086
 
        APC_UNLOCK(cache->header);
1087
 
 
1088
 
        return 1;
1089
 
}
1090
 
/* }}} */
1091
 
 
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)
1094
 
{
1095
 
    assert(key != NULL);
1096
 
 
1097
 
    if (!str) {
1098
 
            return 0;
1099
 
    }
1100
 
    
1101
 
        if (!len)
1102
 
            len = strlen(str) + 1;
1103
 
        
1104
 
    key->str = str;
1105
 
    key->len = len;
1106
 
    key->h = zend_inline_hash_func((char *)key->str, key->len);
1107
 
    key->mtime = apc_time();
1108
 
 
1109
 
    return 1;
1110
 
}
1111
 
/* }}} */
1112
 
 
1113
 
/* {{{ my_serialize_object */
1114
 
static zval* my_serialize_object(zval* dst, const zval* src, apc_context_t* ctxt TSRMLS_DC)
1115
 
{
1116
 
    smart_str buf = {0};
1117
 
    apc_pool* pool = ctxt->pool;
1118
 
    apc_serialize_t serialize = APC_SERIALIZER_NAME(php);
1119
 
    void *config = NULL;
1120
 
 
1121
 
    if(ctxt->serializer) {
1122
 
        serialize = ctxt->serializer->serialize;
1123
 
        config = (ctxt->serializer->config != NULL) ? ctxt->serializer->config : ctxt;
1124
 
    }
1125
 
 
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));
1130
 
    }
1131
 
 
1132
 
    if(buf.c) {
1133
 
                smart_str_free(&buf);
1134
 
        }
1135
 
 
1136
 
    return dst;
1137
 
}
1138
 
/* }}} */
1139
 
 
1140
 
/* {{{ my_unserialize_object */
1141
 
static zval* my_unserialize_object(zval* dst, const zval* src, apc_context_t* ctxt TSRMLS_DC)
1142
 
{
1143
 
    apc_unserialize_t unserialize = APC_UNSERIALIZER_NAME(php);
1144
 
    unsigned char *p = (unsigned char*)Z_STRVAL_P(src);
1145
 
    void *config = NULL;
1146
 
 
1147
 
    if(ctxt->serializer) {
1148
 
        unserialize = ctxt->serializer->unserialize;
1149
 
        config = (ctxt->serializer->config != NULL) ? ctxt->serializer->config : ctxt;
1150
 
    }
1151
 
 
1152
 
    if(unserialize(&dst, p, Z_STRLEN_P(src), config TSRMLS_CC)) {
1153
 
        return dst;
1154
 
    } else {
1155
 
        zval_dtor(dst);
1156
 
        dst->type = IS_NULL;
1157
 
    }
1158
 
    return dst;
1159
 
}
1160
 
/* }}} */
1161
 
 
1162
 
 
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,
1167
 
                                    int holds_ptrs,
1168
 
                                    apc_context_t* ctxt,
1169
 
                                    ht_check_copy_fun_t check_fn,
1170
 
                                    ...)
1171
 
{
1172
 
    Bucket* curr = NULL;
1173
 
    Bucket* prev = NULL;
1174
 
    Bucket* newp = NULL;
1175
 
    int first = 1;
1176
 
    apc_pool* pool = ctxt->pool;
1177
 
 
1178
 
    assert(src != NULL);
1179
 
 
1180
 
    if (!dst) {
1181
 
        CHECK(dst = (HashTable*) pool->palloc(pool, sizeof(src[0]) TSRMLS_CC));
1182
 
    }
1183
 
 
1184
 
    memcpy(dst, src, sizeof(src[0]));
1185
 
 
1186
 
    /* allocate buckets for the new hashtable */
1187
 
    CHECK((dst->arBuckets = pool->palloc(pool, (dst->nTableSize * sizeof(Bucket*)) TSRMLS_CC)));
1188
 
 
1189
 
    memset(dst->arBuckets, 0, dst->nTableSize * sizeof(Bucket*));
1190
 
    dst->pInternalPointer = NULL;
1191
 
    dst->pListHead = NULL;
1192
 
 
1193
 
    for (curr = src->pListHead; curr != NULL; curr = curr->pListNext) {
1194
 
        int n = curr->h % dst->nTableSize;
1195
 
 
1196
 
        if(check_fn) {
1197
 
            va_list args;
1198
 
            va_start(args, check_fn);
1199
 
 
1200
 
            /* Call the check_fn to see if the current bucket
1201
 
             * needs to be copied out
1202
 
             */
1203
 
            if(!check_fn(curr, args)) {
1204
 
                dst->nNumOfElements--;
1205
 
                va_end(args);
1206
 
                continue;
1207
 
            }
1208
 
 
1209
 
            va_end(args);
1210
 
        }
1211
 
 
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)));
1218
 
        } else {
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);
1222
 
        }
1223
 
#else
1224
 
        CHECK((newp = (Bucket*) apc_pmemcpy(curr,
1225
 
                                  (sizeof(Bucket) + curr->nKeyLength - 1),
1226
 
                                  pool TSRMLS_CC)));
1227
 
#endif
1228
 
 
1229
 
        /* insert 'newp' into the linked list at its hashed index */
1230
 
        if (dst->arBuckets[n]) {
1231
 
            newp->pNext = dst->arBuckets[n];
1232
 
            newp->pLast = NULL;
1233
 
            newp->pNext->pLast = newp;
1234
 
        } else {
1235
 
            newp->pNext = newp->pLast = NULL;
1236
 
        }
1237
 
 
1238
 
        dst->arBuckets[n] = newp;
1239
 
 
1240
 
        /* copy the bucket data using our 'copy_fn' callback function */
1241
 
        CHECK((newp->pData = copy_fn(NULL, curr->pData, ctxt TSRMLS_CC)));
1242
 
 
1243
 
        if (holds_ptrs) {
1244
 
            memcpy(&newp->pDataPtr, newp->pData, sizeof(void*));
1245
 
        }
1246
 
        else {
1247
 
            newp->pDataPtr = NULL;
1248
 
        }
1249
 
 
1250
 
        /* insert 'newp' into the table-thread linked list */
1251
 
        newp->pListLast = prev;
1252
 
        newp->pListNext = NULL;
1253
 
 
1254
 
        if (prev) {
1255
 
            prev->pListNext = newp;
1256
 
        }
1257
 
 
1258
 
        if (first) {
1259
 
            dst->pListHead = newp;
1260
 
            first = 0;
1261
 
        }
1262
 
 
1263
 
        prev = newp;
1264
 
    }
1265
 
 
1266
 
    dst->pListTail = newp;
1267
 
 
1268
 
    zend_hash_internal_pointer_reset(dst);
1269
 
 
1270
 
    return dst;
1271
 
}
1272
 
/* }}} */
1273
 
 
1274
 
 
1275
 
/* {{{ my_copy_zval_ptr */
1276
 
static zval** my_copy_zval_ptr(zval** dst, const zval** src, apc_context_t* ctxt TSRMLS_DC)
1277
 
{
1278
 
    zval* dst_new;
1279
 
    apc_pool* pool = ctxt->pool;
1280
 
    int usegc = (ctxt->copy == APC_COPY_OUT);
1281
 
 
1282
 
    assert(src != NULL);
1283
 
 
1284
 
    if (!dst) {
1285
 
        CHECK(dst = (zval**) pool->palloc(pool, sizeof(zval*) TSRMLS_CC));
1286
 
    }
1287
 
 
1288
 
    if(usegc) {
1289
 
        ALLOC_ZVAL(dst[0]);
1290
 
        CHECK(dst[0]);
1291
 
    } else {
1292
 
        CHECK((dst[0] = (zval*) pool->palloc(pool, sizeof(zval) TSRMLS_CC)));
1293
 
    }
1294
 
 
1295
 
    CHECK((dst_new = my_copy_zval(*dst, *src, ctxt TSRMLS_CC)));
1296
 
 
1297
 
    if(dst_new != *dst) {
1298
 
        if(usegc) {
1299
 
            FREE_ZVAL(dst[0]);
1300
 
        }
1301
 
        *dst = dst_new;
1302
 
    }
1303
 
 
1304
 
    return dst;
1305
 
}
1306
 
/* }}} */
1307
 
 
1308
 
/* {{{ my_copy_zval */
1309
 
static APC_HOTSPOT zval* my_copy_zval(zval* dst, const zval* src, apc_context_t* ctxt TSRMLS_DC)
1310
 
{
1311
 
    zval **tmp;
1312
 
    apc_pool* pool = ctxt->pool;
1313
 
 
1314
 
    assert(dst != NULL);
1315
 
    assert(src != NULL);
1316
 
 
1317
 
    memcpy(dst, src, sizeof(src[0]));
1318
 
 
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);
1323
 
            }
1324
 
            Z_ADDREF_PP(tmp);
1325
 
            return *tmp;
1326
 
        }
1327
 
 
1328
 
        zend_hash_index_update(&ctxt->copied, (ulong)src, (void**)&dst, sizeof(zval*), NULL);
1329
 
    }
1330
 
 
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);
1336
 
    } else {
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));
1340
 
    }
1341
 
 
1342
 
    switch (src->type & IS_CONSTANT_TYPE_MASK) {
1343
 
    case IS_RESOURCE:
1344
 
    case IS_BOOL:
1345
 
    case IS_LONG:
1346
 
    case IS_DOUBLE:
1347
 
    case IS_NULL:
1348
 
        break;
1349
 
 
1350
 
    case IS_CONSTANT:
1351
 
    case IS_STRING:
1352
 
        if (src->value.str.val) {
1353
 
            CHECK(dst->value.str.val = apc_pmemcpy(src->value.str.val,
1354
 
                                                   src->value.str.len+1,
1355
 
                                                   pool TSRMLS_CC));
1356
 
        }
1357
 
        break;
1358
 
 
1359
 
    case IS_ARRAY:
1360
 
    case IS_CONSTANT_AST:
1361
 
        if(ctxt->serializer == NULL) {
1362
 
 
1363
 
            CHECK(dst->value.ht =
1364
 
                my_copy_hashtable(NULL,
1365
 
                                  src->value.ht,
1366
 
                                  (ht_copy_fun_t) my_copy_zval_ptr,
1367
 
                                  1,
1368
 
                                  ctxt));
1369
 
            break;
1370
 
        } else {
1371
 
            /* fall through to object case */
1372
 
        }
1373
 
 
1374
 
    case IS_OBJECT:
1375
 
    
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);
1381
 
        }
1382
 
        break;
1383
 
#ifdef ZEND_ENGINE_2_4
1384
 
    case IS_CALLABLE:
1385
 
        /* XXX implement this */
1386
 
        assert(0);
1387
 
        break;
1388
 
#endif
1389
 
 
1390
 
    default:
1391
 
        assert(0);
1392
 
    }
1393
 
 
1394
 
    return dst;
1395
 
}
1396
 
/* }}} */
1397
 
 
1398
 
/* {{{ apc_copy_zval */
1399
 
PHP_APCU_API zval* apc_copy_zval(zval* dst, const zval* src, apc_context_t* ctxt TSRMLS_DC)
1400
 
{
1401
 
    apc_pool* pool = ctxt->pool;
1402
 
    
1403
 
    assert(src != NULL);
1404
 
 
1405
 
    if (!dst) {
1406
 
        if(ctxt->copy == APC_COPY_OUT) {
1407
 
            ALLOC_ZVAL(dst);
1408
 
            CHECK(dst);
1409
 
        } else {
1410
 
            CHECK(dst = (zval*) pool->palloc(pool, sizeof(zval) TSRMLS_CC));
1411
 
        }
1412
 
    }
1413
 
 
1414
 
    CHECK(dst = my_copy_zval(dst, src, ctxt TSRMLS_CC));
1415
 
    return dst;
1416
 
}
1417
 
/* }}} */
1418
 
 
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)
1421
 
{
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;
1428
 
    } else {
1429
 
        dst = apc_copy_zval(dst, src, ctxt TSRMLS_CC);
1430
 
    }
1431
 
 
1432
 
 
1433
 
    return dst;
1434
 
}
1435
 
/* }}} */
1436
 
 
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)
1439
 
{
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;
1446
 
    } else {
1447
 
        dst = apc_copy_zval(dst, src, ctxt TSRMLS_CC);
1448
 
    }
1449
 
 
1450
 
 
1451
 
    return dst;
1452
 
}
1453
 
/* }}} */
1454
 
 
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)
1457
 
{
1458
 
    apc_cache_entry_t* entry;
1459
 
    apc_pool* pool = ctxt->pool;
1460
 
 
1461
 
    entry = (apc_cache_entry_t*) pool->palloc(pool, sizeof(apc_cache_entry_t) TSRMLS_CC);
1462
 
    if (!entry) {
1463
 
                return NULL;
1464
 
        }
1465
 
        
1466
 
        /* set key for serializer */
1467
 
        ctxt->key = key;
1468
 
        
1469
 
    entry->val = apc_cache_store_zval(NULL, val, ctxt TSRMLS_CC);
1470
 
    if(!entry->val) {
1471
 
        return NULL;
1472
 
    }
1473
 
 
1474
 
    INIT_PZVAL(entry->val);
1475
 
    entry->ttl = ttl;
1476
 
    entry->ref_count = 0;
1477
 
    entry->mem_size = 0;
1478
 
    entry->pool = pool;
1479
 
    return entry;
1480
 
}
1481
 
/* }}} */
1482
 
 
1483
 
/* {{{ apc_cache_link_info */
1484
 
static zval* apc_cache_link_info(apc_cache_t *cache, apc_cache_slot_t* p TSRMLS_DC)
1485
 
{
1486
 
    zval *link = NULL;
1487
 
 
1488
 
    ALLOC_INIT_ZVAL(link);
1489
 
 
1490
 
    if(!link) {
1491
 
        return NULL;
1492
 
    }
1493
 
 
1494
 
    array_init(link);
1495
 
 
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);
1498
 
 
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);
1506
 
 
1507
 
    return link;
1508
 
}
1509
 
/* }}} */
1510
 
 
1511
 
/* {{{ apc_cache_info */
1512
 
PHP_APCU_API zval* apc_cache_info(apc_cache_t* cache, zend_bool limited TSRMLS_DC)
1513
 
{
1514
 
    zval *info = NULL;
1515
 
    zval *list = NULL;
1516
 
    zval *gc = NULL;
1517
 
    zval *slots = NULL;
1518
 
    apc_cache_slot_t* p;
1519
 
    zend_ulong i, j;
1520
 
 
1521
 
    if(!cache) {
1522
 
                return NULL;
1523
 
        }
1524
 
 
1525
 
    ALLOC_INIT_ZVAL(info);
1526
 
 
1527
 
        /* read lock header */
1528
 
        APC_RLOCK(cache->header);
1529
 
 
1530
 
    array_init(info);
1531
 
    add_assoc_long(info, "nslots", cache->nslots);
1532
 
    add_assoc_long(info, "ttl", cache->ttl);
1533
 
 
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);
1539
 
    
1540
 
    add_assoc_long(info, "stime", cache->header->stime);
1541
 
    add_assoc_double(info, "mem_size", (double)cache->header->mem_size);
1542
 
 
1543
 
#ifdef MULTIPART_EVENT_FORMDATA
1544
 
    add_assoc_long(info, "file_upload_progress", 1);
1545
 
#else
1546
 
    add_assoc_long(info, "file_upload_progress", 0);
1547
 
#endif
1548
 
#if APC_MMAP
1549
 
    add_assoc_stringl(info, "memory_type", "mmap", sizeof("mmap")-1, 1);
1550
 
#else
1551
 
    add_assoc_stringl(info, "memory_type", "IPC shared", sizeof("IPC shared")-1, 1);
1552
 
#endif
1553
 
 
1554
 
    if(!limited) {
1555
 
        /* For each hashtable slot */
1556
 
        ALLOC_INIT_ZVAL(list);
1557
 
        array_init(list);
1558
 
 
1559
 
        ALLOC_INIT_ZVAL(slots);
1560
 
        array_init(slots);
1561
 
 
1562
 
        for (i = 0; i < cache->nslots; i++) {
1563
 
            p = cache->slots[i];
1564
 
            j = 0;
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);
1568
 
                j++;
1569
 
            }
1570
 
            if(j != 0) {
1571
 
                add_index_long(slots, (ulong)i, j);
1572
 
            }
1573
 
        }
1574
 
 
1575
 
        /* For each slot pending deletion */
1576
 
        ALLOC_INIT_ZVAL(gc);
1577
 
        array_init(gc);
1578
 
 
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);
1582
 
        }
1583
 
        
1584
 
        add_assoc_zval(info, "cache_list", list);
1585
 
        add_assoc_zval(info, "deleted_list", gc);
1586
 
        add_assoc_zval(info, "slot_distribution", slots);
1587
 
    }
1588
 
        
1589
 
        /* unlock header */
1590
 
        APC_RUNLOCK(cache->header);
1591
 
 
1592
 
    return info;
1593
 
}
1594
 
/* }}} */
1595
 
 
1596
 
/*
1597
 
 fetches information about the key provided
1598
 
*/
1599
 
PHP_APCU_API zval* apc_cache_stat(apc_cache_t* cache,
1600
 
                                  char *strkey,
1601
 
                                  zend_uint keylen TSRMLS_DC) {
1602
 
    zval *stat;
1603
 
    apc_cache_slot_t** slot;
1604
 
        zend_ulong h, s;
1605
 
    
1606
 
        /* calculate hash and slot */
1607
 
        apc_cache_hash_slot(cache, strkey, keylen, &h, &s);
1608
 
        
1609
 
        /* allocate stat buffer */
1610
 
        ALLOC_INIT_ZVAL(stat);
1611
 
 
1612
 
    /* read lock header */
1613
 
        APC_RLOCK(cache->header);
1614
 
 
1615
 
        /* find head */
1616
 
        slot = &cache->slots[s];
1617
 
 
1618
 
        while (*slot) {
1619
 
                /* check for a matching key by has and identifier */
1620
 
            if ((h == (*slot)->key.h) && !memcmp((*slot)->key.str, strkey, keylen)) {
1621
 
            array_init(stat);
1622
 
            
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);
1630
 
                
1631
 
                break;
1632
 
            }
1633
 
 
1634
 
                /* next */
1635
 
            slot = &(*slot)->next;              
1636
 
        }
1637
 
    
1638
 
    APC_RUNLOCK(cache->header);
1639
 
    
1640
 
    return stat;
1641
 
}
1642
 
 
1643
 
/* {{{ apc_cache_busy */
1644
 
PHP_APCU_API zend_bool apc_cache_busy(apc_cache_t* cache TSRMLS_DC)
1645
 
{       
1646
 
        return (cache->header->state & APC_CACHE_ST_BUSY);
1647
 
}
1648
 
/* }}} */
1649
 
 
1650
 
/* {{{ apc_cache_defense */
1651
 
PHP_APCU_API zend_bool apc_cache_defense(apc_cache_t* cache, apc_cache_key_t* key TSRMLS_DC)
1652
 
{
1653
 
        zend_bool result = 0;
1654
 
 
1655
 
        /* in ZTS mode, we use the current TSRM context to determine the owner */
1656
 
#ifdef ZTS
1657
 
# define FROM_DIFFERENT_THREAD(k) ((key->owner = TSRMLS_C) != (k)->owner) 
1658
 
#else
1659
 
# define FROM_DIFFERENT_THREAD(k) ((key->owner = getpid()) != (k)->owner) 
1660
 
#endif
1661
 
 
1662
 
        /* only continue if slam defense is enabled */
1663
 
        if (cache->defend) {
1664
 
 
1665
 
                /* for copy of locking key struct */
1666
 
                apc_cache_key_t *last = &cache->header->lastkey;
1667
 
 
1668
 
                /* check the hash and length match */
1669
 
                if(last->h == key->h && last->len == key->len) {
1670
 
 
1671
 
                        /* check the time ( last second considered slam ) and context */
1672
 
                        if(last->mtime == key->mtime && 
1673
 
                           FROM_DIFFERENT_THREAD(last)) {
1674
 
 
1675
 
                            /* potential cache slam */
1676
 
                            apc_debug(
1677
 
                                        "Potential cache slam averted for key '%s'" TSRMLS_CC, key->str);
1678
 
                            result = 1;
1679
 
                        } else {
1680
 
                                /* sets enough information for an educated guess, but is not exact */
1681
 
                                last->h = key->h;
1682
 
                                last->len = key->len;
1683
 
                                last->mtime = apc_time();
1684
 
 
1685
 
                                /* required to tell contexts apart */
1686
 
#ifdef ZTS
1687
 
                                last->owner = TSRMLS_C;
1688
 
#else
1689
 
                                last->owner = getpid();
1690
 
#endif                          
1691
 
                        }
1692
 
                }
1693
 
        }
1694
 
 
1695
 
    return result;
1696
 
}
1697
 
/* }}} */
1698
 
 
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);
1703
 
        }
1704
 
} /* }}} */
1705
 
 
1706
 
/*
1707
 
 * Local variables:
1708
 
 * tab-width: 4
1709
 
 * c-basic-offset: 4
1710
 
 * End:
1711
 
 * vim>600: expandtab sw=4 ts=4 sts=4 fdm=marker
1712
 
 * vim<600: expandtab sw=4 ts=4 sts=4
1713
 
 */