From f25ecdacf805f840f743b67b6d84485b2deceb4f Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Thu, 22 Jun 2017 01:45:28 +0300 Subject: [PATCH] shutdown_executor() refactoring (reuse opcache fast request shutdown code) --- Zend/zend.c | 25 ---- Zend/zend_compile.h | 4 - Zend/zend_constants.c | 24 --- Zend/zend_constants.h | 1 - Zend/zend_execute_API.c | 206 ++++++++++++-------------- Zend/zend_hash.h | 36 ++++- Zend/zend_objects_API.c | 43 ++++-- Zend/zend_objects_API.h | 2 +- Zend/zend_opcode.c | 45 ------ ext/opcache/README | 6 - ext/opcache/ZendAccelerator.c | 192 ------------------------ ext/opcache/ZendAccelerator.h | 1 - ext/opcache/zend_accelerator_module.c | 2 - main/streams/streams.c | 30 ---- 14 files changed, 158 insertions(+), 459 deletions(-) diff --git a/Zend/zend.c b/Zend/zend.c index d51bd277a6..a5434c2e5e 100644 --- a/Zend/zend.c +++ b/Zend/zend.c @@ -903,31 +903,6 @@ void zend_post_startup(void) /* {{{ */ void zend_shutdown(void) /* {{{ */ { zend_destroy_rsrc_list(&EG(persistent_list)); - if (EG(active)) - { - /* - * The order of destruction is important here. - * See bugs #65463 and 66036. - */ - zend_function *func; - zend_class_entry *ce; - - ZEND_HASH_REVERSE_FOREACH_PTR(GLOBAL_FUNCTION_TABLE, func) { - if (func->type == ZEND_USER_FUNCTION) { - zend_cleanup_op_array_data((zend_op_array *) func); - } - } ZEND_HASH_FOREACH_END(); - ZEND_HASH_REVERSE_FOREACH_PTR(GLOBAL_CLASS_TABLE, ce) { - if (ce->type == ZEND_USER_CLASS) { - zend_cleanup_user_class_data(ce); - } else { - break; - } - } ZEND_HASH_FOREACH_END(); - zend_cleanup_internal_classes(); - zend_hash_reverse_apply(GLOBAL_FUNCTION_TABLE, (apply_func_t) clean_non_persistent_function_full); - zend_hash_reverse_apply(GLOBAL_CLASS_TABLE, (apply_func_t) clean_non_persistent_class_full); - } zend_destroy_modules(); virtual_cwd_deactivate(); diff --git a/Zend/zend_compile.h b/Zend/zend_compile.h index 4264232bc4..99f8e37240 100644 --- a/Zend/zend_compile.h +++ b/Zend/zend_compile.h @@ -779,12 +779,8 @@ ZEND_API int open_file_for_scanning(zend_file_handle *file_handle); ZEND_API void init_op_array(zend_op_array *op_array, zend_uchar type, int initial_ops_size); ZEND_API void destroy_op_array(zend_op_array *op_array); ZEND_API void zend_destroy_file_handle(zend_file_handle *file_handle); -ZEND_API void zend_cleanup_user_class_data(zend_class_entry *ce); ZEND_API void zend_cleanup_internal_class_data(zend_class_entry *ce); ZEND_API void zend_cleanup_internal_classes(void); -ZEND_API void zend_cleanup_op_array_data(zend_op_array *op_array); -ZEND_API int clean_non_persistent_function_full(zval *zv); -ZEND_API int clean_non_persistent_class_full(zval *zv); ZEND_API void destroy_zend_function(zend_function *function); ZEND_API void zend_function_dtor(zval *zv); diff --git a/Zend/zend_constants.c b/Zend/zend_constants.c index 77124fcf12..4d128c4843 100644 --- a/Zend/zend_constants.c +++ b/Zend/zend_constants.c @@ -69,20 +69,6 @@ void zend_copy_constants(HashTable *target, HashTable *source) } -static int clean_non_persistent_constant(zval *zv) -{ - zend_constant *c = Z_PTR_P(zv); - return (c->flags & CONST_PERSISTENT) ? ZEND_HASH_APPLY_STOP : ZEND_HASH_APPLY_REMOVE; -} - - -static int clean_non_persistent_constant_full(zval *zv) -{ - zend_constant *c = Z_PTR_P(zv); - return (c->flags & CONST_PERSISTENT) ? 0 : 1; -} - - static int clean_module_constant(zval *el, void *arg) { zend_constant *c = (zend_constant *)Z_PTR_P(el); @@ -152,16 +138,6 @@ int zend_shutdown_constants(void) return SUCCESS; } - -void clean_non_persistent_constants(void) -{ - if (EG(full_tables_cleanup)) { - zend_hash_apply(EG(zend_constants), clean_non_persistent_constant_full); - } else { - zend_hash_reverse_apply(EG(zend_constants), clean_non_persistent_constant); - } -} - ZEND_API void zend_register_null_constant(const char *name, size_t name_len, int flags, int module_number) { zend_constant c; diff --git a/Zend/zend_constants.h b/Zend/zend_constants.h index 531ef665d8..3622cc45b8 100644 --- a/Zend/zend_constants.h +++ b/Zend/zend_constants.h @@ -64,7 +64,6 @@ void free_zend_constant(zval *zv); int zend_startup_constants(void); int zend_shutdown_constants(void); void zend_register_standard_constants(void); -void clean_non_persistent_constants(void); ZEND_API int zend_verify_const_access(zend_class_constant *c, zend_class_entry *ce); ZEND_API zval *zend_get_constant(zend_string *name); ZEND_API zval *zend_get_constant_str(const char *name, size_t name_len); diff --git a/Zend/zend_execute_API.c b/Zend/zend_execute_API.c index ef29546ac0..3c453e64ef 100644 --- a/Zend/zend_execute_API.c +++ b/Zend/zend_execute_API.c @@ -100,28 +100,21 @@ static void zend_extension_deactivator(zend_extension *extension) /* {{{ */ } /* }}} */ -static int clean_non_persistent_function(zval *zv) /* {{{ */ +static int clean_non_persistent_constant_full(zval *zv) /* {{{ */ { - zend_function *function = Z_PTR_P(zv); - return (function->type == ZEND_INTERNAL_FUNCTION) ? ZEND_HASH_APPLY_STOP : ZEND_HASH_APPLY_REMOVE; + zend_constant *c = Z_PTR_P(zv); + return (c->flags & CONST_PERSISTENT) ? ZEND_HASH_APPLY_KEEP : ZEND_HASH_APPLY_REMOVE; } /* }}} */ -ZEND_API int clean_non_persistent_function_full(zval *zv) /* {{{ */ +static int clean_non_persistent_function_full(zval *zv) /* {{{ */ { zend_function *function = Z_PTR_P(zv); return (function->type == ZEND_INTERNAL_FUNCTION) ? ZEND_HASH_APPLY_KEEP : ZEND_HASH_APPLY_REMOVE; } /* }}} */ -static int clean_non_persistent_class(zval *zv) /* {{{ */ -{ - zend_class_entry *ce = Z_PTR_P(zv); - return (ce->type == ZEND_INTERNAL_CLASS) ? ZEND_HASH_APPLY_STOP : ZEND_HASH_APPLY_REMOVE; -} -/* }}} */ - -ZEND_API int clean_non_persistent_class_full(zval *zv) /* {{{ */ +static int clean_non_persistent_class_full(zval *zv) /* {{{ */ { zend_class_entry *ce = Z_PTR_P(zv); return (ce->type == ZEND_INTERNAL_CLASS) ? ZEND_HASH_APPLY_KEEP : ZEND_HASH_APPLY_REMOVE; @@ -253,123 +246,117 @@ void shutdown_destructors(void) /* {{{ */ void shutdown_executor(void) /* {{{ */ { - zend_function *func; - zend_class_entry *ce; + zend_string *key; + zval *zv; +#if ZEND_DEBUG + zend_bool fast_shutdown = 0; +#else + zend_bool fast_shutdown = is_zend_mm() && !EG(full_tables_cleanup); +#endif zend_try { + zend_llist_destroy(&CG(open_files)); + } zend_end_try(); -/* Removed because this can not be safely done, e.g. in this situation: - Object 1 creates object 2 - Object 3 holds reference to object 2. - Now when 1 and 2 are destroyed, 3 can still access 2 in its destructor, with - very problematic results */ -/* zend_objects_store_call_destructors(&EG(objects_store)); */ + zend_try { + zend_close_rsrc_list(&EG(regular_list)); + } zend_end_try(); -/* Moved after symbol table cleaners, because some of the cleaners can call - destructors, which would use EG(symtable_cache_ptr) and thus leave leaks */ -/* while (EG(symtable_cache_ptr)>=EG(symtable_cache)) { - zend_hash_destroy(*EG(symtable_cache_ptr)); - efree(*EG(symtable_cache_ptr)); - EG(symtable_cache_ptr)--; - } -*/ - zend_llist_apply(&zend_extensions, (llist_apply_func_t) zend_extension_deactivator); + zend_objects_store_free_object_storage(&EG(objects_store), fast_shutdown); - zend_hash_graceful_reverse_destroy(&EG(symbol_table)); + /* All resources and objects are destroyed. */ + /* No PHP callback functions may be called after this point. */ + + zend_try { + zend_llist_apply(&zend_extensions, (llist_apply_func_t) zend_extension_deactivator); } zend_end_try(); + EG(valid_symbol_table) = 0; - zend_try { - zval *zeh; + if (fast_shutdown) { + /* Fast Request Shutdown + * ===================== + * Zend Memory Manager frees memory by its own. We don't have to free + * each allocated block separately. + */ + ZEND_HASH_REVERSE_FOREACH_VAL(EG(zend_constants), zv) { + zend_constant *c = Z_PTR_P(zv); + if (c->flags & CONST_PERSISTENT) { + break; + } + } ZEND_HASH_FOREACH_END_DEL(); + ZEND_HASH_REVERSE_FOREACH_VAL(EG(function_table), zv) { + zend_function *func = Z_PTR_P(zv); + if (func->type == ZEND_INTERNAL_FUNCTION) { + break; + } + } ZEND_HASH_FOREACH_END_DEL(); + ZEND_HASH_REVERSE_FOREACH_VAL(EG(class_table), zv) { + zend_class_entry *ce = Z_PTR_P(zv); + if (ce->type == ZEND_INTERNAL_CLASS) { + break; + } + } ZEND_HASH_FOREACH_END_DEL(); + } else { + zend_hash_graceful_reverse_destroy(&EG(symbol_table)); + +#if ZEND_DEBUG + if (GC_G(gc_enabled) && !CG(unclean_shutdown)) { + gc_collect_cycles(); + } +#endif + /* remove error handlers before destroying classes and functions, * so that if handler used some class, crash would not happen */ if (Z_TYPE(EG(user_error_handler)) != IS_UNDEF) { - zeh = &EG(user_error_handler); - zval_ptr_dtor(zeh); + zval_ptr_dtor(&EG(user_error_handler)); ZVAL_UNDEF(&EG(user_error_handler)); } if (Z_TYPE(EG(user_exception_handler)) != IS_UNDEF) { - zeh = &EG(user_exception_handler); - zval_ptr_dtor(zeh); + zval_ptr_dtor(&EG(user_exception_handler)); ZVAL_UNDEF(&EG(user_exception_handler)); } zend_stack_clean(&EG(user_error_handlers_error_reporting), NULL, 1); zend_stack_clean(&EG(user_error_handlers), (void (*)(void *))ZVAL_PTR_DTOR, 1); zend_stack_clean(&EG(user_exception_handlers), (void (*)(void *))ZVAL_PTR_DTOR, 1); - } zend_end_try(); - zend_try { - /* Cleanup static data for functions and arrays. - * We need a separate cleanup stage because of the following problem: - * Suppose we destroy class X, which destroys the class's function table, - * and in the function table we have function foo() that has static $bar. - * Now if an object of class X is assigned to $bar, its destructor will be - * called and will fail since X's function table is in mid-destruction. - * So we want first of all to clean up all data and then move to tables destruction. - * Note that only run-time accessed data need to be cleaned up, pre-defined data can - * not contain objects and thus are not probelmatic */ + zend_vm_stack_destroy(); + if (EG(full_tables_cleanup)) { - ZEND_HASH_FOREACH_PTR(EG(function_table), func) { - if (func->type == ZEND_USER_FUNCTION) { - zend_cleanup_op_array_data((zend_op_array *) func); + zend_hash_reverse_apply(EG(zend_constants), clean_non_persistent_constant_full); + zend_hash_reverse_apply(EG(function_table), clean_non_persistent_function_full); + zend_hash_reverse_apply(EG(class_table), clean_non_persistent_class_full); + } else { + ZEND_HASH_REVERSE_FOREACH_STR_KEY_VAL(EG(zend_constants), key, zv) { + zend_constant *c = Z_PTR_P(zv); + if (c->flags & CONST_PERSISTENT) { + break; } - } ZEND_HASH_FOREACH_END(); - ZEND_HASH_REVERSE_FOREACH_PTR(EG(class_table), ce) { - if (ce->type == ZEND_USER_CLASS) { - zend_cleanup_user_class_data(ce); - } else { - zend_cleanup_internal_class_data(ce); + zval_ptr_dtor(&c->value); + if (c->name) { + zend_string_release(c->name); } - } ZEND_HASH_FOREACH_END(); - } else { - ZEND_HASH_REVERSE_FOREACH_PTR(EG(function_table), func) { - if (func->type != ZEND_USER_FUNCTION) { + efree(c); + zend_string_release(key); + } ZEND_HASH_FOREACH_END_DEL(); + ZEND_HASH_REVERSE_FOREACH_STR_KEY_VAL(EG(function_table), key, zv) { + zend_function *func = Z_PTR_P(zv); + if (func->type == ZEND_INTERNAL_FUNCTION) { break; } - zend_cleanup_op_array_data((zend_op_array *) func); - } ZEND_HASH_FOREACH_END(); - ZEND_HASH_REVERSE_FOREACH_PTR(EG(class_table), ce) { - if (ce->type != ZEND_USER_CLASS) { + destroy_op_array(&func->op_array); + zend_string_release(key); + } ZEND_HASH_FOREACH_END_DEL(); + ZEND_HASH_REVERSE_FOREACH_STR_KEY_VAL(EG(class_table), key, zv) { + zend_class_entry *ce = Z_PTR_P(zv); + if (ce->type == ZEND_INTERNAL_CLASS) { break; } - zend_cleanup_user_class_data(ce); - } ZEND_HASH_FOREACH_END(); - zend_cleanup_internal_classes(); - } - } zend_end_try(); - - zend_try { - zend_llist_destroy(&CG(open_files)); - } zend_end_try(); - - zend_try { - clean_non_persistent_constants(); - } zend_end_try(); - - zend_try { - zend_close_rsrc_list(&EG(regular_list)); - } zend_end_try(); - -#if ZEND_DEBUG - if (GC_G(gc_enabled) && !CG(unclean_shutdown)) { - gc_collect_cycles(); - } -#endif - - zend_try { - zend_objects_store_free_object_storage(&EG(objects_store)); - - zend_vm_stack_destroy(); - - /* Destroy all op arrays */ - if (EG(full_tables_cleanup)) { - zend_hash_reverse_apply(EG(function_table), clean_non_persistent_function_full); - zend_hash_reverse_apply(EG(class_table), clean_non_persistent_class_full); - } else { - zend_hash_reverse_apply(EG(function_table), clean_non_persistent_function); - zend_hash_reverse_apply(EG(class_table), clean_non_persistent_class); + destroy_zend_class(zv); + zend_string_release(key); + } ZEND_HASH_FOREACH_END_DEL(); } while (EG(symtable_cache_ptr)>=EG(symtable_cache)) { @@ -377,12 +364,6 @@ void shutdown_executor(void) /* {{{ */ FREE_HASHTABLE(*EG(symtable_cache_ptr)); EG(symtable_cache_ptr)--; } - } zend_end_try(); - - zend_try { -#if 0&&ZEND_DEBUG - signal(SIGSEGV, original_sigsegv_handler); -#endif zend_hash_destroy(&EG(included_files)); @@ -394,9 +375,11 @@ void shutdown_executor(void) /* {{{ */ zend_hash_destroy(EG(in_autoload)); FREE_HASHTABLE(EG(in_autoload)); } - } zend_end_try(); - zend_shutdown_fpu(); + if (EG(ht_iterators) != EG(ht_iterators_slots)) { + efree(EG(ht_iterators)); + } + } #if ZEND_DEBUG if (EG(ht_iterators_used) && !CG(unclean_shutdown)) { @@ -405,9 +388,10 @@ void shutdown_executor(void) /* {{{ */ #endif EG(ht_iterators_used) = 0; - if (EG(ht_iterators) != EG(ht_iterators_slots)) { - efree(EG(ht_iterators)); - } + + zend_cleanup_internal_classes(); + + zend_shutdown_fpu(); EG(active) = 0; } diff --git a/Zend/zend_hash.h b/Zend/zend_hash.h index 638fbe27fc..ef0d23bbbf 100644 --- a/Zend/zend_hash.h +++ b/Zend/zend_hash.h @@ -800,8 +800,9 @@ static zend_always_inline void *zend_hash_get_current_data_ptr_ex(HashTable *ht, zend_hash_get_current_data_ptr_ex(ht, &(ht)->nInternalPointer) #define ZEND_HASH_FOREACH(_ht, indirect) do { \ - Bucket *_p = (_ht)->arData; \ - Bucket *_end = _p + (_ht)->nNumUsed; \ + HashTable *__ht = (_ht); \ + Bucket *_p = __ht->arData; \ + Bucket *_end = _p + __ht->nNumUsed; \ for (; _p != _end; _p++) { \ zval *_z = &_p->val; \ if (indirect && Z_TYPE_P(_z) == IS_INDIRECT) { \ @@ -810,9 +811,10 @@ static zend_always_inline void *zend_hash_get_current_data_ptr_ex(HashTable *ht, if (UNEXPECTED(Z_TYPE_P(_z) == IS_UNDEF)) continue; #define ZEND_HASH_REVERSE_FOREACH(_ht, indirect) do { \ + HashTable *__ht = (_ht); \ uint32_t _idx; \ - for (_idx = (_ht)->nNumUsed; _idx > 0; _idx--) { \ - Bucket *_p = (_ht)->arData + _idx - 1; \ + for (_idx = __ht->nNumUsed; _idx > 0; _idx--) { \ + Bucket *_p = __ht->arData + (_idx - 1); \ zval *_z = &_p->val; \ if (indirect && Z_TYPE_P(_z) == IS_INDIRECT) { \ _z = Z_INDIRECT_P(_z); \ @@ -823,6 +825,27 @@ static zend_always_inline void *zend_hash_get_current_data_ptr_ex(HashTable *ht, } \ } while (0) +#define ZEND_HASH_FOREACH_END_DEL() \ + __ht->nNumOfElements--; \ + do { \ + uint32_t j = HT_IDX_TO_HASH(_idx - 1); \ + uint32_t nIndex = _p->h | __ht->nTableMask; \ + uint32_t i = HT_HASH(__ht, nIndex); \ + if (j != i) { \ + Bucket *prev = HT_HASH_TO_BUCKET(__ht, i); \ + while (Z_NEXT(prev->val) != j) { \ + i = Z_NEXT(prev->val); \ + prev = HT_HASH_TO_BUCKET(__ht, i); \ + } \ + Z_NEXT(prev->val) = Z_NEXT(_p->val); \ + } else { \ + HT_HASH(__ht, _p->h | __ht->nTableMask) = Z_NEXT(_p->val); \ + } \ + } while (0); \ + } \ + __ht->nNumUsed = _idx; \ + } while (0) + #define ZEND_HASH_FOREACH_BUCKET(ht, _bucket) \ ZEND_HASH_FOREACH(ht, 0); \ _bucket = _p; @@ -911,6 +934,11 @@ static zend_always_inline void *zend_hash_get_current_data_ptr_ex(HashTable *ht, ZEND_HASH_REVERSE_FOREACH(ht, 1); \ _val = _z; +#define ZEND_HASH_REVERSE_FOREACH_STR_KEY_VAL(ht, _key, _val) \ + ZEND_HASH_REVERSE_FOREACH(ht, 0); \ + _key = _p->key; \ + _val = _z; + #define ZEND_HASH_REVERSE_FOREACH_KEY_VAL(ht, _h, _key, _val) \ ZEND_HASH_REVERSE_FOREACH(ht, 0); \ _h = _p->h; \ diff --git a/Zend/zend_objects_API.c b/Zend/zend_objects_API.c index 53a0c3be23..f838eddc97 100644 --- a/Zend/zend_objects_API.c +++ b/Zend/zend_objects_API.c @@ -76,7 +76,7 @@ ZEND_API void zend_objects_store_mark_destructed(zend_objects_store *objects) } } -ZEND_API void zend_objects_store_free_object_storage(zend_objects_store *objects) +ZEND_API void zend_objects_store_free_object_storage(zend_objects_store *objects, zend_bool fast_shutdown) { zend_object **obj_ptr, **end, *obj; @@ -88,20 +88,37 @@ ZEND_API void zend_objects_store_free_object_storage(zend_objects_store *objects end = objects->object_buckets + 1; obj_ptr = objects->object_buckets + objects->top; - do { - obj_ptr--; - obj = *obj_ptr; - if (IS_OBJ_VALID(obj)) { - if (!(GC_FLAGS(obj) & IS_OBJ_FREE_CALLED)) { - GC_FLAGS(obj) |= IS_OBJ_FREE_CALLED; - if (obj->handlers->free_obj) { - GC_REFCOUNT(obj)++; - obj->handlers->free_obj(obj); - GC_REFCOUNT(obj)--; + if (fast_shutdown) { + do { + obj_ptr--; + obj = *obj_ptr; + if (IS_OBJ_VALID(obj)) { + if (!(GC_FLAGS(obj) & IS_OBJ_FREE_CALLED)) { + GC_FLAGS(obj) |= IS_OBJ_FREE_CALLED; + if (obj->handlers->free_obj && obj->handlers->free_obj != zend_object_std_dtor) { + GC_REFCOUNT(obj)++; + obj->handlers->free_obj(obj); + GC_REFCOUNT(obj)--; + } } } - } - } while (obj_ptr != end); + } while (obj_ptr != end); + } else { + do { + obj_ptr--; + obj = *obj_ptr; + if (IS_OBJ_VALID(obj)) { + if (!(GC_FLAGS(obj) & IS_OBJ_FREE_CALLED)) { + GC_FLAGS(obj) |= IS_OBJ_FREE_CALLED; + if (obj->handlers->free_obj) { + GC_REFCOUNT(obj)++; + obj->handlers->free_obj(obj); + GC_REFCOUNT(obj)--; + } + } + } + } while (obj_ptr != end); + } } diff --git a/Zend/zend_objects_API.h b/Zend/zend_objects_API.h index 223060035e..b105def176 100644 --- a/Zend/zend_objects_API.h +++ b/Zend/zend_objects_API.h @@ -68,7 +68,7 @@ static zend_always_inline void zend_object_store_ctor_failed(zend_object *obj) GC_FLAGS(obj) |= IS_OBJ_DESTRUCTOR_CALLED; } -ZEND_API void zend_objects_store_free_object_storage(zend_objects_store *objects); +ZEND_API void zend_objects_store_free_object_storage(zend_objects_store *objects, zend_bool fast_shutdown); #define ZEND_OBJECTS_STORE_HANDLERS 0, zend_object_std_dtor, zend_objects_destroy_object, zend_objects_clone_obj diff --git a/Zend/zend_opcode.c b/Zend/zend_opcode.c index e8764e964a..a0ec068405 100644 --- a/Zend/zend_opcode.c +++ b/Zend/zend_opcode.c @@ -148,51 +148,6 @@ ZEND_API void zend_function_dtor(zval *zv) } } -ZEND_API void zend_cleanup_op_array_data(zend_op_array *op_array) -{ - if (op_array->static_variables && - !(GC_FLAGS(op_array->static_variables) & IS_ARRAY_IMMUTABLE) - ) { - /* The static variables are initially shared when inheriting methods and will - * be separated on first use. If they are never used, they stay shared. Cleaning - * a shared static variables table is safe, as the intention is to clean all - * such tables. */ - HT_ALLOW_COW_VIOLATION(op_array->static_variables); - - zend_hash_clean(op_array->static_variables); - } -} - -ZEND_API void zend_cleanup_user_class_data(zend_class_entry *ce) -{ - /* Clean all parts that can contain run-time data */ - /* Note that only run-time accessed data need to be cleaned up, pre-defined data can - not contain objects and thus are not probelmatic */ - if (ce->ce_flags & ZEND_HAS_STATIC_IN_METHODS) { - zend_function *func; - - ZEND_HASH_FOREACH_PTR(&ce->function_table, func) { - if (func->type == ZEND_USER_FUNCTION) { - zend_cleanup_op_array_data((zend_op_array *) func); - } - } ZEND_HASH_FOREACH_END(); - } - if (ce->static_members_table) { - zval *static_members = ce->static_members_table; - zval *p = static_members; - zval *end = p + ce->default_static_members_count; - - - ce->default_static_members_count = 0; - ce->default_static_members_table = ce->static_members_table = NULL; - while (p != end) { - i_zval_ptr_dtor(p ZEND_FILE_LINE_CC); - p++; - } - efree(static_members); - } -} - ZEND_API void zend_cleanup_internal_class_data(zend_class_entry *ce) { if (CE_STATIC_MEMBERS(ce)) { diff --git a/ext/opcache/README b/ext/opcache/README index cf305ad9d1..6f07f7ee58 100644 --- a/ext/opcache/README +++ b/ext/opcache/README @@ -47,7 +47,6 @@ opcache.memory_consumption=128 opcache.interned_strings_buffer=8 opcache.max_accelerated_files=4000 opcache.revalidate_freq=60 -opcache.fast_shutdown=1 opcache.enable_cli=1 You also may add the following, but it may break some applications and @@ -137,11 +136,6 @@ opcache.save_comments (default "1") size of the optimized code. Disabling "Doc Comments" may break some existing applications and frameworks (e.g. Doctrine, ZF2, PHPUnit) -opcache.fast_shutdown (default "0") - If enabled, a fast shutdown sequence is used for the accelerated code - The fast shutdown sequence doesn't free each allocated block, but lets - the Zend Engine Memory Manager do the work. - opcache.enable_file_override (default "0") Allow file existence override (file_exists, etc.) performance feature. diff --git a/ext/opcache/ZendAccelerator.c b/ext/opcache/ZendAccelerator.c index e6dc4ce684..7e375e8289 100644 --- a/ext/opcache/ZendAccelerator.c +++ b/ext/opcache/ZendAccelerator.c @@ -2250,192 +2250,6 @@ static void accel_activate(void) } } -#if !ZEND_DEBUG - -/* Fast Request Shutdown - * ===================== - * Zend Memory Manager frees memory by its own. We don't have to free each - * allocated block separately, but we like to call all the destructors and - * callbacks in exactly the same order. - */ -static void accel_fast_hash_destroy(HashTable *ht); - -static void accel_fast_zval_dtor(zval *zvalue) -{ -tail_call: - switch (Z_TYPE_P(zvalue)) { - case IS_ARRAY: - GC_REMOVE_FROM_BUFFER(Z_ARR_P(zvalue)); - if (Z_ARR_P(zvalue) != &EG(symbol_table)) { - /* break possible cycles */ - ZVAL_NULL(zvalue); - accel_fast_hash_destroy(Z_ARRVAL_P(zvalue)); - } - break; - case IS_OBJECT: - OBJ_RELEASE(Z_OBJ_P(zvalue)); - break; - case IS_RESOURCE: - zend_list_delete(Z_RES_P(zvalue)); - break; - case IS_REFERENCE: { - zend_reference *ref = Z_REF_P(zvalue); - - if (--GC_REFCOUNT(ref) == 0) { - if (Z_REFCOUNTED(ref->val) && Z_DELREF(ref->val) == 0) { - zvalue = &ref->val; - goto tail_call; - } - } - } - break; - } -} - -static void accel_fast_hash_destroy(HashTable *ht) -{ - Bucket *p = ht->arData; - Bucket *end = p + ht->nNumUsed; - - while (p != end) { - if (Z_REFCOUNTED(p->val) && Z_DELREF(p->val) == 0) { - accel_fast_zval_dtor(&p->val); - } - p++; - } -} - -static inline void zend_accel_fast_del_bucket(HashTable *ht, uint32_t idx, Bucket *p) -{ - uint32_t nIndex = p->h | ht->nTableMask; - uint32_t i = HT_HASH(ht, nIndex); - - ht->nNumOfElements--; - if (idx != i) { - Bucket *prev = HT_HASH_TO_BUCKET(ht, i); - while (Z_NEXT(prev->val) != idx) { - i = Z_NEXT(prev->val); - prev = HT_HASH_TO_BUCKET(ht, i); - } - Z_NEXT(prev->val) = Z_NEXT(p->val); - } else { - HT_HASH(ht, p->h | ht->nTableMask) = Z_NEXT(p->val); - } -} - -static void zend_accel_fast_shutdown(void) -{ - if (EG(full_tables_cleanup)) { - return; - } - - if (EG(objects_store).top > 1 || zend_hash_num_elements(&EG(regular_list)) > 0) { - /* We don't have to destroy all zvals if they cannot call any destructors */ - zend_try { - ZEND_HASH_REVERSE_FOREACH(&EG(symbol_table), 0) { - if (Z_REFCOUNTED(_p->val) && Z_DELREF(_p->val) == 0) { - accel_fast_zval_dtor(&_p->val); - } - zend_accel_fast_del_bucket(&EG(symbol_table), HT_IDX_TO_HASH(_idx-1), _p); - } ZEND_HASH_FOREACH_END(); - } zend_end_try(); - zend_hash_init(&EG(symbol_table), 8, NULL, NULL, 0); - - ZEND_HASH_REVERSE_FOREACH(EG(function_table), 0) { - zend_function *func = Z_PTR(_p->val); - - if (func->type == ZEND_INTERNAL_FUNCTION) { - break; - } else { - if (func->op_array.static_variables) { - if (!(GC_FLAGS(func->op_array.static_variables) & IS_ARRAY_IMMUTABLE)) { - if (--GC_REFCOUNT(func->op_array.static_variables) == 0) { - accel_fast_hash_destroy(func->op_array.static_variables); - } - } - } - zend_accel_fast_del_bucket(EG(function_table), HT_IDX_TO_HASH(_idx-1), _p); - } - } ZEND_HASH_FOREACH_END(); - - ZEND_HASH_REVERSE_FOREACH(EG(class_table), 0) { - zend_class_entry *ce = Z_PTR(_p->val); - - if (ce->type == ZEND_INTERNAL_CLASS) { - break; - } else { - if (ce->ce_flags & ZEND_HAS_STATIC_IN_METHODS) { - zend_function *func; - - ZEND_HASH_FOREACH_PTR(&ce->function_table, func) { - if (func->type == ZEND_USER_FUNCTION) { - if (func->op_array.static_variables) { - if (!(GC_FLAGS(func->op_array.static_variables) & IS_ARRAY_IMMUTABLE)) { - if (--GC_REFCOUNT(func->op_array.static_variables) == 0) { - accel_fast_hash_destroy(func->op_array.static_variables); - } - } - func->op_array.static_variables = NULL; - } - } - } ZEND_HASH_FOREACH_END(); - } - if (ce->static_members_table) { - int i; - - for (i = 0; i < ce->default_static_members_count; i++) { - zval *zv = &ce->static_members_table[i]; - ZVAL_UNDEF(&ce->static_members_table[i]); - if (Z_REFCOUNTED_P(zv) && Z_DELREF_P(zv) == 0) { - accel_fast_zval_dtor(zv); - } - } - ce->static_members_table = NULL; - } - zend_accel_fast_del_bucket(EG(class_table), HT_IDX_TO_HASH(_idx-1), _p); - } - } ZEND_HASH_FOREACH_END(); - - } else { - - zend_hash_init(&EG(symbol_table), 8, NULL, NULL, 0); - - ZEND_HASH_REVERSE_FOREACH(EG(function_table), 0) { - zend_function *func = Z_PTR(_p->val); - - if (func->type == ZEND_INTERNAL_FUNCTION) { - break; - } else { - zend_accel_fast_del_bucket(EG(function_table), HT_IDX_TO_HASH(_idx-1), _p); - } - } ZEND_HASH_FOREACH_END(); - - ZEND_HASH_REVERSE_FOREACH(EG(class_table), 0) { - zend_class_entry *ce = Z_PTR(_p->val); - - if (ce->type == ZEND_INTERNAL_CLASS) { - break; - } else { - zend_accel_fast_del_bucket(EG(class_table), HT_IDX_TO_HASH(_idx-1), _p); - } - } ZEND_HASH_FOREACH_END(); - } - - ZEND_HASH_REVERSE_FOREACH(EG(zend_constants), 0) { - zend_constant *c = Z_PTR(_p->val); - - if (c->flags & CONST_PERSISTENT) { - break; - } else { - zend_accel_fast_del_bucket(EG(zend_constants), HT_IDX_TO_HASH(_idx-1), _p); - } - } ZEND_HASH_FOREACH_END(); - EG(function_table)->nNumUsed = EG(function_table)->nNumOfElements; - EG(class_table)->nNumUsed = EG(class_table)->nNumOfElements; - EG(zend_constants)->nNumUsed = EG(zend_constants)->nNumOfElements; -} -#endif - int accel_post_deactivate(void) { if (!ZCG(enabled) || !accel_startup_ok) { @@ -2464,12 +2278,6 @@ static void accel_deactivate(void) if (!ZCG(enabled) || !accel_startup_ok) { return; } - -#if !ZEND_DEBUG - if (ZCG(accel_directives).fast_shutdown && is_zend_mm()) { - zend_accel_fast_shutdown(); - } -#endif } static int accelerator_remove_cb(zend_extension *element1, zend_extension *element2) diff --git a/ext/opcache/ZendAccelerator.h b/ext/opcache/ZendAccelerator.h index 6faa263db2..17b6f8da52 100644 --- a/ext/opcache/ZendAccelerator.h +++ b/ext/opcache/ZendAccelerator.h @@ -175,7 +175,6 @@ typedef struct _zend_accel_directives { zend_bool validate_timestamps; zend_bool revalidate_path; zend_bool save_comments; - zend_bool fast_shutdown; zend_bool protect_memory; zend_bool file_override_enabled; zend_bool inherited_hack; diff --git a/ext/opcache/zend_accelerator_module.c b/ext/opcache/zend_accelerator_module.c index 6f76851455..6c8efac7c7 100644 --- a/ext/opcache/zend_accelerator_module.c +++ b/ext/opcache/zend_accelerator_module.c @@ -299,7 +299,6 @@ ZEND_INI_BEGIN() STD_PHP_INI_ENTRY("opcache.protect_memory" , "0" , PHP_INI_SYSTEM, OnUpdateBool, accel_directives.protect_memory, zend_accel_globals, accel_globals) STD_PHP_INI_ENTRY("opcache.save_comments" , "1" , PHP_INI_SYSTEM, OnUpdateBool, accel_directives.save_comments, zend_accel_globals, accel_globals) - STD_PHP_INI_ENTRY("opcache.fast_shutdown" , "0" , PHP_INI_SYSTEM, OnUpdateBool, accel_directives.fast_shutdown, zend_accel_globals, accel_globals) STD_PHP_INI_ENTRY("opcache.optimization_level" , DEFAULT_OPTIMIZATION_LEVEL , PHP_INI_SYSTEM, OnUpdateLong, accel_directives.optimization_level, zend_accel_globals, accel_globals) STD_PHP_INI_ENTRY("opcache.opt_debug_level" , "0" , PHP_INI_SYSTEM, OnUpdateLong, accel_directives.opt_debug_level, zend_accel_globals, accel_globals) @@ -722,7 +721,6 @@ static ZEND_FUNCTION(opcache_get_configuration) add_assoc_bool(&directives, "opcache.protect_memory", ZCG(accel_directives).protect_memory); add_assoc_bool(&directives, "opcache.save_comments", ZCG(accel_directives).save_comments); - add_assoc_bool(&directives, "opcache.fast_shutdown", ZCG(accel_directives).fast_shutdown); add_assoc_bool(&directives, "opcache.enable_file_override", ZCG(accel_directives).file_override_enabled); add_assoc_long(&directives, "opcache.optimization_level", ZCG(accel_directives).optimization_level); diff --git a/main/streams/streams.c b/main/streams/streams.c index 25898cd2c4..23d46455c1 100644 --- a/main/streams/streams.c +++ b/main/streams/streams.c @@ -502,43 +502,13 @@ fprintf(stderr, "stream_free: %s:%p[%s] preserve_handle=%d release_cast=%d remov /* we don't work with *stream but need its value for comparison */ zend_hash_apply_with_argument(&EG(persistent_list), _php_stream_free_persistent, stream); } -#if ZEND_DEBUG - if ((close_options & PHP_STREAM_FREE_RSRC_DTOR) && (stream->__exposed == 0) && (EG(error_reporting) & E_WARNING)) { - /* it leaked: Lets deliberately NOT pefree it so that the memory manager shows it - * as leaked; it will log a warning, but lets help it out and display what kind - * of stream it was. */ - if (!CG(unclean_shutdown)) { - char *leakinfo; - spprintf(&leakinfo, 0, __FILE__ "(%d) : Stream of type '%s' %p (path:%s) was not closed\n", __LINE__, stream->ops->label, stream, stream->orig_path); - - if (stream->orig_path) { - pefree(stream->orig_path, stream->is_persistent); - stream->orig_path = NULL; - } -# if defined(PHP_WIN32) - OutputDebugString(leakinfo); -# else - fprintf(stderr, "%s", leakinfo); -# endif - efree(leakinfo); - } - } else { - if (stream->orig_path) { - pefree(stream->orig_path, stream->is_persistent); - stream->orig_path = NULL; - } - - pefree(stream, stream->is_persistent); - } -#else if (stream->orig_path) { pefree(stream->orig_path, stream->is_persistent); stream->orig_path = NULL; } pefree(stream, stream->is_persistent); -#endif } if (context) { -- 2.40.0