php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Return to Bug #60139
Patch gc-closure2.diff revision 2011-10-31 11:04 UTC by dmitry@php.net
Patch gc-closure.diff revision 2011-10-31 11:04 UTC by dmitry@php.net
revision 2011-10-31 08:44 UTC by dmitry@php.net
Patch bug60139.php5.4.diff revision 2011-10-29 10:23 UTC by arnaud dot lb at gmail dot com
Patch test2.phpt revision 2011-10-28 14:24 UTC by arnaud dot lb at gmail dot com
Patch test1.phpt revision 2011-10-28 14:23 UTC by arnaud dot lb at gmail dot com

Patch bug60139.php5.4.diff for Performance problem Bug #60139

Patch version 2011-10-29 10:23 UTC

Return to Bug #60139 | Download this patch
Patch Revisions:

Developer: arnaud.lb@gmail.com

diff --git a/Zend/tests/bug60139-1.phpt b/Zend/tests/bug60139-1.phpt
new file mode 100644
index 0000000..96d0a28
--- /dev/null
+++ b/Zend/tests/bug60139-1.phpt
@@ -0,0 +1,29 @@
+--TEST--
+Closure and GC - 1 (bug #60139)
+--FILE--
+<?php
+
+class c {
+
+	public $foo;
+
+	public function __construct()
+	{
+		$this->foo = function() {};
+	}
+
+	public function __destruct()
+	{
+		echo "destruct\n";
+	}
+}
+
+new c;
+gc_collect_cycles();
+
+
+echo "end\n";
+
+--EXPECT--
+destruct
+end
diff --git a/Zend/tests/bug60139-2.phpt b/Zend/tests/bug60139-2.phpt
new file mode 100644
index 0000000..43721c9
--- /dev/null
+++ b/Zend/tests/bug60139-2.phpt
@@ -0,0 +1,33 @@
+--TEST--
+Closure and GC - 2 (bug #60139)
+--FILE--
+<?php
+
+class c {
+
+	public $foo;
+
+	public function __construct()
+	{
+		$this->foo = get_closure($this);
+	}
+
+	public function __destruct()
+	{
+		echo "destruct\n";
+	}
+}
+
+function get_closure($x) {
+	return function() use ($x) {};
+}
+
+new c;
+gc_collect_cycles();
+
+
+echo "end\n";
+
+--EXPECT--
+destruct
+end
diff --git a/Zend/zend_closures.c b/Zend/zend_closures.c
index 344e600..ef26477 100644
--- a/Zend/zend_closures.c
+++ b/Zend/zend_closures.c
@@ -318,6 +318,61 @@ int zend_closure_get_closure(zval *obj, zend_class_entry **ce_ptr, zend_function
 }
 /* }}} */
 
+/* overriden for garbage collection
+ * This is very hacky, but unfortunately the garbage collector can only query objects for
+ * dependencies through get_properties */
+static HashTable *zend_closure_get_properties(zval *object TSRMLS_DC)
+{
+	zend_closure *closure = (zend_closure *)zend_object_store_get_object(object TSRMLS_CC);
+	HashTable *props;
+	zval *gcdata_arr = NULL;
+	zval **gcdata_arr_pp;
+
+	props = std_object_handlers.get_properties(object TSRMLS_CC);
+
+	if (!GC_G(gc_active)) {
+		zend_hash_del(props, "\x00gcdata", sizeof("\x00gcdata"));
+		return props;
+	}
+
+	if (props->nApplyCount > 0) {
+		return props;
+	}
+
+	if (zend_hash_find(props, "\x00gcdata", sizeof("\x00gcdata"), (void**) &gcdata_arr_pp) == SUCCESS) {
+		gcdata_arr = *gcdata_arr_pp;
+		zend_hash_clean(Z_ARRVAL_P(gcdata_arr));
+	}
+
+	if (gcdata_arr == NULL) {
+		MAKE_STD_ZVAL(gcdata_arr);
+		array_init(gcdata_arr);
+		/* don't decrease refcount of members when destroying */
+		Z_ARRVAL_P(gcdata_arr)->pDestructor = NULL;
+
+		/* name starts with  to make tampering in user-land more difficult */
+		zend_hash_add(props, "\x00gcdata", sizeof("\x00gcdata"), &gcdata_arr, sizeof(gcdata_arr), NULL);
+	}
+
+	if (closure->func.type == ZEND_USER_FUNCTION && closure->func.op_array.static_variables) {
+		HashTable * static_variables = closure->func.op_array.static_variables;
+		HashPosition pos;
+		zval *val;
+
+		zend_hash_internal_pointer_reset_ex(static_variables, &pos);
+		while (zend_hash_get_current_data_ex(static_variables, (void **)&val, &pos) == SUCCESS) {
+			zend_hash_next_index_insert(Z_ARRVAL_P(gcdata_arr), val, sizeof(zval*), NULL);
+			zend_hash_move_forward_ex(static_variables, &pos);
+		}
+	}
+
+	if (closure->this_ptr) {
+		zend_hash_next_index_insert(Z_ARRVAL_P(gcdata_arr), (void**)&closure->this_ptr, sizeof(zval*), NULL);
+	}
+
+	return props;
+}
+
 static HashTable *zend_closure_get_debug_info(zval *object, int *is_temp TSRMLS_DC) /* {{{ */
 {
 	zend_closure *closure = (zend_closure *)zend_object_store_get_object(object TSRMLS_CC);
@@ -423,6 +478,7 @@ void zend_register_closure_ce(TSRMLS_D) /* {{{ */
 	closure_handlers.unset_property = zend_closure_unset_property;
 	closure_handlers.compare_objects = zend_closure_compare_objects;
 	closure_handlers.clone_obj = zend_closure_clone;
+	closure_handlers.get_properties = zend_closure_get_properties;
 	closure_handlers.get_debug_info = zend_closure_get_debug_info;
 	closure_handlers.get_closure = zend_closure_get_closure;
 }
 
PHP Copyright © 2001-2014 The PHP Group
All rights reserved.
Last updated: Wed Apr 16 19:02:26 2014 UTC