com php-src: Fixed GC for inner-loops (ext/standard/tests/arr ay/compact_variation1.phpt). Fixed bug #6 3734.: Zend/zend_gc.c Zend/zend_gc.h
| From: | Dmitry Stogov | Date: | Thu, 07 Aug 2014 14:50:03 +0000 |
| Subject: | com php-src: Fixed GC for inner-loops (ext/standard/tests/arr ay/compact_variation1.phpt). Fixed bug #6 3734.: Zend/zend_gc.c Zend/zend_gc.h | ||
| Groups: | php.cvs | ||
| Request: | Send a blank email to php-cvs+get-79778@lists.php.net to get a copy of this message | ||
Commit: 50a76860bd7fa0daacf6db9b869b204344811b8b
Author: Dmitry Stogov <dmitry@zend.com> Thu, 7 Aug 2014 18:50:03 +0400
Parents: 417ed16d11d2d7376b1a74028c42cd87818bfffb
Branches: phpng
Link: http://git.php.net/?p=php-src.git;a=commitdiff;h=50a76860bd7fa0daacf6db9b869b204344811b8b
Log:
Fixed GC for inner-loops (ext/standard/tests/array/compact_variation1.phpt).
Fixed bug #63734.
Bugs:
https://bugs.php.net/63734
Changed paths:
M Zend/zend_gc.c
M Zend/zend_gc.h
Diff:
diff --git a/Zend/zend_gc.c b/Zend/zend_gc.c
index 1d1f0b3..adc02a3 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 d060fae..8d619f2 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 {