r5:
[php5-apc.git] / apc_main.c
diff --git a/apc_main.c b/apc_main.c
new file mode 100644 (file)
index 0000000..50b673d
--- /dev/null
@@ -0,0 +1,701 @@
+/*
+  +----------------------------------------------------------------------+
+  | APC                                                                  |
+  +----------------------------------------------------------------------+
+  | Copyright (c) 2006 The PHP Group                                     |
+  +----------------------------------------------------------------------+
+  | This source file is subject to version 3.01 of the PHP license,      |
+  | that is bundled with this package in the file LICENSE, and is        |
+  | available through the world-wide-web at the following url:           |
+  | http://www.php.net/license/3_01.txt                                  |
+  | If you did not receive a copy of the PHP license and are unable to   |
+  | obtain it through the world-wide-web, please send a note to          |
+  | license@php.net so we can mail you a copy immediately.               |
+  +----------------------------------------------------------------------+
+  | Authors: Daniel Cowgill <dcowgill@communityconnect.com>              |
+  |          Rasmus Lerdorf <rasmus@php.net>                             |
+  |          Arun C. Murthy <arunc@yahoo-inc.com>                        |
+  |          Gopal Vijayaraghavan <gopalv@yahoo-inc.com>                 |
+  +----------------------------------------------------------------------+
+
+   This software was contributed to PHP by Community Connect Inc. in 2002
+   and revised in 2005 by Yahoo! Inc. to add support for PHP 5.1.
+   Future revisions and derivatives of this source code must acknowledge
+   Community Connect Inc. as the original contributor of this module by
+   leaving this note intact in the source code.
+
+   All other licensing and usage conditions are those of the PHP Group.
+
+ */
+
+/* $Id: apc_main.c,v 3.103 2007/11/14 19:46:46 shire Exp $ */
+
+#include "apc_php.h"
+#include "apc_main.h"
+#include "apc.h"
+#include "apc_lock.h"
+#include "apc_cache.h"
+#include "apc_compile.h"
+#include "apc_globals.h"
+#include "apc_sma.h"
+#include "apc_stack.h"
+#include "apc_zend.h"
+#include "SAPI.h"
+#if PHP_API_VERSION <= 20020918
+#if HAVE_APACHE
+#ifdef APC_PHP4_STAT
+#undef XtOffsetOf
+#include "httpd.h"
+#endif
+#endif
+#endif
+
+/* {{{ module variables */
+
+/* pointer to the original Zend engine compile_file function */
+typedef zend_op_array* (zend_compile_t)(zend_file_handle*, int TSRMLS_DC);
+static zend_compile_t *old_compile_file;
+
+/* }}} */
+
+/* {{{ get/set old_compile_file (to interact with other extensions that need the compile hook) */
+static zend_compile_t* set_compile_hook(zend_compile_t *ptr)
+{
+    zend_compile_t *retval = old_compile_file;
+
+    if (ptr != NULL) old_compile_file = ptr;
+    return retval;
+}
+/* }}} */
+
+/* {{{ install_function */
+static int install_function(apc_function_t fn TSRMLS_DC)
+{
+    int status =
+        zend_hash_add(EG(function_table),
+                      fn.name,
+                      fn.name_len+1,
+                      apc_copy_function_for_execution(fn.function),
+                      sizeof(fn.function[0]),
+                      NULL);
+
+    if (status == FAILURE) {
+        /* apc_eprint("Cannot redeclare %s()", fn.name); */
+    }
+
+    return status;
+}
+/* }}} */
+
+/* {{{ install_class */
+static int install_class(apc_class_t cl TSRMLS_DC)
+{
+    zend_class_entry* class_entry = cl.class_entry;
+    zend_class_entry* parent = NULL;
+    int status;
+#ifdef ZEND_ENGINE_2
+    zend_class_entry** allocated_ce = NULL;
+#endif
+
+
+    /* Special case for mangled names. Mangled names are unique to a file.
+     * There is no way two classes with the same mangled name will occur,
+     * unless a file is included twice. And if in case, a file is included
+     * twice, all mangled name conflicts can be ignored and the class redeclaration
+     * error may be deferred till runtime of the corresponding DECLARE_CLASS
+     * calls.
+     */
+
+    if(cl.name_len != 0 && cl.name[0] == '\0') {
+        if(zend_hash_exists(CG(class_table), cl.name, cl.name_len+1)) {
+            return SUCCESS;
+        }
+    }
+    
+#ifdef ZEND_ENGINE_2    
+    /*
+     * XXX: We need to free this somewhere...
+     */
+    allocated_ce = apc_php_malloc(sizeof(zend_class_entry*));    
+
+    if(!allocated_ce) {
+        return FAILURE;
+    }
+
+    *allocated_ce = 
+#endif        
+    class_entry =
+        apc_copy_class_entry_for_execution(cl.class_entry,
+                                           cl.is_derived);
+
+
+    /* restore parent class pointer for compile-time inheritance */
+    if (cl.parent_name != NULL) {
+#ifdef ZEND_ENGINE_2    
+        zend_class_entry** parent_ptr = NULL;
+        /*
+         * zend_lookup_class has to be due to presence of __autoload, 
+         * just looking up the EG(class_table) is not enough in php5!
+         * Even more dangerously, thanks to __autoload and people using
+         * class names as filepaths for inclusion, this has to be case
+         * sensitive. zend_lookup_class automatically does a case_fold
+         * internally, but passes the case preserved version to __autoload.
+         * Aside: Do NOT pass *strlen(cl.parent_name)+1* because 
+         * zend_lookup_class does it internally anyway!
+         */
+        status = zend_lookup_class(cl.parent_name,
+                                    strlen(cl.parent_name),
+                                    &parent_ptr TSRMLS_CC);
+#else
+        status = zend_hash_find(EG(class_table),
+                                cl.parent_name,
+                                strlen(cl.parent_name)+1,
+                                (void**) &parent);
+#endif
+        if (status == FAILURE) {
+            if(APCG(report_autofilter)) {
+                apc_wprint("Dynamic inheritance detected for class %s", cl.name);
+            }
+            class_entry->parent = NULL;
+            return status;
+        }
+        else {
+#ifdef ZEND_ENGINE_2            
+            parent = *parent_ptr;
+#endif 
+            class_entry->parent = parent;
+#ifdef ZEND_ENGINE_2
+            zend_do_inheritance(class_entry, parent TSRMLS_CC);
+#else
+            zend_do_inheritance(class_entry, parent);
+#endif
+        }
+
+
+    }
+
+#ifdef ZEND_ENGINE_2                           
+    status = zend_hash_add(EG(class_table),
+                           cl.name,
+                           cl.name_len+1,
+                           allocated_ce,
+                           sizeof(zend_class_entry*),
+                           NULL);
+#else                           
+    status = zend_hash_add(EG(class_table),
+                           cl.name,
+                           cl.name_len+1,
+                           class_entry,
+                           sizeof(zend_class_entry),
+                           NULL);
+#endif                           
+
+    if (status == FAILURE) {
+        apc_eprint("Cannot redeclare class %s", cl.name);
+    } 
+    return status;
+}
+/* }}} */
+
+/* {{{ uninstall_class */
+static int uninstall_class(apc_class_t cl TSRMLS_DC)
+{
+    int status;
+
+#ifdef ZEND_ENGINE_2                           
+    status = zend_hash_del(EG(class_table),
+                           cl.name,
+                           cl.name_len+1);
+#else                           
+    status = zend_hash_del(EG(class_table),
+                           cl.name,
+                           cl.name_len+1);
+#endif                           
+    if (status == FAILURE) {
+        apc_eprint("Cannot delete class %s", cl.name);
+    } 
+    return status;
+}
+/* }}} */
+
+/* {{{ compare_file_handles */
+static int compare_file_handles(void* a, void* b)
+{
+    zend_file_handle* fh1 = (zend_file_handle*)a;
+    zend_file_handle* fh2 = (zend_file_handle*)b;
+    return (fh1->type == fh2->type && 
+            fh1->filename == fh2->filename &&
+            fh1->opened_path == fh2->opened_path);
+}
+/* }}} */
+
+/* {{{ cached_compile */
+static zend_op_array* cached_compile(zend_file_handle* h,
+                                        int type TSRMLS_DC)
+{
+    apc_cache_entry_t* cache_entry;
+    int i, ii;
+
+    cache_entry = (apc_cache_entry_t*) apc_stack_top(APCG(cache_stack));
+    assert(cache_entry != NULL);
+
+    if (cache_entry->data.file.classes) {
+        for (i = 0; cache_entry->data.file.classes[i].class_entry != NULL; i++) {
+            if(install_class(cache_entry->data.file.classes[i] TSRMLS_CC) == FAILURE) {
+                goto default_compile;
+            }
+        }
+    }
+
+    if (cache_entry->data.file.functions) {
+        for (i = 0; cache_entry->data.file.functions[i].function != NULL; i++) {
+            install_function(cache_entry->data.file.functions[i] TSRMLS_CC);
+        }
+    }
+
+
+    return apc_copy_op_array_for_execution(NULL, cache_entry->data.file.op_array TSRMLS_CC);
+
+default_compile:
+
+    if(APCG(report_autofilter)) {
+        apc_wprint("Autofiltering %s", h->opened_path);
+    }
+
+    if(cache_entry->data.file.classes) {
+        for(ii = 0; ii < i ; ii++) {
+            uninstall_class(cache_entry->data.file.classes[ii] TSRMLS_CC);
+        }
+    }
+    
+    apc_stack_pop(APCG(cache_stack)); /* pop out cache_entry */
+    
+    apc_cache_release(apc_cache, cache_entry);
+
+    /* cannot free up cache data yet, it maybe in use */
+    
+    zend_llist_del_element(&CG(open_files), h, compare_file_handles); /* XXX: kludge */
+    
+    h->type = ZEND_HANDLE_FILENAME;
+
+    return NULL;
+}
+/* }}} */
+
+/* {{{ my_compile_file
+   Overrides zend_compile_file */
+static zend_op_array* my_compile_file(zend_file_handle* h,
+                                               int type TSRMLS_DC)
+{
+    apc_cache_key_t key;
+    apc_cache_entry_t* cache_entry;
+    zend_op_array* op_array;
+    int num_functions, num_classes, ret;
+    zend_op_array* alloc_op_array;
+    apc_function_t* alloc_functions;
+    apc_class_t* alloc_classes;
+    time_t t;
+    char *path;
+    size_t mem_size;
+
+    if (!APCG(enabled) || (apc_cache_busy(apc_cache) && !APCG(localcache))) {
+               return old_compile_file(h, type TSRMLS_CC);
+       }
+
+    /* check our regular expression filters */
+    if (APCG(filters) && apc_compiled_filters) {
+        int ret = apc_regex_match_array(apc_compiled_filters, h->filename);
+        if(ret == APC_NEGATIVE_MATCH || (ret != APC_POSITIVE_MATCH && !APCG(cache_by_default))) {
+            return old_compile_file(h, type TSRMLS_CC);
+        }
+    } else if(!APCG(cache_by_default)) {
+        return old_compile_file(h, type TSRMLS_CC);
+    }
+
+#if PHP_API_VERSION < 20041225
+#if HAVE_APACHE && defined(APC_PHP4_STAT)
+    t = ((request_rec *)SG(server_context))->request_time;
+#else 
+    t = time(0);
+#endif
+#else 
+    t = sapi_get_request_time(TSRMLS_C);
+#endif
+
+#ifdef __DEBUG_APC__
+    fprintf(stderr,"1. h->opened_path=[%s]  h->filename=[%s]\n", h->opened_path?h->opened_path:"null",h->filename);
+#endif
+
+    /* try to create a cache key; if we fail, give up on caching */
+    if (!apc_cache_make_file_key(&key, h->filename, PG(include_path), t TSRMLS_CC)) {
+        return old_compile_file(h, type TSRMLS_CC);
+    }
+
+
+    if(!APCG(force_file_update)) {
+        if(APCG(localcache)) {
+            /* search for the file in the local cache */
+            cache_entry = apc_local_cache_find(APCG(lcache), key, t);
+        } else {
+            /* search for the file in the cache */
+            cache_entry = apc_cache_find(apc_cache, key, t);
+        }
+    } else {
+        cache_entry = NULL;
+    }
+
+    if (cache_entry != NULL) {
+        int dummy = 1;
+        if (h->opened_path == NULL) {
+            h->opened_path = estrdup(cache_entry->data.file.filename);
+        }
+        zend_hash_add(&EG(included_files), h->opened_path, strlen(h->opened_path)+1, (void *)&dummy, sizeof(int), NULL);
+        zend_llist_add_element(&CG(open_files), h); /* XXX kludge */
+        apc_stack_push(APCG(cache_stack), cache_entry);
+        op_array = cached_compile(h, type TSRMLS_CC);
+        if(op_array) {
+#ifdef APC_FILEHITS
+            /* If the file comes from the cache, add it to the global request file list */
+            add_next_index_string(APCG(filehits), h->filename, 1);
+#endif
+            return op_array;
+        }
+        if(APCG(report_autofilter)) {
+            apc_wprint("Recompiling %s", h->opened_path);
+        }
+        /* TODO: check what happens with EG(included_files) */
+    }
+    
+    if(apc_cache_busy(apc_cache) && APCG(localcache)) {
+        /* possibly local cache returned NULL because cache is busy */
+               return old_compile_file(h, type TSRMLS_CC);
+       }
+
+    /* remember how many functions and classes existed before compilation */
+    num_functions = zend_hash_num_elements(CG(function_table));
+    num_classes   = zend_hash_num_elements(CG(class_table));
+    
+    /* compile the file using the default compile function */
+    op_array = old_compile_file(h, type TSRMLS_CC);
+    if (op_array == NULL) {
+        return NULL;
+    }
+    /*
+     * Basically this will cause a file only to be cached on a percentage 
+     * of the attempts.  This is to avoid cache slams when starting up a
+     * very busy server or when modifying files on a very busy live server.
+     * There is no point having many processes all trying to cache the same
+     * file at the same time.  By introducing a chance of being cached
+     * we theoretically cut the cache slam problem by the given percentage.
+     * For example if apc.slam_defense is set to 66 then 2/3 of the attempts
+     * to cache an uncached file will be ignored.
+     */
+    if(APCG(slam_defense)) {
+        if(APCG(slam_rand)==-1) {
+            APCG(slam_rand) = (int)(100.0*rand()/(RAND_MAX+1.0));
+        }
+        if(APCG(slam_rand) < APCG(slam_defense)) {
+            return op_array;
+        }
+    }
+
+    /* Make sure the mtime reflects the files last known mtime in the case of fpstat==0 */
+    if(key.type == APC_CACHE_KEY_FPFILE) {
+        apc_fileinfo_t fileinfo;
+        struct stat *tmp_buf = NULL;
+        if(!strcmp(SG(request_info).path_translated, h->filename)) {
+            tmp_buf = sapi_get_stat(TSRMLS_C);  /* Apache has already done this stat() for us */
+        }
+        if(tmp_buf) { 
+            fileinfo.st_buf = *tmp_buf;
+        } else {
+            if (apc_search_paths(h->filename, PG(include_path), &fileinfo) != 0) {
+#ifdef __DEBUG_APC__
+                fprintf(stderr,"Stat failed %s - bailing (%s) (%d)\n",filename,SG(request_info).path_translated);
+#endif
+                return op_array;
+            }
+        }
+        key.mtime = fileinfo.st_buf.st_mtime;
+    }
+
+    HANDLE_BLOCK_INTERRUPTIONS();
+
+#if NONBLOCKING_LOCK_AVAILABLE
+    if(APCG(write_lock)) {
+        if(!apc_cache_write_lock(apc_cache)) {
+            HANDLE_UNBLOCK_INTERRUPTIONS();
+            return op_array;
+        }
+    }
+#endif
+
+    mem_size = 0;
+    APCG(mem_size_ptr) = &mem_size;
+    if(!(alloc_op_array = apc_copy_op_array(NULL, op_array, apc_sma_malloc, apc_sma_free TSRMLS_CC))) {
+        apc_cache_expunge(apc_cache,t);
+        apc_cache_expunge(apc_user_cache,t);
+        APCG(mem_size_ptr) = NULL;
+#if NONBLOCKING_LOCK_AVAILABLE
+        if(APCG(write_lock)) {
+            apc_cache_write_unlock(apc_cache);
+        }
+#endif
+        HANDLE_UNBLOCK_INTERRUPTIONS();
+        return op_array;
+    }
+    
+    if(!(alloc_functions = apc_copy_new_functions(num_functions, apc_sma_malloc, apc_sma_free TSRMLS_CC))) {
+        apc_free_op_array(alloc_op_array, apc_sma_free);
+        apc_cache_expunge(apc_cache,t);
+        apc_cache_expunge(apc_user_cache,t);
+        APCG(mem_size_ptr) = NULL;
+#if NONBLOCKING_LOCK_AVAILABLE
+        if(APCG(write_lock)) {
+            apc_cache_write_unlock(apc_cache);
+        }
+#endif
+        HANDLE_UNBLOCK_INTERRUPTIONS();
+        return op_array;
+    }
+    if(!(alloc_classes = apc_copy_new_classes(op_array, num_classes, apc_sma_malloc, apc_sma_free TSRMLS_CC))) {
+        apc_free_op_array(alloc_op_array, apc_sma_free);
+        apc_free_functions(alloc_functions, apc_sma_free);
+        apc_cache_expunge(apc_cache,t);
+        apc_cache_expunge(apc_user_cache,t);
+        APCG(mem_size_ptr) = NULL;
+#if NONBLOCKING_LOCK_AVAILABLE
+        if(APCG(write_lock)) {
+            apc_cache_write_unlock(apc_cache);
+        }
+#endif
+        HANDLE_UNBLOCK_INTERRUPTIONS();
+        return op_array;
+    }
+
+    path = h->opened_path;
+    if(!path) path=h->filename;
+
+#ifdef __DEBUG_APC__
+    fprintf(stderr,"2. h->opened_path=[%s]  h->filename=[%s]\n", h->opened_path?h->opened_path:"null",h->filename);
+#endif
+
+    if(!(cache_entry = apc_cache_make_file_entry(path, alloc_op_array, alloc_functions, alloc_classes))) {
+        apc_free_op_array(alloc_op_array, apc_sma_free);
+        apc_free_functions(alloc_functions, apc_sma_free);
+        apc_free_classes(alloc_classes, apc_sma_free);
+        apc_cache_expunge(apc_cache,t);
+        apc_cache_expunge(apc_user_cache,t);
+        APCG(mem_size_ptr) = NULL;
+#if NONBLOCKING_LOCK_AVAILABLE
+        if(APCG(write_lock)) {
+            apc_cache_write_unlock(apc_cache);
+        }
+#endif
+        HANDLE_UNBLOCK_INTERRUPTIONS();
+        return op_array;
+    }
+    APCG(mem_size_ptr) = NULL;
+    cache_entry->mem_size = mem_size;
+
+    if ((ret = apc_cache_insert(apc_cache, key, cache_entry, t)) != 1) {
+        apc_cache_free_entry(cache_entry);
+        if(ret==-1) {
+            apc_cache_expunge(apc_cache,t);
+            apc_cache_expunge(apc_user_cache,t);
+        }
+    }
+
+#if NONBLOCKING_LOCK_AVAILABLE
+    if(APCG(write_lock)) {
+        apc_cache_write_unlock(apc_cache);
+    }
+#endif
+    HANDLE_UNBLOCK_INTERRUPTIONS();
+
+    return op_array;
+}
+/* }}} */
+
+/* {{{ module init and shutdown */
+
+int apc_module_init(int module_number TSRMLS_DC)
+{
+    /* apc initialization */
+#if APC_MMAP
+    apc_sma_init(APCG(shm_segments), APCG(shm_size)*1024*1024, APCG(mmap_file_mask));
+#else
+    apc_sma_init(APCG(shm_segments), APCG(shm_size)*1024*1024, NULL);
+#endif
+    apc_cache = apc_cache_create(APCG(num_files_hint), APCG(gc_ttl), APCG(ttl));
+    apc_user_cache = apc_cache_create(APCG(user_entries_hint), APCG(gc_ttl), APCG(user_ttl));
+
+    apc_compiled_filters = apc_regex_compile_array(APCG(filters));
+
+    /* override compilation */
+    old_compile_file = zend_compile_file;
+    zend_compile_file = my_compile_file;
+    REGISTER_LONG_CONSTANT("\000apc_magic", (long)&set_compile_hook, CONST_PERSISTENT | CONST_CS);
+
+    APCG(initialized) = 1;
+    return 0;
+}
+
+int apc_module_shutdown(TSRMLS_D)
+{
+    if (!APCG(initialized))
+        return 0;
+
+    /* restore compilation */
+    zend_compile_file = old_compile_file;
+
+    /* 
+     * In case we got interrupted by a SIGTERM or something else during execution
+     * we may have cache entries left on the stack that we need to check to make
+     * sure that any functions or classes these may have added to the global function
+     * and class tables are removed before we blow away the memory that hold them.
+     * 
+     * This is merely to remove memory leak warnings - as the process is terminated
+     * immediately after shutdown. The following while loop can be removed without
+     * affecting anything else.
+     */
+    while (apc_stack_size(APCG(cache_stack)) > 0) {
+        int i;
+        apc_cache_entry_t* cache_entry = (apc_cache_entry_t*) apc_stack_pop(APCG(cache_stack));
+        if (cache_entry->data.file.functions) {
+            for (i = 0; cache_entry->data.file.functions[i].function != NULL; i++) {
+                zend_hash_del(EG(function_table),
+                    cache_entry->data.file.functions[i].name,
+                    cache_entry->data.file.functions[i].name_len+1);
+            }
+        }
+        if (cache_entry->data.file.classes) {
+            for (i = 0; cache_entry->data.file.classes[i].class_entry != NULL; i++) {
+                zend_hash_del(EG(class_table),
+                    cache_entry->data.file.classes[i].name,
+                    cache_entry->data.file.classes[i].name_len+1);
+            }
+        }
+        apc_cache_release(apc_cache, cache_entry);
+    }
+
+    apc_cache_destroy(apc_cache);
+    apc_cache_destroy(apc_user_cache);
+    apc_sma_cleanup();
+
+    APCG(initialized) = 0;
+    return 0;
+}
+
+/* }}} */
+
+/* {{{ process init and shutdown */
+int apc_process_init(int module_number TSRMLS_DC)
+{
+    int minttl = (APCG(gc_ttl) >  APCG(ttl) ? APCG(ttl) : APCG(gc_ttl))/2;
+    int size = APCG(localcache_size);
+    if(APCG(initialized) && APCG(localcache)) {
+        /* TTL is 2 mins by default */
+        APCG(lcache) = apc_local_cache_create(apc_cache, size, minttl ? minttl : 120); 
+    }
+    return 0;
+}
+
+int apc_process_shutdown(TSRMLS_D)
+{
+    if(APCG(initialized) && APCG(localcache) && APCG(lcache)) {
+        apc_local_cache_destroy(APCG(lcache));
+        APCG(lcache) = NULL;
+    }
+       return 0;
+}
+/* }}} */
+
+/* {{{ request init and shutdown */
+
+int apc_request_init(TSRMLS_D)
+{
+    apc_stack_clear(APCG(cache_stack));
+    APCG(slam_rand) = -1;
+    APCG(copied_zvals) = NULL;
+
+#ifdef APC_FILEHITS
+    ALLOC_INIT_ZVAL(APCG(filehits));
+    array_init(APCG(filehits));
+#endif
+
+    return 0;
+}
+
+int apc_request_shutdown(TSRMLS_D)
+{
+    apc_deactivate(TSRMLS_C);
+
+#ifdef APC_FILEHITS
+    zval_ptr_dtor(&APCG(filehits));
+#endif
+
+    return 0;
+}
+
+/* }}} */
+
+/* {{{ apc_deactivate */
+void apc_deactivate(TSRMLS_D)
+{
+    /* The execution stack was unwound, which prevented us from decrementing
+     * the reference counts on active cache entries in `my_execute`.
+     */
+    while (apc_stack_size(APCG(cache_stack)) > 0) {
+        int i;
+        zend_class_entry* zce = NULL;
+        void ** centry = (void*)(&zce);
+#ifdef ZEND_ENGINE_2
+        zend_class_entry** pzce = NULL;
+#endif
+        
+        apc_cache_entry_t* cache_entry =
+            (apc_cache_entry_t*) apc_stack_pop(APCG(cache_stack));
+
+        if (cache_entry->data.file.classes) {
+            for (i = 0; cache_entry->data.file.classes[i].class_entry != NULL; i++) {
+#ifdef ZEND_ENGINE_2
+                centry = (void**)&pzce; /* a triple indirection to get zend_class_entry*** */
+#endif
+                if(zend_hash_find(EG(class_table), 
+                    cache_entry->data.file.classes[i].name,
+                    cache_entry->data.file.classes[i].name_len+1,
+                    (void**)centry) == FAILURE)
+                {
+                    /* double inclusion of conditional classes ends up failing 
+                     * this lookup the second time around.
+                     */
+                    continue;
+                }
+
+#ifdef ZEND_ENGINE_2
+                zce = *pzce;
+#endif
+                zend_hash_del(EG(class_table),
+                    cache_entry->data.file.classes[i].name,
+                    cache_entry->data.file.classes[i].name_len+1);
+                
+                apc_free_class_entry_after_execution(zce);
+            }
+        }
+        apc_cache_release(apc_cache, cache_entry);
+    }
+    if(APCG(localcache)) {
+        apc_local_cache_cleanup(APCG(lcache)); 
+    }
+}
+/* }}} */
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: expandtab sw=4 ts=4 sts=4 fdm=marker
+ * vim<600: expandtab sw=4 ts=4 sts=4
+ */