@@ -1773,7 +1773,8 @@ static int gc_remove_nested_data_from_buffer(zend_refcounted *ref, gc_root_buffe
1773
1773
}
1774
1774
1775
1775
static void zend_get_gc_buffer_release (void );
1776
- static void zend_gc_root_tmpvars (void );
1776
+ static void zend_gc_check_root_tmpvars (void );
1777
+ static void zend_gc_remove_root_tmpvars (void );
1777
1778
1778
1779
ZEND_API int zend_gc_collect_cycles (void )
1779
1780
{
@@ -1782,6 +1783,9 @@ ZEND_API int zend_gc_collect_cycles(void)
1782
1783
bool did_rerun_gc = 0 ;
1783
1784
1784
1785
zend_hrtime_t start_time = zend_hrtime ();
1786
+ if (GC_G (num_roots ) && GC_G (gc_active )) {
1787
+ zend_gc_remove_root_tmpvars ();
1788
+ }
1785
1789
1786
1790
rerun_gc :
1787
1791
if (GC_G (num_roots )) {
@@ -1986,7 +1990,7 @@ ZEND_API int zend_gc_collect_cycles(void)
1986
1990
1987
1991
finish :
1988
1992
zend_get_gc_buffer_release ();
1989
- zend_gc_root_tmpvars ();
1993
+ zend_gc_check_root_tmpvars ();
1990
1994
GC_G (collector_time ) += zend_hrtime () - start_time ;
1991
1995
return total_count ;
1992
1996
}
@@ -2033,7 +2037,7 @@ static void zend_get_gc_buffer_release(void) {
2033
2037
* cycles. However, there are some rare exceptions where this is possible, in which case we rely
2034
2038
* on the producing code to root the value. If a GC run occurs between the rooting and consumption
2035
2039
* of the value, we would end up leaking it. To avoid this, root all live TMPVAR values here. */
2036
- static void zend_gc_root_tmpvars (void ) {
2040
+ static void zend_gc_check_root_tmpvars (void ) {
2037
2041
zend_execute_data * ex = EG (current_execute_data );
2038
2042
for (; ex ; ex = ex -> prev_execute_data ) {
2039
2043
zend_function * func = ex -> func ;
@@ -2063,6 +2067,36 @@ static void zend_gc_root_tmpvars(void) {
2063
2067
}
2064
2068
}
2065
2069
2070
+ static void zend_gc_remove_root_tmpvars (void ) {
2071
+ zend_execute_data * ex = EG (current_execute_data );
2072
+ for (; ex ; ex = ex -> prev_execute_data ) {
2073
+ zend_function * func = ex -> func ;
2074
+ if (!func || !ZEND_USER_CODE (func -> type )) {
2075
+ continue ;
2076
+ }
2077
+
2078
+ uint32_t op_num = ex -> opline - ex -> func -> op_array .opcodes ;
2079
+ for (uint32_t i = 0 ; i < func -> op_array .last_live_range ; i ++ ) {
2080
+ const zend_live_range * range = & func -> op_array .live_range [i ];
2081
+ if (range -> start > op_num ) {
2082
+ break ;
2083
+ }
2084
+ if (range -> end <= op_num ) {
2085
+ continue ;
2086
+ }
2087
+
2088
+ uint32_t kind = range -> var & ZEND_LIVE_MASK ;
2089
+ if (kind == ZEND_LIVE_TMPVAR || kind == ZEND_LIVE_LOOP ) {
2090
+ uint32_t var_num = range -> var & ~ZEND_LIVE_MASK ;
2091
+ zval * var = ZEND_CALL_VAR (ex , var_num );
2092
+ if (Z_REFCOUNTED_P (var )) {
2093
+ GC_REMOVE_FROM_BUFFER (Z_COUNTED_P (var ));
2094
+ }
2095
+ }
2096
+ }
2097
+ }
2098
+ }
2099
+
2066
2100
#if GC_BENCH
2067
2101
void gc_bench_print (void )
2068
2102
{
0 commit comments