diff --git a/Zend/zend_gc.c b/Zend/zend_gc.c index e15f97ecfe80..169febbeb7ff 100644 --- a/Zend/zend_gc.c +++ b/Zend/zend_gc.c @@ -983,6 +983,9 @@ static void gc_scan_black(zend_refcounted *ref, gc_stack *stack) handle_ht: n = ht->nNumUsed; zv = ht->arPacked; + if (GC_FLAGS(ht) & GC_NOT_COLLECTABLE) { + goto next; + } if (HT_IS_PACKED(ht)) { goto handle_zvals; } @@ -1041,7 +1044,7 @@ static void gc_scan_black(zend_refcounted *ref, gc_stack *stack) * counts and mark visited nodes grey. See MarkGray() in Bacon & Rajan. */ static void gc_mark_grey(zend_refcounted *ref, gc_stack *stack) { - HashTable *ht; + HashTable *ht, *primitive_ht = NULL; Bucket *p; zval *zv; uint32_t n; @@ -1132,6 +1135,7 @@ static void gc_mark_grey(zend_refcounted *ref, gc_stack *stack) handle_zvals: for (; n != 0; n--) { if (Z_COLLECTABLE_P(zv)) { + primitive_ht = NULL; ref = Z_COUNTED_P(zv); GC_DELREF(ref); if (!GC_REF_CHECK_COLOR(ref, GC_GREY)) { @@ -1153,12 +1157,20 @@ static void gc_mark_grey(zend_refcounted *ref, gc_stack *stack) } zv++; } + if (primitive_ht) { + GC_ADD_FLAGS(primitive_ht, GC_NOT_COLLECTABLE); + primitive_ht = NULL; + } } } else if (GC_TYPE(ref) == IS_ARRAY) { ZEND_ASSERT(((zend_array*)ref) != &EG(symbol_table)); ht = (zend_array*)ref; handle_ht: n = ht->nNumUsed; + if (GC_FLAGS(ht) & GC_NOT_COLLECTABLE) { + goto next; + } + primitive_ht = ht; if (HT_IS_PACKED(ht)) { zv = ht->arPacked; goto handle_zvals; @@ -1171,6 +1183,7 @@ static void gc_mark_grey(zend_refcounted *ref, gc_stack *stack) zv = Z_INDIRECT_P(zv); } if (Z_COLLECTABLE_P(zv)) { + primitive_ht = NULL; ref = Z_COUNTED_P(zv); GC_DELREF(ref); if (!GC_REF_CHECK_COLOR(ref, GC_GREY)) { @@ -1196,6 +1209,10 @@ static void gc_mark_grey(zend_refcounted *ref, gc_stack *stack) } p++; } + if (primitive_ht) { + GC_ADD_FLAGS(ht, GC_NOT_COLLECTABLE); + primitive_ht = NULL; + } } else if (GC_TYPE(ref) == IS_REFERENCE) { if (Z_COLLECTABLE(((zend_reference*)ref)->val)) { ref = Z_COUNTED(((zend_reference*)ref)->val); @@ -1378,6 +1395,9 @@ static void gc_scan(zend_refcounted *ref, gc_stack *stack) handle_ht: n = ht->nNumUsed; + if (GC_FLAGS(ht) & GC_NOT_COLLECTABLE) { + goto next; + } if (HT_IS_PACKED(ht)) { zv = ht->arPacked; goto handle_zvals; @@ -1623,6 +1643,9 @@ static int gc_collect_white(zend_refcounted *ref, uint32_t *flags, gc_stack *sta handle_ht: n = ht->nNumUsed; + if (GC_FLAGS(ht) & GC_NOT_COLLECTABLE) { + goto next; + } if (HT_IS_PACKED(ht)) { zv = ht->arPacked; goto handle_zvals; @@ -1811,6 +1834,9 @@ static int gc_remove_nested_data_from_buffer(zend_refcounted *ref, gc_root_buffe handle_ht: n = ht->nNumUsed; + if (GC_FLAGS(ht) & GC_NOT_COLLECTABLE) { + goto next; + } if (HT_IS_PACKED(ht)) { zv = ht->arPacked; goto handle_zvals; diff --git a/Zend/zend_hash.c b/Zend/zend_hash.c index f48c298f167e..3111cda233cd 100644 --- a/Zend/zend_hash.c +++ b/Zend/zend_hash.c @@ -885,6 +885,7 @@ static zend_always_inline zval *_zend_hash_add_or_update_i(HashTable *ht, zend_s nIndex = h | ht->nTableMask; Z_NEXT(p->val) = HT_HASH_EX(arData, nIndex); HT_HASH_EX(arData, nIndex) = HT_IDX_TO_HASH(idx); + GC_DEL_FLAGS(ht, GC_NOT_COLLECTABLE); if (flag & HASH_LOOKUP) { ZVAL_NULL(&p->val); } else { @@ -970,6 +971,7 @@ static zend_always_inline zval *_zend_hash_str_add_or_update_i(HashTable *ht, co nIndex = h | ht->nTableMask; Z_NEXT(p->val) = HT_HASH(ht, nIndex); HT_HASH(ht, nIndex) = HT_IDX_TO_HASH(idx); + GC_DEL_FLAGS(ht, GC_NOT_COLLECTABLE); return &p->val; } @@ -1172,6 +1174,7 @@ static zend_always_inline zval *_zend_hash_index_add_or_update_i(HashTable *ht, p = ht->arData + idx; Z_NEXT(p->val) = HT_HASH(ht, nIndex); HT_HASH(ht, nIndex) = HT_IDX_TO_HASH(idx); + GC_DEL_FLAGS(ht, GC_NOT_COLLECTABLE); if ((zend_long)h >= ht->nNextFreeElement) { ht->nNextFreeElement = (zend_long)h < ZEND_LONG_MAX ? h + 1 : ZEND_LONG_MAX; } diff --git a/Zend/zend_hash.h b/Zend/zend_hash.h index 71206e61550b..cd48c88e7d12 100644 --- a/Zend/zend_hash.h +++ b/Zend/zend_hash.h @@ -1638,6 +1638,7 @@ static zend_always_inline zval *_zend_hash_append_ex(HashTable *ht, zend_string nIndex = (uint32_t)p->h | ht->nTableMask; Z_NEXT(p->val) = HT_HASH(ht, nIndex); HT_HASH(ht, nIndex) = HT_IDX_TO_HASH(idx); + GC_DEL_FLAGS(ht, GC_NOT_COLLECTABLE); ht->nNumOfElements++; return &p->val; } @@ -1664,6 +1665,7 @@ static zend_always_inline zval *_zend_hash_append_ptr_ex(HashTable *ht, zend_str nIndex = (uint32_t)p->h | ht->nTableMask; Z_NEXT(p->val) = HT_HASH(ht, nIndex); HT_HASH(ht, nIndex) = HT_IDX_TO_HASH(idx); + GC_DEL_FLAGS(ht, GC_NOT_COLLECTABLE); ht->nNumOfElements++; return &p->val; } @@ -1690,6 +1692,7 @@ static zend_always_inline void _zend_hash_append_ind(HashTable *ht, zend_string nIndex = (uint32_t)p->h | ht->nTableMask; Z_NEXT(p->val) = HT_HASH(ht, nIndex); HT_HASH(ht, nIndex) = HT_IDX_TO_HASH(idx); + GC_DEL_FLAGS(ht, GC_NOT_COLLECTABLE); ht->nNumOfElements++; }