Skip to content

Commit 19e0f35

Browse files
committed
Handle GC cycles properly in intl iterators
This is a follow-up on phpGH-17343 to implement GC cycle management. Previously the objects lived too long due to the strong cycle. This patch adds get_gc handlers to break the cycle.
1 parent a970eef commit 19e0f35

File tree

4 files changed

+60
-6
lines changed

4 files changed

+60
-6
lines changed

ext/intl/breakiterator/breakiterator_iterators.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,6 @@ typedef struct zoi_break_iter_parts {
144144
static void _breakiterator_parts_destroy_it(zend_object_iterator *iter)
145145
{
146146
zval_ptr_dtor(&iter->data);
147-
efree(iter);
148147
}
149148

150149
static void _breakiterator_parts_get_current_key(zend_object_iterator *iter, zval *key)
@@ -223,7 +222,7 @@ static const zend_object_iterator_funcs breakiterator_parts_it_funcs = {
223222
_breakiterator_parts_move_forward,
224223
_breakiterator_parts_rewind,
225224
zoi_with_current_invalidate_current,
226-
NULL, /* get_gc */
225+
zoi_with_current_get_gc,
227226
};
228227

229228
void IntlIterator_from_BreakIterator_parts(zval *break_iter_zv,

ext/intl/common/common_enum.cpp

Lines changed: 39 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ void zoi_with_current_dtor(zend_object_iterator *iter)
3636
{
3737
zoi_with_current *zoiwc = (zoi_with_current*)iter;
3838
zval_ptr_dtor(&zoiwc->wrapping_obj);
39+
ZVAL_UNDEF(&zoiwc->wrapping_obj);
3940
}
4041

4142
U_CFUNC int zoi_with_current_valid(zend_object_iterator *iter)
@@ -80,6 +81,14 @@ static void string_enum_current_move_forward(zend_object_iterator *iter)
8081
} //else we've reached the end of the enum, nothing more is required
8182
}
8283

84+
HashTable *zoi_with_current_get_gc(zend_object_iterator *iter, zval **table, int *n)
85+
{
86+
zoi_with_current *zoiwc = reinterpret_cast<zoi_with_current*>(iter);
87+
*table = &zoiwc->wrapping_obj;
88+
*n = 1;
89+
return nullptr;
90+
}
91+
8392
static void string_enum_rewind(zend_object_iterator *iter)
8493
{
8594
zoi_with_current *zoi_iter = (zoi_with_current*)iter;
@@ -106,7 +115,6 @@ static void string_enum_rewind(zend_object_iterator *iter)
106115
static void string_enum_destroy_it(zend_object_iterator *iter)
107116
{
108117
delete (StringEnumeration*)Z_PTR(iter->data);
109-
efree(iter);
110118
}
111119

112120
static const zend_object_iterator_funcs string_enum_object_iterator_funcs = {
@@ -117,7 +125,7 @@ static const zend_object_iterator_funcs string_enum_object_iterator_funcs = {
117125
string_enum_current_move_forward,
118126
string_enum_rewind,
119127
zoi_with_current_invalidate_current,
120-
NULL, /* get_gc */
128+
zoi_with_current_get_gc,
121129
};
122130

123131
U_CFUNC void IntlIterator_from_StringEnumeration(StringEnumeration *se, zval *object)
@@ -135,18 +143,43 @@ U_CFUNC void IntlIterator_from_StringEnumeration(StringEnumeration *se, zval *ob
135143
ZVAL_UNDEF(&((zoi_with_current*)ii->iterator)->current);
136144
}
137145

138-
static void IntlIterator_objects_free(zend_object *object)
146+
static void IntlIterator_objects_dtor(zend_object *object)
139147
{
140148
IntlIterator_object *ii = php_intl_iterator_fetch_object(object);
141-
142149
if (ii->iterator) {
143150
((zoi_with_current*)ii->iterator)->destroy_it(ii->iterator);
151+
OBJ_RELEASE(&ii->iterator->std);
152+
ii->iterator = NULL;
144153
}
154+
}
155+
156+
static void IntlIterator_objects_free(zend_object *object)
157+
{
158+
IntlIterator_object *ii = php_intl_iterator_fetch_object(object);
159+
145160
intl_error_reset(INTLITERATOR_ERROR_P(ii));
146161

147162
zend_object_std_dtor(&ii->zo);
148163
}
149164

165+
static HashTable *IntlIterator_object_get_gc(zend_object *obj, zval **table, int *n)
166+
{
167+
IntlIterator_object *ii = php_intl_iterator_fetch_object(obj);
168+
if (ii->iterator) {
169+
zend_get_gc_buffer *gc_buffer = zend_get_gc_buffer_create();
170+
zend_get_gc_buffer_add_obj(gc_buffer, &ii->iterator->std);
171+
zend_get_gc_buffer_use(gc_buffer, table, n);
172+
} else {
173+
*table = nullptr;
174+
*n = 0;
175+
}
176+
if (obj->properties == nullptr && obj->ce->default_properties_count == 0) {
177+
return nullptr;
178+
} else {
179+
return zend_std_get_properties(obj);
180+
}
181+
}
182+
150183
static zend_object_iterator *IntlIterator_get_iterator(
151184
zend_class_entry *ce, zval *object, int by_ref)
152185
{
@@ -273,7 +306,9 @@ U_CFUNC void intl_register_common_symbols(int module_number)
273306
sizeof IntlIterator_handlers);
274307
IntlIterator_handlers.offset = XtOffsetOf(IntlIterator_object, zo);
275308
IntlIterator_handlers.clone_obj = NULL;
309+
IntlIterator_handlers.dtor_obj = IntlIterator_objects_dtor;
276310
IntlIterator_handlers.free_obj = IntlIterator_objects_free;
311+
IntlIterator_handlers.get_gc = IntlIterator_object_get_gc;
277312

278313
register_common_symbols(module_number);
279314
}

ext/intl/common/common_enum.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ U_CFUNC void zoi_with_current_dtor(zend_object_iterator *iter);
7171
U_CFUNC int zoi_with_current_valid(zend_object_iterator *iter);
7272
U_CFUNC zval *zoi_with_current_get_current_data(zend_object_iterator *iter);
7373
U_CFUNC void zoi_with_current_invalidate_current(zend_object_iterator *iter);
74+
U_CFUNC HashTable *zoi_with_current_get_gc(zend_object_iterator *iter, zval **table, int *n);
7475

7576
#ifdef __cplusplus
7677
using icu::StringEnumeration;
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
--TEST--
2+
IntlIterator cycle management
3+
--EXTENSIONS--
4+
intl
5+
--FILE--
6+
<?php
7+
function break_cycle_test() {
8+
$obj = IntlCalendar::getKeywordValuesForLocale('calendar', 'fa_IR', true);
9+
unset($obj);
10+
var_dump(gc_collect_cycles());
11+
}
12+
break_cycle_test();
13+
break_cycle_test();
14+
break_cycle_test();
15+
?>
16+
--EXPECT--
17+
int(1)
18+
int(1)
19+
int(1)

0 commit comments

Comments
 (0)