root/tags/1.2.0/xcache.c

Revision 299, 68.5 kB (checked in by moo, 2 years ago)

fix false assertion on php.autoglobal_cnt

Line 
1
2#if 0
3#define DEBUG
4#endif
5
6#if 0
7#define SHOW_DPRINT
8#endif
9
10/* {{{ macros */
11#include <stdlib.h>
12#include <stdio.h>
13#include <string.h>
14
15#include <signal.h>
16
17#include "php.h"
18#include "ext/standard/info.h"
19#include "ext/standard/md5.h"
20#include "ext/standard/php_math.h"
21#include "zend_extensions.h"
22#include "SAPI.h"
23
24#include "xcache.h"
25#include "optimizer.h"
26#include "coverager.h"
27#include "disassembler.h"
28#include "align.h"
29#include "stack.h"
30#include "xcache_globals.h"
31#include "processor.h"
32#include "utils.h"
33#include "const_string.h"
34#include "opcode_spec.h"
35
36#ifdef DEBUG
37#   undef NDEBUG
38#   undef inline
39#   define inline
40#else
41#   ifndef NDEBUG
42#       define NDEBUG
43#   endif
44#endif
45#include <assert.h>
46
47#define VAR_ENTRY_EXPIRED(pentry) ((pentry)->ttl && XG(request_time) > pentry->ctime + (pentry)->ttl)
48#define CHECK(x, e) do { if ((x) == NULL) { zend_error(E_ERROR, "XCache: " e); goto err; } } while (0)
49#define LOCK(x) xc_lock(x->lck)
50#define UNLOCK(x) xc_unlock(x->lck)
51
52#define ENTER_LOCK_EX(x) \
53    xc_lock(x->lck); \
54    zend_try { \
55        do
56#define LEAVE_LOCK_EX(x) \
57        while (0); \
58    } zend_catch { \
59        catched = 1; \
60    } zend_end_try(); \
61    xc_unlock(x->lck)
62
63#define ENTER_LOCK(x) do { \
64    int catched = 0; \
65    ENTER_LOCK_EX(x)
66#define LEAVE_LOCK(x) \
67    LEAVE_LOCK_EX(x); \
68    if (catched) { \
69        zend_bailout(); \
70    } \
71} while(0)
72
73/* }}} */
74
75/* {{{ globals */
76static char *xc_shm_scheme = NULL;
77static char *xc_mmap_path = NULL;
78static char *xc_coredump_dir = NULL;
79
80static xc_hash_t xc_php_hcache = {0};
81static xc_hash_t xc_php_hentry = {0};
82static xc_hash_t xc_var_hcache = {0};
83static xc_hash_t xc_var_hentry = {0};
84
85static zend_ulong xc_php_ttl    = 0;
86static zend_ulong xc_var_maxttl = 0;
87
88enum { xc_deletes_gc_interval = 120 };
89static zend_ulong xc_php_gc_interval = 0;
90static zend_ulong xc_var_gc_interval = 0;
91
92/* total size */
93static zend_ulong xc_php_size  = 0;
94static zend_ulong xc_var_size  = 0;
95
96static xc_cache_t **xc_php_caches = NULL;
97static xc_cache_t **xc_var_caches = NULL;
98
99static zend_bool xc_initized = 0;
100static zend_compile_file_t *origin_compile_file;
101
102static zend_bool xc_test = 0;
103static zend_bool xc_readonly_protection = 0;
104
105zend_bool xc_have_op_array_ctor = 0;
106
107static zend_bool xc_module_gotup = 0;
108static zend_bool xc_zend_extension_gotup = 0;
109static zend_bool xc_zend_extension_faked = 0;
110#if !COMPILE_DL_XCACHE
111#   define zend_extension_entry xcache_zend_extension_entry
112#endif
113ZEND_DLEXPORT zend_extension zend_extension_entry;
114ZEND_DECLARE_MODULE_GLOBALS(xcache);
115/* }}} */
116
117/* any function in *_dmz is only safe be called within locked(single thread) area */
118
119static inline int xc_entry_equal_dmz(xc_entry_t *a, xc_entry_t *b) /* {{{ */
120{
121    /* this function isn't required but can be in dmz */
122
123    if (a->type != b->type) {
124        return 0;
125    }
126    switch (a->type) {
127        case XC_TYPE_PHP:
128#ifdef HAVE_INODE
129            do {
130                xc_entry_data_php_t *ap = a->data.php;
131                xc_entry_data_php_t *bp = b->data.php;
132                if (ap->inode) {
133                    return ap->inode == bp->inode
134                        && ap->device == bp->device;
135                }
136            } while(0);
137#endif
138            /* fall */
139
140        case XC_TYPE_VAR:
141            do {
142#ifdef IS_UNICODE
143                if (a->name_type == IS_UNICODE) {
144                    if (a->name.ustr.len != b->name.ustr.len) {
145                        return 0;
146                    }
147                    return memcmp(a->name.ustr.val, b->name.ustr.val, (a->name.ustr.len + 1) * sizeof(UChar)) == 0;
148                }
149                else {
150                    return memcmp(a->name.str.val, b->name.str.val, a->name.str.len + 1) == 0;
151                }
152#else
153                return memcmp(a->name.str.val, b->name.str.val, a->name.str.len + 1) == 0;
154#endif
155
156            } while(0);
157        default:
158            assert(0);
159    }
160    return 0;
161}
162/* }}} */
163static void xc_entry_free_real_dmz(volatile xc_entry_t *xce) /* {{{ */
164{
165    xce->cache->mem->handlers->free(xce->cache->mem, (xc_entry_t *)xce);
166}
167/* }}} */
168static void xc_entry_add_dmz(xc_entry_t *xce) /* {{{ */
169{
170    xc_entry_t **head = &(xce->cache->entries[xce->hvalue]);
171    xce->next = *head;
172    *head = xce;
173    xce->cache->entries_count ++;
174}
175/* }}} */
176static xc_entry_t *xc_entry_store_dmz(xc_entry_t *xce TSRMLS_DC) /* {{{ */
177{
178    xc_entry_t *stored_xce;
179
180    xce->hits  = 0;
181    xce->ctime = XG(request_time);
182    xce->atime = XG(request_time);
183    stored_xce = xc_processor_store_xc_entry_t(xce TSRMLS_CC);
184    if (stored_xce) {
185        xc_entry_add_dmz(stored_xce);
186        return stored_xce;
187    }
188    else {
189        xce->cache->ooms ++;
190        return NULL;
191    }
192}
193/* }}} */
194static void xc_entry_free_dmz(xc_entry_t *xce TSRMLS_DC) /* {{{ */
195{
196    xce->cache->entries_count --;
197    if (xce->refcount == 0) {
198        xc_entry_free_real_dmz(xce);
199    }
200    else {
201        xce->next = xce->cache->deletes;
202        xce->cache->deletes = xce;
203        xce->dtime = XG(request_time);
204        xce->cache->deletes_count ++;
205    }
206    return;
207}
208/* }}} */
209static void xc_entry_remove_dmz(xc_entry_t *xce TSRMLS_DC) /* {{{ */
210{
211    xc_entry_t **pp = &(xce->cache->entries[xce->hvalue]);
212    xc_entry_t *p;
213    for (p = *pp; p; pp = &(p->next), p = p->next) {
214        if (xc_entry_equal_dmz(xce, p)) {
215            /* unlink */
216            *pp = p->next;
217            xc_entry_free_dmz(xce TSRMLS_CC);
218            return;
219        }
220    }
221    assert(0);
222}
223/* }}} */
224static xc_entry_t *xc_entry_find_dmz(xc_entry_t *xce TSRMLS_DC) /* {{{ */
225{
226    xc_entry_t *p;
227    for (p = xce->cache->entries[xce->hvalue]; p; p = p->next) {
228        if (xc_entry_equal_dmz(xce, p)) {
229            if (p->type == XC_TYPE_VAR || /* PHP */ p->data.php->mtime == xce->data.php->mtime) {
230                p->hits ++;
231                p->atime = XG(request_time);
232                return p;
233            }
234            else {
235                xc_entry_remove_dmz(p TSRMLS_CC);
236                return NULL;
237            }
238        }
239    }
240    return NULL;
241}
242/* }}} */
243static void xc_entry_hold_php_dmz(xc_entry_t *xce TSRMLS_DC) /* {{{ */
244{
245#ifdef DEBUG
246    fprintf(stderr, "hold %s\n", xce->name.str.val);
247#endif
248    xce->refcount ++;
249    xc_stack_push(&XG(php_holds)[xce->cache->cacheid], (void *)xce);
250}
251/* }}} */
252#if 0
253static void xc_entry_hold_var_dmz(xc_entry_t *xce TSRMLS_DC) /* {{{ */
254{
255    xce->refcount ++;
256    xc_stack_push(&XG(var_holds)[xce->cache->cacheid], (void *)xce);
257}
258/* }}} */
259#endif
260
261/* helper function that loop through each entry */
262#define XC_ENTRY_APPLY_FUNC(name) int name(xc_entry_t *entry TSRMLS_DC)
263typedef XC_ENTRY_APPLY_FUNC((*cache_apply_dmz_func_t));
264static void xc_entry_apply_dmz(xc_cache_t *cache, cache_apply_dmz_func_t apply_func TSRMLS_DC) /* {{{ */
265{
266    xc_entry_t *p, **pp;
267    int i, c;
268
269    for (i = 0, c = cache->hentry->size; i < c; i ++) {
270        pp = &(cache->entries[i]);
271        for (p = *pp; p; p = *pp) {
272            if (apply_func(p TSRMLS_CC)) {
273                /* unlink */
274                *pp = p->next;
275                xc_entry_free_dmz(p TSRMLS_CC);
276            }
277            else {
278                pp = &(p->next);
279            }
280        }
281    }
282}
283/* }}} */
284
285#define XC_CACHE_APPLY_FUNC(name) void name(xc_cache_t *cache TSRMLS_DC)
286/* call graph:
287 * xc_gc_expires_php -> xc_gc_expires_one -> xc_entry_apply_dmz -> xc_gc_expires_php_entry_dmz
288 * xc_gc_expires_var -> xc_gc_expires_one -> xc_entry_apply_dmz -> xc_gc_expires_var_entry_dmz
289 */
290static XC_ENTRY_APPLY_FUNC(xc_gc_expires_php_entry_dmz) /* {{{ */
291{
292#ifdef DEBUG
293    fprintf(stderr, "ttl %lu, %lu %lu\n", (zend_ulong) XG(request_time), (zend_ulong) entry->atime, xc_php_ttl);
294#endif
295    if (XG(request_time) > entry->atime + xc_php_ttl) {
296        return 1;
297    }
298    return 0;
299}
300/* }}} */
301static XC_ENTRY_APPLY_FUNC(xc_gc_expires_var_entry_dmz) /* {{{ */
302{
303    if (VAR_ENTRY_EXPIRED(entry)) {
304        return 1;
305    }
306    return 0;
307}
308/* }}} */
309static void xc_gc_expires_one(xc_cache_t *cache, zend_ulong gc_interval, cache_apply_dmz_func_t apply_func TSRMLS_DC) /* {{{ */
310{
311#ifdef DEBUG
312    fprintf(stderr, "interval %lu, %lu %lu\n", (zend_ulong) XG(request_time), (zend_ulong) cache->last_gc_expires, gc_interval);
313#endif
314    if (XG(request_time) - cache->last_gc_expires >= gc_interval) {
315        ENTER_LOCK(cache) {
316            if (XG(request_time) - cache->last_gc_expires >= gc_interval) {
317                cache->last_gc_expires = XG(request_time);
318                xc_entry_apply_dmz(cache, apply_func TSRMLS_CC);
319            }
320        } LEAVE_LOCK(cache);
321    }
322}
323/* }}} */
324static void xc_gc_expires_php(TSRMLS_D) /* {{{ */
325{
326    int i, c;
327
328    if (!xc_php_ttl || !xc_php_gc_interval) {
329        return;
330    }
331
332    for (i = 0, c = xc_php_hcache.size; i < c; i ++) {
333        xc_gc_expires_one(xc_php_caches[i], xc_php_gc_interval, xc_gc_expires_php_entry_dmz TSRMLS_CC);
334    }
335}
336/* }}} */
337static void xc_gc_expires_var(TSRMLS_D) /* {{{ */
338{
339    int i, c;
340
341    if (!xc_var_gc_interval) {
342        return;
343    }
344
345    for (i = 0, c = xc_var_hcache.size; i < c; i ++) {
346        xc_gc_expires_one(xc_var_caches[i], xc_var_gc_interval, xc_gc_expires_var_entry_dmz TSRMLS_CC);
347    }
348}
349/* }}} */
350
351static XC_CACHE_APPLY_FUNC(xc_gc_delete_dmz) /* {{{ */
352{
353    xc_entry_t *p, **pp;
354
355    pp = &cache->deletes;
356    for (p = *pp; p; p = *pp) {
357        if (XG(request_time) - p->dtime > 3600) {
358            p->refcount = 0;
359            /* issue warning here */
360        }
361        if (p->refcount == 0) {
362            /* unlink */
363            *pp = p->next;
364            cache->deletes_count --;
365            xc_entry_free_real_dmz(p);
366        }
367        else {
368            pp = &(p->next);
369        }
370    }
371}
372/* }}} */
373static XC_CACHE_APPLY_FUNC(xc_gc_deletes_one) /* {{{ */
374{
375    if (cache->deletes && XG(request_time) - cache->last_gc_deletes > xc_deletes_gc_interval) {
376        ENTER_LOCK(cache) {
377            if (cache->deletes && XG(request_time) - cache->last_gc_deletes > xc_deletes_gc_interval) {
378                cache->last_gc_deletes = XG(request_time);
379                xc_gc_delete_dmz(cache TSRMLS_CC);
380            }
381        } LEAVE_LOCK(cache);
382    }
383}
384/* }}} */
385static void xc_gc_deletes(TSRMLS_D) /* {{{ */
386{
387    int i, c;
388
389    for (i = 0, c = xc_php_hcache.size; i < c; i ++) {
390        xc_gc_deletes_one(xc_php_caches[i] TSRMLS_CC);
391    }
392
393    for (i = 0, c = xc_var_hcache.size; i < c; i ++) {
394        xc_gc_deletes_one(xc_var_caches[i] TSRMLS_CC);
395    }
396}
397/* }}} */
398
399/* helper functions for user functions */
400static void xc_fillinfo_dmz(int cachetype, xc_cache_t *cache, zval *return_value TSRMLS_DC) /* {{{ */
401{
402    zval *blocks;
403    const xc_block_t *b;
404#ifndef NDEBUG
405    xc_memsize_t avail = 0;
406#endif
407    xc_mem_t *mem = cache->mem;
408    const xc_mem_handlers_t *handlers = mem->handlers;
409    zend_ulong interval = (cachetype == XC_TYPE_PHP) ? xc_php_gc_interval : xc_var_gc_interval;
410
411    add_assoc_long_ex(return_value, ZEND_STRS("slots"),     cache->hentry->size);
412    add_assoc_long_ex(return_value, ZEND_STRS("compiling"), cache->compiling);
413    add_assoc_long_ex(return_value, ZEND_STRS("misses"),    cache->misses);
414    add_assoc_long_ex(return_value, ZEND_STRS("hits"),      cache->hits);
415    add_assoc_long_ex(return_value, ZEND_STRS("clogs"),     cache->clogs);
416    add_assoc_long_ex(return_value, ZEND_STRS("ooms"),      cache->ooms);
417
418    add_assoc_long_ex(return_value, ZEND_STRS("cached"),    cache->entries_count);
419    add_assoc_long_ex(return_value, ZEND_STRS("deleted"),   cache->deletes_count);
420    if (interval) {
421        add_assoc_long_ex(return_value, ZEND_STRS("gc"),    (cache->last_gc_expires + interval) - XG(request_time));
422    }
423    else {
424        add_assoc_null_ex(return_value, ZEND_STRS("gc"));
425    }
426
427    MAKE_STD_ZVAL(blocks);
428    array_init(blocks);
429
430    add_assoc_long_ex(return_value, ZEND_STRS("size"),  handlers->size(mem));
431    add_assoc_long_ex(return_value, ZEND_STRS("avail"), handlers->avail(mem));
432    add_assoc_bool_ex(return_value, ZEND_STRS("can_readonly"), xc_readonly_protection);
433
434    for (b = handlers->freeblock_first(mem); b; b = handlers->freeblock_next(b)) {
435        zval *bi;
436
437        MAKE_STD_ZVAL(bi);
438        array_init(bi);
439
440        add_assoc_long_ex(bi, ZEND_STRS("size"),   handlers->block_size(b));
441        add_assoc_long_ex(bi, ZEND_STRS("offset"), handlers->block_offset(mem, b));
442        add_next_index_zval(blocks, bi);
443#ifndef NDEBUG
444        avail += handlers->block_size(b);
445#endif
446    }
447    add_assoc_zval_ex(return_value, ZEND_STRS("free_blocks"), blocks);
448    assert(avail == handlers->avail(mem));
449}
450/* }}} */
451static void xc_fillentry_dmz(xc_entry_t *entry, int del, zval *list TSRMLS_DC) /* {{{ */
452{
453    zval* ei;
454    xc_entry_data_php_t *php;
455    xc_entry_data_var_t *var;
456
457    ALLOC_INIT_ZVAL(ei);
458    array_init(ei);
459
460    add_assoc_long_ex(ei, ZEND_STRS("size"),     entry->size);
461    add_assoc_long_ex(ei, ZEND_STRS("refcount"), entry->refcount);
462    add_assoc_long_ex(ei, ZEND_STRS("hits"),     entry->hits);
463    add_assoc_long_ex(ei, ZEND_STRS("ctime"),    entry->ctime);
464    add_assoc_long_ex(ei, ZEND_STRS("atime"),    entry->atime);
465    if (del) {
466        add_assoc_long_ex(ei, ZEND_STRS("dtime"), entry->dtime);
467    }
468#ifdef IS_UNICODE
469    do {
470        zval *zv;
471        ALLOC_INIT_ZVAL(zv);
472        switch (entry->name_type) {
473            case IS_UNICODE:
474                    ZVAL_UNICODEL(zv, entry->name.ustr.val, entry->name.ustr.len, 1);
475                break;
476            case IS_STRING:
477                ZVAL_STRINGL(zv, entry->name.str.val, entry->name.str.len, 1);
478                break;
479            default:
480                assert(0);
481        }
482        zv->type = entry->name_type;
483        add_assoc_zval_ex(ei, ZEND_STRS("name"), zv);
484    } while (0);
485#else
486    add_assoc_stringl_ex(ei, ZEND_STRS("name"), entry->name.str.val, entry->name.str.len, 1);
487#endif
488    switch (entry->type) {
489        case XC_TYPE_PHP:
490            php = entry->data.php;
491            add_assoc_long_ex(ei, ZEND_STRS("sourcesize"),    php->sourcesize);
492#ifdef HAVE_INODE
493            add_assoc_long_ex(ei, ZEND_STRS("device"),        php->device);
494            add_assoc_long_ex(ei, ZEND_STRS("inode"),         php->inode);
495#endif
496            add_assoc_long_ex(ei, ZEND_STRS("mtime"),         php->mtime);
497
498#ifdef HAVE_XCACHE_CONSTANT
499            add_assoc_long_ex(ei, ZEND_STRS("constinfo_cnt"), php->constinfo_cnt);
500#endif
501            add_assoc_long_ex(ei, ZEND_STRS("function_cnt"),  php->funcinfo_cnt);
502            add_assoc_long_ex(ei, ZEND_STRS("class_cnt"),     php->classinfo_cnt);
503#ifdef ZEND_ENGINE_2_1
504            add_assoc_long_ex(ei, ZEND_STRS("autoglobal_cnt"),php->autoglobal_cnt);
505#endif
506            break;
507        case XC_TYPE_VAR:
508            var = entry->data.var;
509            break;
510
511        default:
512            assert(0);
513    }
514
515    add_next_index_zval(list, ei);
516}
517/* }}} */
518static void xc_filllist_dmz(xc_cache_t *cache, zval *return_value TSRMLS_DC) /* {{{ */
519{
520    zval* list;
521    int i, c;
522    xc_entry_t *e;
523
524    ALLOC_INIT_ZVAL(list);
525    array_init(list);
526
527    for (i = 0, c = cache->hentry->size; i < c; i ++) {
528        for (e = cache->entries[i]; e; e = e->next) {
529            xc_fillentry_dmz(e, 0, list TSRMLS_CC);
530        }
531    }
532    add_assoc_zval(return_value, "cache_list", list);
533
534    ALLOC_INIT_ZVAL(list);
535    array_init(list);
536    for (e = cache->deletes; e; e = e->next) {
537        xc_fillentry_dmz(e, 1, list TSRMLS_CC);
538    }
539    add_assoc_zval(return_value, "deleted_list", list);
540}
541/* }}} */
542
543static zend_op_array *xc_entry_install(xc_entry_t *xce, zend_file_handle *h TSRMLS_DC) /* {{{ */
544{
545    zend_uint i;
546    xc_entry_data_php_t *p = xce->data.php;
547#ifndef ZEND_ENGINE_2
548    /* new ptr which is stored inside CG(class_table) */
549    xc_cest_t **new_cest_ptrs = (xc_cest_t **)do_alloca(sizeof(xc_cest_t*) * p->classinfo_cnt);
550#endif
551
552    CG(active_op_array) = p->op_array;
553
554#ifdef HAVE_XCACHE_CONSTANT
555    /* install constant */
556    for (i = 0; i < p->constinfo_cnt; i ++) {
557        xc_constinfo_t *ci = &p->constinfos[i];
558        xc_install_constant(xce->name.str.val, &ci->constant,
559                UNISW(0, ci->type), ci->key, ci->key_size TSRMLS_CC);
560    }
561#endif
562
563    /* install function */
564    for (i = 0; i < p->funcinfo_cnt; i ++) {
565        xc_funcinfo_t  *fi = &p->funcinfos[i];
566        xc_install_function(xce->name.str.val, &fi->func,
567                UNISW(0, fi->type), fi->key, fi->key_size TSRMLS_CC);
568    }
569
570    /* install class */
571    for (i = 0; i < p->classinfo_cnt; i ++) {
572        xc_classinfo_t *ci = &p->classinfos[i];
573#ifndef ZEND_ENGINE_2
574        zend_class_entry *ce = CestToCePtr(ci->cest);
575        /* fix pointer to the be which inside class_table */
576        if (ce->parent) {
577            zend_uint class_idx = (/* class_num */ (int) (long) ce->parent) - 1;
578            assert(class_idx < i);
579            ci->cest.parent = new_cest_ptrs[class_idx];
580        }
581        new_cest_ptrs[i] =
582#endif
583        xc_install_class(xce->name.str.val, &ci->cest, ci->oplineno,
584                UNISW(0, ci->type), ci->key, ci->key_size TSRMLS_CC);
585    }
586
587#ifdef ZEND_ENGINE_2_1
588    /* trigger auto_globals jit */
589    for (i = 0; i < p->autoglobal_cnt; i ++) {
590        xc_autoglobal_t *aginfo = &p->autoglobals[i];
591        /*
592        zend_auto_global *auto_global;
593        if (zend_u_hash_find(CG(auto_globals), aginfo->type, aginfo->key, aginfo->key_len+1, (void **) &auto_global)==SUCCESS) {
594            if (auto_global->armed) {
595                auto_global->armed = auto_global->auto_global_callback(auto_global->name, auto_global->name_len TSRMLS_CC);
596            }
597        }
598        */
599        zend_u_is_auto_global(aginfo->type, aginfo->key, aginfo->key_len TSRMLS_CC);
600    }
601#endif
602
603    i = 1;
604    zend_hash_add(&EG(included_files), xce->name.str.val, xce->name.str.len+1, (void *)&i, sizeof(int), NULL);
605    if (h) {
606        zend_llist_add_element(&CG(open_files), h);
607    }
608
609#ifndef ZEND_ENGINE_2
610    free_alloca(new_cest_ptrs);
611#endif
612    return p->op_array;
613}
614/* }}} */
615
616static inline void xc_entry_unholds_real(xc_stack_t *holds, xc_cache_t **caches, int cachecount TSRMLS_DC) /* {{{ */
617{
618    int i;
619    xc_stack_t *s;
620    xc_cache_t *cache;
621    xc_entry_t *xce;
622
623    for (i = 0; i < cachecount; i ++) {
624        s = &holds[i];
625#ifdef DEBUG
626        fprintf(stderr, "holded %d\n", xc_stack_size(s));
627#endif
628        if (xc_stack_size(s)) {
629            cache = ((xc_entry_t *)xc_stack_top(s))->cache;
630            ENTER_LOCK(cache) {
631                while (xc_stack_size(s)) {
632                    xce = (xc_entry_t*) xc_stack_pop(s);
633#ifdef DEBUG
634                    fprintf(stderr, "unhold %s\n", xce->name.str.val);
635#endif
636                    xce->refcount --;
637                    assert(xce->refcount >= 0);
638                }
639            } LEAVE_LOCK(cache);
640        }
641    }
642}
643/* }}} */
644static void xc_entry_unholds(TSRMLS_D) /* {{{ */
645{
646    xc_entry_unholds_real(XG(php_holds), xc_php_caches, xc_php_hcache.size TSRMLS_CC);
647    xc_entry_unholds_real(XG(var_holds), xc_var_caches, xc_var_hcache.size TSRMLS_CC);
648}
649/* }}} */
650static int xc_stat(const char *filename, const char *include_path, struct stat *pbuf TSRMLS_DC) /* {{{ */
651{
652    char filepath[MAXPATHLEN];
653    char *paths, *path;
654    char *tokbuf;
655    int size = strlen(include_path) + 1;
656    char tokens[] = { DEFAULT_DIR_SEPARATOR, '\0' };
657
658    paths = (char *)do_alloca(size);
659    memcpy(paths, include_path, size);
660
661    for (path = php_strtok_r(paths, tokens, &tokbuf); path; path = php_strtok_r(NULL, tokens, &tokbuf)) {
662        if (snprintf(filepath, sizeof(filepath), "%s/%s", path, filename) >= MAXPATHLEN - 1) {
663            continue;
664        }
665        if (VCWD_STAT(filepath, pbuf) == 0) {
666            free_alloca(paths);
667            return 0;
668        }
669    }
670
671    free_alloca(paths);
672
673    return 1;
674}
675/* }}} */
676
677#define HASH(i) (i)
678#define HASH_USTR_L(t, s, l) HASH(zend_u_inline_hash_func(t, s, (l + 1) * sizeof(UChar)))
679#define HASH_STR_L(s, l) HASH(zend_inline_hash_func(s, l + 1))
680#define HASH_STR(s) HASH_STR_L(s, strlen(s) + 1)
681#define HASH_NUM(n) HASH(n)
682static inline xc_hash_value_t xc_entry_hash_name(xc_entry_t *xce TSRMLS_DC) /* {{{ */
683{
684    return UNISW(NOTHING, UG(unicode) ? HASH_USTR_L(xce->name_type, xce->name.uni.val, xce->name.uni.len) :)
685        HASH_STR_L(xce->name.str.val, xce->name.str.len);
686}
687/* }}} */
688#define xc_entry_hash_var xc_entry_hash_name
689static inline xc_hash_value_t xc_entry_hash_php(xc_entry_t *xce TSRMLS_DC) /* {{{ */
690{
691#ifdef HAVE_INODE
692    if (xce->data.php->inode) {
693        return HASH(xce->data.php->device + xce->data.php->inode);
694    }
695#endif
696    return xc_entry_hash_name(xce TSRMLS_CC);
697}
698/* }}} */
699static int xc_entry_init_key_php(xc_entry_t *xce, char *filename, char *opened_path_buffer TSRMLS_DC) /* {{{ */
700{
701    struct stat buf, *pbuf;
702    xc_hash_value_t hv;
703    int cacheid;
704    xc_entry_data_php_t *php;
705    char *ptr;
706
707    if (!filename || !SG(request_info).path_translated) {
708        return 0;
709    }
710
711    php = xce->data.php;
712
713    if (XG(stat)) {
714        if (strcmp(SG(request_info).path_translated, filename) == 0) {
715            /* sapi has already done this stat() for us */
716            pbuf = sapi_get_stat(TSRMLS_C);
717            if (pbuf) {
718                goto stat_done;
719            }
720        }
721
722        /* absolute path */
723        pbuf = &buf;
724        if (IS_ABSOLUTE_PATH(filename, strlen(filename))) {
725            if (VCWD_STAT(filename, pbuf) != 0) {
726                return 0;
727            }
728            goto stat_done;
729        }
730
731        /* relative path */
732        if (*filename == '.' && (IS_SLASH(filename[1]) || filename[1] == '.')) {
733            ptr = filename + 1;
734            if (*ptr == '.')