mirror of
https://github.com/php/php-src.git
synced 2025-08-15 21:48:51 +02:00
shutdown_executor() refactoring (reuse opcache fast request shutdown code)
This commit is contained in:
parent
9fb0e6ffe2
commit
f25ecdacf8
14 changed files with 169 additions and 470 deletions
25
Zend/zend.c
25
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();
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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_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)); */
|
||||
|
||||
/* 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_hash_graceful_reverse_destroy(&EG(symbol_table));
|
||||
} zend_end_try();
|
||||
EG(valid_symbol_table) = 0;
|
||||
|
||||
zend_try {
|
||||
zval *zeh;
|
||||
/* 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_UNDEF(&EG(user_error_handler));
|
||||
}
|
||||
|
||||
if (Z_TYPE(EG(user_exception_handler)) != IS_UNDEF) {
|
||||
zeh = &EG(user_exception_handler);
|
||||
zval_ptr_dtor(zeh);
|
||||
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 */
|
||||
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_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);
|
||||
}
|
||||
} ZEND_HASH_FOREACH_END();
|
||||
} else {
|
||||
ZEND_HASH_REVERSE_FOREACH_PTR(EG(function_table), func) {
|
||||
if (func->type != ZEND_USER_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) {
|
||||
break;
|
||||
}
|
||||
zend_cleanup_user_class_data(ce);
|
||||
} ZEND_HASH_FOREACH_END();
|
||||
zend_cleanup_internal_classes();
|
||||
}
|
||||
} zend_end_try();
|
||||
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();
|
||||
|
||||
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_objects_store_free_object_storage(&EG(objects_store), fast_shutdown);
|
||||
|
||||
/* All resources and objects are destroyed. */
|
||||
/* No PHP callback functions may be called after this point. */
|
||||
|
||||
zend_try {
|
||||
zend_objects_store_free_object_storage(&EG(objects_store));
|
||||
zend_llist_apply(&zend_extensions, (llist_apply_func_t) zend_extension_deactivator);
|
||||
} zend_end_try();
|
||||
|
||||
EG(valid_symbol_table) = 0;
|
||||
|
||||
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) {
|
||||
zval_ptr_dtor(&EG(user_error_handler));
|
||||
ZVAL_UNDEF(&EG(user_error_handler));
|
||||
}
|
||||
|
||||
if (Z_TYPE(EG(user_exception_handler)) != IS_UNDEF) {
|
||||
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_vm_stack_destroy();
|
||||
|
||||
/* Destroy all op arrays */
|
||||
if (EG(full_tables_cleanup)) {
|
||||
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_apply(EG(function_table), clean_non_persistent_function);
|
||||
zend_hash_reverse_apply(EG(class_table), clean_non_persistent_class);
|
||||
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;
|
||||
}
|
||||
zval_ptr_dtor(&c->value);
|
||||
if (c->name) {
|
||||
zend_string_release(c->name);
|
||||
}
|
||||
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;
|
||||
}
|
||||
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;
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -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; \
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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)) {
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue