]> granicus.if.org Git - php/commitdiff
Fixed GC for inner-loops (ext/standard/tests/array/compact_variation1.phpt).
authorDmitry Stogov <dmitry@zend.com>
Thu, 7 Aug 2014 14:50:03 +0000 (18:50 +0400)
committerDmitry Stogov <dmitry@zend.com>
Thu, 7 Aug 2014 14:50:03 +0000 (18:50 +0400)
Fixed bug #63734.

Zend/zend_gc.c
Zend/zend_gc.h

index 1d1f0b30580a7ac4eb7902d07637d362614db27a..adc02a3eb0b487834da1a38964c43dc07966a445 100644 (file)
@@ -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) {
index d060fae9dd841e23c22d1e781bb320054f912550..8d619f2e5ec5b2989e1f28055f5ab1507c5bb495 100644 (file)
        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 {