From 50a76860bd7fa0daacf6db9b869b204344811b8b Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Thu, 7 Aug 2014 18:50:03 +0400 Subject: [PATCH] Fixed GC for inner-loops (ext/standard/tests/array/compact_variation1.phpt). Fixed bug #63734. --- Zend/zend_gc.c | 110 ++++++++++++++++++++++++++++++++++++++++++++++++- Zend/zend_gc.h | 5 ++- 2 files changed, 112 insertions(+), 3 deletions(-) diff --git a/Zend/zend_gc.c b/Zend/zend_gc.c index 1d1f0b3058..adc02a3eb0 100644 --- a/Zend/zend_gc.c +++ b/Zend/zend_gc.c @@ -481,6 +481,30 @@ tail_call: count++; } +#if 1 + if ((GC_TYPE(ref) == IS_OBJECT || GC_TYPE(ref) == IS_ARRAY) && + !GC_ADDRESS(GC_INFO(ref))) { + /* add garbage into list */ + gc_root_buffer *buf = GC_G(unused); + + if (buf) { + GC_G(unused) = buf->prev; + } else if (GC_G(first_unused) != GC_G(last_unused)) { + buf = GC_G(first_unused); + GC_G(first_unused)++; + } + /* TODO: what should we do if we don't have room ??? */ + if (buf) { + buf->ref = ref; + buf->next = GC_G(roots).next; + buf->prev = &GC_G(roots); + GC_G(roots).next->prev = buf; + GC_G(roots).next = buf; + GC_SET_ADDRESS(GC_INFO(ref), buf - GC_G(buf)); + } + } +#endif + if (GC_TYPE(ref) == IS_OBJECT && EG(objects_store).object_buckets) { zend_object_get_gc_t get_gc; zend_object *obj = (zend_object*)ref; @@ -607,6 +631,73 @@ static int gc_collect_roots(TSRMLS_D) return count; } +static void gc_remove_nested_data_from_buffer(zend_refcounted *ref TSRMLS_DC) +{ + HashTable *ht; + uint idx; + Bucket *p; + +tail_call: + if (GC_ADDRESS(GC_INFO(ref)) != 0) { + GC_REMOVE_FROM_BUFFER(ref); + + if (GC_TYPE(ref) == IS_OBJECT && EG(objects_store).object_buckets) { + zend_object_get_gc_t get_gc; + zend_object *obj = (zend_object*)ref; + + if (EXPECTED(IS_OBJ_VALID(EG(objects_store).object_buckets[obj->handle]) && + (get_gc = obj->handlers->get_gc) != NULL)) { + int i, n; + zval *table; + zval tmp; + HashTable *props; + + ZVAL_OBJ(&tmp, obj); + props = get_gc(&tmp, &table, &n TSRMLS_CC); + + while (n > 0 && !Z_REFCOUNTED(table[n-1])) n--; + for (i = 0; i < n; i++) { + if (Z_REFCOUNTED(table[i])) { + ref = Z_COUNTED(table[i]); + if (!props && i == n - 1) { + goto tail_call; + } else { + gc_remove_nested_data_from_buffer(ref TSRMLS_CC); + } + } + } + if (!props) { + return; + } + ht = props; + } + } else if (GC_TYPE(ref) == IS_ARRAY) { + ht = &((zend_array*)ref)->ht; + } else if (GC_TYPE(ref) == IS_REFERENCE) { + if (Z_REFCOUNTED(((zend_reference*)ref)->val)) { + if (UNEXPECTED(!EG(objects_store).object_buckets) && + Z_TYPE(((zend_reference*)ref)->val) == IS_OBJECT) { + return; + } + ref = Z_COUNTED(((zend_reference*)ref)->val); + goto tail_call; + } + return; + } + if (!ht) return; + for (idx = 0; idx < ht->nNumUsed; idx++) { + p = ht->arData + idx; + if (!Z_REFCOUNTED(p->val)) continue; + ref = Z_COUNTED(p->val); + if (idx == ht->nNumUsed-1) { + goto tail_call; + } else { + gc_remove_nested_data_from_buffer(ref TSRMLS_CC); + } + } + } +} + ZEND_API int gc_collect_cycles(TSRMLS_D) { int count = 0; @@ -643,7 +734,14 @@ ZEND_API int gc_collect_cycles(TSRMLS_D) orig_next_to_free = GC_G(next_to_free); - /* First call destructors */ + /* Remember reference counters before calling destructors */ + current = to_free.next; + while (current != &to_free) { + current->refcount = GC_REFCOUNT(current->ref); + current = current->next; + } + + /* Call destructors */ current = to_free.next; while (current != &to_free) { p = current->ref; @@ -666,6 +764,16 @@ ZEND_API int gc_collect_cycles(TSRMLS_D) current = GC_G(next_to_free); } + /* Remove values captured in destructors */ + current = to_free.next; + while (current != &to_free) { + GC_G(next_to_free) = current->next; + if (GC_REFCOUNT(current->ref) > current->refcount) { + gc_remove_nested_data_from_buffer(current->ref TSRMLS_CC); + } + current = GC_G(next_to_free); + } + /* Destroy zvals */ current = to_free.next; while (current != &to_free) { diff --git a/Zend/zend_gc.h b/Zend/zend_gc.h index d060fae9dd..8d619f2e5e 100644 --- a/Zend/zend_gc.h +++ b/Zend/zend_gc.h @@ -74,9 +74,10 @@ GC_SET_PURPLE(Z_GC_INFO_P(v)) typedef struct _gc_root_buffer { - struct _gc_root_buffer *prev; /* double-linked list */ - struct _gc_root_buffer *next; zend_refcounted *ref; + struct _gc_root_buffer *next; /* double-linked list */ + struct _gc_root_buffer *prev; + zend_uint refcount; } gc_root_buffer; typedef struct _zend_gc_globals { -- 2.50.1