Skip to content

Commit 7d4e18b

Browse files
committed
Improved user iterator implementation to reduce zend_class_entry memory consumption and avoid race condition during resolving/caching of user iterator functions of internal classes in ZTS build.
1 parent 0834679 commit 7d4e18b

17 files changed

+120
-85
lines changed

Zend/zend.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,8 @@ struct _zend_class_entry {
139139
union _zend_function *serialize_func;
140140
union _zend_function *unserialize_func;
141141

142-
zend_class_iterator_funcs iterator_funcs;
142+
/* allocated only if class implements Itetrator or IteratorAggregate interface */
143+
zend_class_iterator_funcs *iterator_funcs_ptr;
143144

144145
/* handlers */
145146
zend_object* (*create_object)(zend_class_entry *class_type);

Zend/zend_API.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -189,8 +189,9 @@ typedef struct _zend_fcall_info_cache {
189189

190190
#define INIT_CLASS_ENTRY_EX(class_container, class_name, class_name_len, functions) \
191191
{ \
192+
memset(&class_container, 0, sizeof(zend_class_entry)); \
192193
class_container.name = zend_string_init_interned(class_name, class_name_len, 1); \
193-
INIT_CLASS_ENTRY_INIT_METHODS(class_container, functions) \
194+
class_container.info.internal.builtin_functions = functions; \
194195
}
195196

196197
#define INIT_CLASS_ENTRY_INIT_METHODS(class_container, functions) \
@@ -221,7 +222,7 @@ typedef struct _zend_fcall_info_cache {
221222
class_container.trait_precedences = NULL; \
222223
class_container.interfaces = NULL; \
223224
class_container.get_iterator = NULL; \
224-
class_container.iterator_funcs.funcs = NULL; \
225+
class_container.iterator_funcs_ptr = NULL; \
225226
class_container.info.internal.module = NULL; \
226227
class_container.info.internal.builtin_functions = functions; \
227228
}

Zend/zend_compile.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1750,7 +1750,7 @@ ZEND_API void zend_initialize_class_data(zend_class_entry *ce, zend_bool nullify
17501750
ce->__tostring = NULL;
17511751
ce->create_object = NULL;
17521752
ce->get_iterator = NULL;
1753-
ce->iterator_funcs.funcs = NULL;
1753+
ce->iterator_funcs_ptr = NULL;
17541754
ce->interface_gets_implemented = NULL;
17551755
ce->get_static_method = NULL;
17561756
ce->parent = NULL;

Zend/zend_generators.c

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1196,7 +1196,6 @@ void zend_register_generator_ce(void) /* {{{ */
11961196
/* get_iterator has to be assigned *after* implementing the inferface */
11971197
zend_class_implements(zend_ce_generator, 1, zend_ce_iterator);
11981198
zend_ce_generator->get_iterator = zend_generator_get_iterator;
1199-
zend_ce_generator->iterator_funcs.funcs = &zend_generator_iterator_functions;
12001199

12011200
memcpy(&zend_generator_handlers, &std_object_handlers, sizeof(zend_object_handlers));
12021201
zend_generator_handlers.free_obj = zend_generator_free_storage;

Zend/zend_inheritance.c

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -99,8 +99,23 @@ static void do_inherit_parent_constructor(zend_class_entry *ce) /* {{{ */
9999
if (EXPECTED(!ce->get_iterator)) {
100100
ce->get_iterator = ce->parent->get_iterator;
101101
}
102-
if (EXPECTED(!ce->iterator_funcs.funcs)) {
103-
ce->iterator_funcs.funcs = ce->parent->iterator_funcs.funcs;
102+
if (EXPECTED(!ce->iterator_funcs_ptr) && UNEXPECTED(ce->parent->iterator_funcs_ptr)) {
103+
if (ce->type == ZEND_INTERNAL_CLASS) {
104+
ce->iterator_funcs_ptr = calloc(1, sizeof(zend_class_iterator_funcs));
105+
if (ce->parent->iterator_funcs_ptr->zf_new_iterator) {
106+
ce->iterator_funcs_ptr->zf_new_iterator = zend_hash_str_find_ptr(&ce->function_table, "getiterator", sizeof("getiterator") - 1);
107+
}
108+
if (ce->parent->iterator_funcs_ptr->zf_current) {
109+
ce->iterator_funcs_ptr->zf_rewind = zend_hash_str_find_ptr(&ce->function_table, "rewind", sizeof("rewind") - 1);
110+
ce->iterator_funcs_ptr->zf_valid = zend_hash_str_find_ptr(&ce->function_table, "valid", sizeof("valid") - 1);
111+
ce->iterator_funcs_ptr->zf_key = zend_hash_str_find_ptr(&ce->function_table, "key", sizeof("key") - 1);
112+
ce->iterator_funcs_ptr->zf_current = zend_hash_str_find_ptr(&ce->function_table, "current", sizeof("current") - 1);
113+
ce->iterator_funcs_ptr->zf_next = zend_hash_str_find_ptr(&ce->function_table, "next", sizeof("next") - 1);
114+
}
115+
} else {
116+
ce->iterator_funcs_ptr = zend_arena_alloc(&CG(arena), sizeof(zend_class_iterator_funcs));
117+
memset(ce->iterator_funcs_ptr, 0, sizeof(zend_class_iterator_funcs));
118+
}
104119
}
105120
if (EXPECTED(!ce->__get)) {
106121
ce->__get = ce->parent->__get;

Zend/zend_interfaces.c

Lines changed: 36 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ ZEND_API zval* zend_call_method(zval *object, zend_class_entry *obj_ce, zend_fun
126126
/* {{{ zend_user_it_new_iterator */
127127
ZEND_API void zend_user_it_new_iterator(zend_class_entry *ce, zval *object, zval *retval)
128128
{
129-
zend_call_method_with_0_params(object, ce, &ce->iterator_funcs.zf_new_iterator, "getiterator", retval);
129+
zend_call_method_with_0_params(object, ce, &ce->iterator_funcs_ptr->zf_new_iterator, "getiterator", retval);
130130
}
131131
/* }}} */
132132

@@ -162,7 +162,7 @@ ZEND_API int zend_user_it_valid(zend_object_iterator *_iter)
162162
zval more;
163163
int result;
164164

165-
zend_call_method_with_0_params(object, iter->ce, &iter->ce->iterator_funcs.zf_valid, "valid", &more);
165+
zend_call_method_with_0_params(object, iter->ce, &iter->ce->iterator_funcs_ptr->zf_valid, "valid", &more);
166166
if (Z_TYPE(more) != IS_UNDEF) {
167167
result = i_zend_is_true(&more);
168168
zval_ptr_dtor(&more);
@@ -180,7 +180,7 @@ ZEND_API zval *zend_user_it_get_current_data(zend_object_iterator *_iter)
180180
zval *object = &iter->it.data;
181181

182182
if (Z_ISUNDEF(iter->value)) {
183-
zend_call_method_with_0_params(object, iter->ce, &iter->ce->iterator_funcs.zf_current, "current", &iter->value);
183+
zend_call_method_with_0_params(object, iter->ce, &iter->ce->iterator_funcs_ptr->zf_current, "current", &iter->value);
184184
}
185185
return &iter->value;
186186
}
@@ -193,7 +193,7 @@ ZEND_API void zend_user_it_get_current_key(zend_object_iterator *_iter, zval *ke
193193
zval *object = &iter->it.data;
194194
zval retval;
195195

196-
zend_call_method_with_0_params(object, iter->ce, &iter->ce->iterator_funcs.zf_key, "key", &retval);
196+
zend_call_method_with_0_params(object, iter->ce, &iter->ce->iterator_funcs_ptr->zf_key, "key", &retval);
197197

198198
if (Z_TYPE(retval) != IS_UNDEF) {
199199
ZVAL_ZVAL(key, &retval, 1, 1);
@@ -214,7 +214,7 @@ ZEND_API void zend_user_it_move_forward(zend_object_iterator *_iter)
214214
zval *object = &iter->it.data;
215215

216216
zend_user_it_invalidate_current(_iter);
217-
zend_call_method_with_0_params(object, iter->ce, &iter->ce->iterator_funcs.zf_next, "next", NULL);
217+
zend_call_method_with_0_params(object, iter->ce, &iter->ce->iterator_funcs_ptr->zf_next, "next", NULL);
218218
}
219219
/* }}} */
220220

@@ -225,7 +225,7 @@ ZEND_API void zend_user_it_rewind(zend_object_iterator *_iter)
225225
zval *object = &iter->it.data;
226226

227227
zend_user_it_invalidate_current(_iter);
228-
zend_call_method_with_0_params(object, iter->ce, &iter->ce->iterator_funcs.zf_rewind, "rewind", NULL);
228+
zend_call_method_with_0_params(object, iter->ce, &iter->ce->iterator_funcs_ptr->zf_rewind, "rewind", NULL);
229229
}
230230
/* }}} */
231231

@@ -254,7 +254,7 @@ static zend_object_iterator *zend_user_it_get_iterator(zend_class_entry *ce, zva
254254
zend_iterator_init((zend_object_iterator*)iterator);
255255

256256
ZVAL_COPY(&iterator->it.data, object);
257-
iterator->it.funcs = ce->iterator_funcs.funcs;
257+
iterator->it.funcs = &zend_interface_iterator_funcs_iterator;
258258
iterator->ce = Z_OBJCE_P(object);
259259
ZVAL_UNDEF(&iterator->value);
260260
return (zend_object_iterator*)iterator;
@@ -339,8 +339,18 @@ static int zend_implement_aggregate(zend_class_entry *interface, zend_class_entr
339339
}
340340
}
341341
}
342-
class_type->iterator_funcs.zf_new_iterator = NULL;
343342
class_type->get_iterator = zend_user_it_get_new_iterator;
343+
if (class_type->iterator_funcs_ptr != NULL) {
344+
class_type->iterator_funcs_ptr->zf_new_iterator = NULL;
345+
} else if (class_type->type == ZEND_INTERNAL_CLASS) {
346+
class_type->iterator_funcs_ptr = calloc(1, sizeof(zend_class_iterator_funcs));
347+
} else {
348+
class_type->iterator_funcs_ptr = zend_arena_alloc(&CG(arena), sizeof(zend_class_iterator_funcs));
349+
memset(class_type->iterator_funcs_ptr, 0, sizeof(zend_class_iterator_funcs));
350+
}
351+
if (class_type->type == ZEND_INTERNAL_CLASS) {
352+
class_type->iterator_funcs_ptr->zf_new_iterator = zend_hash_str_find_ptr(&class_type->function_table, "getiterator", sizeof("getiterator") - 1);
353+
}
344354
return SUCCESS;
345355
}
346356
/* }}} */
@@ -364,13 +374,24 @@ static int zend_implement_iterator(zend_class_entry *interface, zend_class_entry
364374
}
365375
}
366376
class_type->get_iterator = zend_user_it_get_iterator;
367-
class_type->iterator_funcs.zf_valid = NULL;
368-
class_type->iterator_funcs.zf_current = NULL;
369-
class_type->iterator_funcs.zf_key = NULL;
370-
class_type->iterator_funcs.zf_next = NULL;
371-
class_type->iterator_funcs.zf_rewind = NULL;
372-
if (!class_type->iterator_funcs.funcs) {
373-
class_type->iterator_funcs.funcs = &zend_interface_iterator_funcs_iterator;
377+
if (class_type->iterator_funcs_ptr != NULL) {
378+
class_type->iterator_funcs_ptr->zf_valid = NULL;
379+
class_type->iterator_funcs_ptr->zf_current = NULL;
380+
class_type->iterator_funcs_ptr->zf_key = NULL;
381+
class_type->iterator_funcs_ptr->zf_next = NULL;
382+
class_type->iterator_funcs_ptr->zf_rewind = NULL;
383+
} else if (class_type->type == ZEND_INTERNAL_CLASS) {
384+
class_type->iterator_funcs_ptr = calloc(1, sizeof(zend_class_iterator_funcs));
385+
} else {
386+
class_type->iterator_funcs_ptr = zend_arena_alloc(&CG(arena), sizeof(zend_class_iterator_funcs));
387+
memset(class_type->iterator_funcs_ptr, 0, sizeof(zend_class_iterator_funcs));
388+
}
389+
if (class_type->type == ZEND_INTERNAL_CLASS) {
390+
class_type->iterator_funcs_ptr->zf_rewind = zend_hash_str_find_ptr(&class_type->function_table, "rewind", sizeof("rewind") - 1);
391+
class_type->iterator_funcs_ptr->zf_valid = zend_hash_str_find_ptr(&class_type->function_table, "valid", sizeof("valid") - 1);
392+
class_type->iterator_funcs_ptr->zf_key = zend_hash_str_find_ptr(&class_type->function_table, "key", sizeof("key") - 1);
393+
class_type->iterator_funcs_ptr->zf_current = zend_hash_str_find_ptr(&class_type->function_table, "current", sizeof("current") - 1);
394+
class_type->iterator_funcs_ptr->zf_next = zend_hash_str_find_ptr(&class_type->function_table, "next", sizeof("next") - 1);
374395
}
375396
return SUCCESS;
376397
}

Zend/zend_iterators.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,6 @@ struct _zend_object_iterator {
6262
};
6363

6464
typedef struct _zend_class_iterator_funcs {
65-
const zend_object_iterator_funcs *funcs;
6665
union _zend_function *zf_new_iterator;
6766
union _zend_function *zf_valid;
6867
union _zend_function *zf_current;

Zend/zend_opcode.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -318,6 +318,9 @@ ZEND_API void destroy_zend_class(zval *zv)
318318
} ZEND_HASH_FOREACH_END();
319319
zend_hash_destroy(&ce->constants_table);
320320
}
321+
if (ce->iterator_funcs_ptr) {
322+
free(ce->iterator_funcs_ptr);
323+
}
321324
if (ce->num_interfaces > 0) {
322325
free(ce->interfaces);
323326
}

ext/date/php_date.c

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2186,7 +2186,6 @@ static void date_register_classes(void) /* {{{ */
21862186
ce_period.create_object = date_object_new_period;
21872187
date_ce_period = zend_register_internal_class_ex(&ce_period, NULL);
21882188
date_ce_period->get_iterator = date_object_period_get_iterator;
2189-
date_ce_period->iterator_funcs.funcs = &date_period_it_funcs;
21902189
zend_class_implements(date_ce_period, 1, zend_ce_traversable);
21912190
memcpy(&date_object_handlers_period, &std_object_handlers, sizeof(zend_object_handlers));
21922191
date_object_handlers_period.offset = XtOffsetOf(php_period_obj, std);

ext/mysqli/mysqli.c

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -655,7 +655,6 @@ PHP_MINIT_FUNCTION(mysqli)
655655
zend_declare_property_null(ce, "num_rows", sizeof("num_rows") - 1, ZEND_ACC_PUBLIC);
656656
zend_declare_property_null(ce, "type", sizeof("type") - 1, ZEND_ACC_PUBLIC);
657657
mysqli_result_class_entry->get_iterator = php_mysqli_result_get_iterator;
658-
mysqli_result_class_entry->iterator_funcs.funcs = &php_mysqli_result_iterator_funcs;
659658
zend_class_implements(mysqli_result_class_entry, 1, zend_ce_traversable);
660659
zend_hash_add_ptr(&classes, ce->name, &mysqli_result_properties);
661660

0 commit comments

Comments
 (0)