summaryrefslogtreecommitdiff
path: root/Zend/zend_gc.c
diff options
context:
space:
mode:
authorDmitry Stogov <dmitry@php.net>2008-03-14 13:21:21 +0000
committerDmitry Stogov <dmitry@php.net>2008-03-14 13:21:21 +0000
commit2b82e0d78aa69237e045ce534b2290eac332edc4 (patch)
tree3824f091991247d2db319bc4165b4825a730d240 /Zend/zend_gc.c
parent6ec12a4794d1cfe7191705020d94419c5ba532df (diff)
downloadphp-git-2b82e0d78aa69237e045ce534b2290eac332edc4.tar.gz
Fixed GC bug
Diffstat (limited to 'Zend/zend_gc.c')
-rw-r--r--Zend/zend_gc.c99
1 files changed, 80 insertions, 19 deletions
diff --git a/Zend/zend_gc.c b/Zend/zend_gc.c
index 72cc457024..30df9aa64c 100644
--- a/Zend/zend_gc.c
+++ b/Zend/zend_gc.c
@@ -55,6 +55,7 @@ static void gc_globals_ctor_ex(zend_gc_globals *gc_globals TSRMLS_DC)
gc_globals->roots.prev = NULL;
gc_globals->unused = NULL;
gc_globals->zval_to_free = NULL;
+ gc_globals->free_list = NULL;
gc_globals->gc_runs = 0;
gc_globals->collected = 0;
@@ -136,6 +137,19 @@ ZEND_API void gc_init(TSRMLS_D)
ZEND_API void gc_zval_possible_root(zval *zv TSRMLS_DC)
{
+ if (UNEXPECTED(GC_G(free_list) != NULL &&
+ GC_ZVAL_ADDRESS(zv) != NULL &&
+ GC_ZVAL_GET_COLOR(zv) == GC_BLACK)) {
+ zval_gc_info **p = &GC_G(free_list);
+
+ while (*p != NULL) {
+ if (*p == (zval_gc_info*)zv) {
+ return;
+ }
+ p = &(*p)->u.next;
+ }
+ }
+
if (zv->type == IS_OBJECT) {
GC_ZOBJ_CHECK_POSSIBLE_ROOT(zv);
return;
@@ -235,6 +249,28 @@ ZEND_API void gc_zobj_possible_root(zval *zv TSRMLS_DC)
}
}
+ZEND_API void _gc_remove_zval_from_buffer(zval *zv)
+{
+ gc_root_buffer* root_buffer = GC_ADDRESS(((zval_gc_info*)zv)->u.buffered);
+ TSRMLS_FETCH();
+
+ if (UNEXPECTED(GC_G(free_list) != NULL &&
+ GC_ZVAL_GET_COLOR(zv) == GC_BLACK)) {
+ zval_gc_info **p = &GC_G(free_list);
+
+ while (*p != NULL) {
+ if (*p == (zval_gc_info*)zv) {
+ *p = (*p)->u.next;
+ return;
+ }
+ p = &(*p)->u.next;
+ }
+ }
+ GC_BENCH_INC(zval_remove_from_buffer);
+ GC_REMOVE_FROM_BUFFER(root_buffer);
+ ((zval_gc_info*)zv)->u.buffered = NULL;
+}
+
static void zobj_scan_black(struct _store_object *obj, zval *pz TSRMLS_DC)
{
GC_SET_BLACK(obj->buffered);
@@ -371,7 +407,6 @@ static int zval_scan(zval *pz TSRMLS_DC)
zval_scan_black(pz TSRMLS_CC);
} else {
GC_ZVAL_SET_COLOR(pz, GC_WHITE);
-
if (Z_TYPE_P(pz) == IS_OBJECT) {
zobj_scan(pz TSRMLS_CC);
} else if (Z_TYPE_P(pz) == IS_ARRAY) {
@@ -435,17 +470,22 @@ static void zval_collect_white(zval *pz TSRMLS_DC)
if (Z_TYPE_P(pz) == IS_OBJECT) {
zobj_collect_white(pz TSRMLS_CC);
+ if (EXPECTED(EG(objects_store).object_buckets[Z_OBJ_HANDLE_P(pz)].valid &&
+ Z_OBJ_HANDLER_P(pz, get_properties) != NULL)) {
+ Z_OBJPROP_P(pz)->pDestructor = NULL;
+ }
} else {
if (Z_TYPE_P(pz) == IS_ARRAY) {
- if (Z_ARRVAL_P(pz) == &EG(symbol_table)) {
- return;
- }
+// if (Z_ARRVAL_P(pz) == &EG(symbol_table)) {
+// return;
+// }
zend_hash_apply(Z_ARRVAL_P(pz), (apply_func_t) children_collect_white TSRMLS_CC);
+ Z_ARRVAL_P(pz)->pDestructor = NULL;
}
- /* restore refcount */
- pz->refcount__gc++;
}
+ /* restore refcount and put into list to free */
+ pz->refcount__gc++;
((zval_gc_info*)pz)->u.next = GC_G(zval_to_free);
GC_G(zval_to_free) = (zval_gc_info*)pz;
}
@@ -453,6 +493,9 @@ static void zval_collect_white(zval *pz TSRMLS_DC)
static int children_collect_white(zval **pz TSRMLS_DC)
{
+ if (Z_TYPE_PP(pz) != IS_ARRAY || Z_ARRVAL_PP(pz) != &EG(symbol_table)) {
+ (*pz)->refcount__gc++;
+ }
zval_collect_white(*pz TSRMLS_CC);
return 0;
}
@@ -486,7 +529,7 @@ ZEND_API int gc_collect_cycles(TSRMLS_D)
int count = 0;
if (GC_G(roots).next != &GC_G(roots)) {
- zval_gc_info *p, *q;
+ zval_gc_info *p, *q, *orig_free_list;
if (GC_G(gc_active)) {
return 0;
@@ -497,35 +540,53 @@ ZEND_API int gc_collect_cycles(TSRMLS_D)
gc_mark_roots(TSRMLS_C);
gc_scan_roots(TSRMLS_C);
gc_collect_roots(TSRMLS_C);
- GC_G(gc_active) = 0;
- p = GC_G(zval_to_free);
+ orig_free_list = GC_G(free_list);
+ p = GC_G(free_list) = GC_G(zval_to_free);
GC_G(zval_to_free) = NULL;
+ GC_G(gc_active) = 0;
+
+ /* First call destructors */
+ while (p) {
+ if (Z_TYPE(p->z) == IS_OBJECT) {
+ if (EG(objects_store).object_buckets &&
+ EG(objects_store).object_buckets[Z_OBJ_HANDLE(p->z)].valid &&
+ EG(objects_store).object_buckets[Z_OBJ_HANDLE(p->z)].bucket.obj.refcount <= 0 &&
+ EG(objects_store).object_buckets[Z_OBJ_HANDLE(p->z)].bucket.obj.dtor &&
+ !EG(objects_store).object_buckets[Z_OBJ_HANDLE(p->z)].destructor_called) {
+
+ EG(objects_store).object_buckets[Z_OBJ_HANDLE(p->z)].destructor_called = 1;
+ zend_try {
+ EG(objects_store).object_buckets[Z_OBJ_HANDLE(p->z)].bucket.obj.dtor(EG(objects_store).object_buckets[Z_OBJ_HANDLE(p->z)].bucket.obj.object, Z_OBJ_HANDLE(p->z) TSRMLS_CC);
+ } zend_end_try();
+ }
+ }
+ p = p->u.next;
+ }
+
+ p = GC_G(free_list);
while (p) {
q = p->u.next;
if (Z_TYPE(p->z) == IS_OBJECT) {
if (EG(objects_store).object_buckets &&
EG(objects_store).object_buckets[Z_OBJ_HANDLE(p->z)].valid &&
EG(objects_store).object_buckets[Z_OBJ_HANDLE(p->z)].bucket.obj.refcount <= 0) {
- if (EXPECTED(Z_OBJ_HANDLER(p->z, get_properties) != NULL)) {
- GC_G(gc_active) = 1;
- Z_OBJPROP(p->z)->pDestructor = NULL;
- GC_G(gc_active) = 0;
- }
EG(objects_store).object_buckets[Z_OBJ_HANDLE(p->z)].bucket.obj.refcount = 1;
- zend_objects_store_del_ref_by_handle(Z_OBJ_HANDLE(p->z) TSRMLS_CC);
+ zend_try {
+ zend_objects_store_del_ref_by_handle(Z_OBJ_HANDLE(p->z) TSRMLS_CC);
+ } zend_end_try();
}
} else {
- if (Z_TYPE(p->z) == IS_ARRAY) {
- Z_ARRVAL(p->z)->pDestructor = NULL;
- }
- zval_dtor(&p->z);
+ zend_try {
+ zval_dtor(&p->z);
+ } zend_end_try();
}
FREE_ZVAL_EX(&p->z);
p = q;
count++;
}
GC_G(collected) += count;
+ GC_G(free_list) = orig_free_list;
}
return count;