PHP NEWS
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
?? ??? 20??, PHP 5.3.0
+- Added garbage collector. (David Wang, Dmitry).
- Added new date/time functionality:
* date_parse_from_format(): Parse date/time strings according to a format.
--- /dev/null
+--TEST--
+GC 001: gc_enable()/gc_diable()/gc_enabled()
+--FILE--
+<?php
+gc_disable();
+var_dump(gc_enabled());
+gc_enable();
+var_dump(gc_enabled());
+?>
+--EXPECT--
+bool(false)
+bool(true)
--- /dev/null
+--TEST--
+GC 002: gc_enable()/gc_diable() and ini_get()
+--FILE--
+<?php
+gc_disable();
+var_dump(gc_enabled());
+echo ini_get('zend.enable_gc') . "\n";
+gc_enable();
+var_dump(gc_enabled());
+echo ini_get('zend.enable_gc') . "\n";
+?>
+--EXPECT--
+bool(false)
+0
+bool(true)
+1
--- /dev/null
+--TEST--
+GC 003: gc_enabled() and ini_set()
+--FILE--
+<?php
+ini_set('zend.enable_gc','0');
+var_dump(gc_enabled());
+echo ini_get('zend.enable_gc') . "\n";
+ini_set('zend.enable_gc','1');
+var_dump(gc_enabled());
+echo ini_get('zend.enable_gc') . "\n";
+?>
+--EXPECT--
+bool(false)
+0
+bool(true)
+1
--- /dev/null
+--TEST--
+GC 004: Simple array cycle
+--FILE--
+<?php
+$a = array();
+$a[] =& $a;
+var_dump($a);
+unset($a);
+var_dump(gc_collect_cycles());
+echo "ok\n"
+?>
+--EXPECT--
+array(1) {
+ [0]=>
+ &array(1) {
+ [0]=>
+ &array(1) {
+ [0]=>
+ *RECURSION*
+ }
+ }
+}
+int(1)
+ok
--- /dev/null
+--TEST--
+GC 005: Simple object cycle
+--FILE--
+<?php
+$a = new stdClass();
+$a->a = $a;
+var_dump($a);
+unset($a);
+var_dump(gc_collect_cycles());
+echo "ok\n"
+?>
+--EXPECT--
+object(stdClass)#1 (1) {
+ ["a"]=>
+ object(stdClass)#1 (1) {
+ ["a"]=>
+ *RECURSION*
+ }
+}
+int(1)
+ok
--- /dev/null
+--TEST--
+GC 006: Simple array-object cycle
+--FILE--
+<?php
+$a = new stdClass();
+$a->a = array();
+$a->a[0] =& $a;
+var_dump($a);
+unset($a);
+var_dump(gc_collect_cycles());
+echo "ok\n"
+?>
+--EXPECT--
+object(stdClass)#1 (1) {
+ ["a"]=>
+ array(1) {
+ [0]=>
+ &object(stdClass)#1 (1) {
+ ["a"]=>
+ array(1) {
+ [0]=>
+ *RECURSION*
+ }
+ }
+ }
+}
+int(2)
+ok
--- /dev/null
+--TEST--
+GC 007: Unreferensed array cycle
+--FILE--
+<?php
+$a = array(array());
+$a[0][0] =& $a[0];
+var_dump($a[0]);
+var_dump(gc_collect_cycles());
+unset($a);
+var_dump(gc_collect_cycles());
+echo "ok\n"
+?>
+--EXPECT--
+array(1) {
+ [0]=>
+ &array(1) {
+ [0]=>
+ &array(1) {
+ [0]=>
+ *RECURSION*
+ }
+ }
+}
+int(0)
+int(1)
+ok
--- /dev/null
+--TEST--
+GC 008: Unreferensed object cycle
+--FILE--
+<?php
+$a = new stdClass();
+$a->a = new stdClass();
+$a->a->a = $a->a;
+var_dump($a->a);
+var_dump(gc_collect_cycles());
+unset($a);
+var_dump(gc_collect_cycles());
+echo "ok\n"
+?>
+--EXPECT--
+object(stdClass)#2 (1) {
+ ["a"]=>
+ object(stdClass)#2 (1) {
+ ["a"]=>
+ *RECURSION*
+ }
+}
+int(0)
+int(1)
+ok
--- /dev/null
+--TEST--
+GC 009: Unreferensed array-object cycle
+--FILE--
+<?php
+$a = array();
+$a[0] = new stdClass();
+$a[0]->a = array();
+$a[0]->a[0] =& $a[0];
+var_dump($a[0]);
+var_dump(gc_collect_cycles());
+unset($a);
+var_dump(gc_collect_cycles());
+echo "ok\n"
+?>
+--EXPECT--
+object(stdClass)#1 (1) {
+ ["a"]=>
+ array(1) {
+ [0]=>
+ &object(stdClass)#1 (1) {
+ ["a"]=>
+ array(1) {
+ [0]=>
+ *RECURSION*
+ }
+ }
+ }
+}
+int(0)
+int(2)
+ok
--- /dev/null
+--TEST--
+GC 010: Cycle with reference to $GLOBALS
+--FILE--
+<?php
+$a = array();
+$a[] =& $a;
+var_dump($a);
+$a[] =& $GLOBALS;
+unset($a);
+var_dump(gc_collect_cycles());
+echo "ok\n"
+?>
+--EXPECT--
+array(1) {
+ [0]=>
+ &array(1) {
+ [0]=>
+ &array(1) {
+ [0]=>
+ *RECURSION*
+ }
+ }
+}
+int(1)
+ok
--- /dev/null
+--TEST--
+GC 011: GC and destructors
+--FILE--
+<?php
+class Foo {
+ public $a;
+ function __destruct() {
+ echo __FUNCTION__,"\n";
+ }
+}
+$a = new Foo();
+$a->a = $a;
+var_dump($a);
+unset($a);
+var_dump(gc_collect_cycles());
+echo "ok\n"
+?>
+--EXPECT--
+object(Foo)#1 (1) {
+ ["a"]=>
+ object(Foo)#1 (1) {
+ ["a"]=>
+ *RECURSION*
+ }
+}
+__destruct
+int(1)
+ok
--- /dev/null
+--TEST--
+GC 012: collection of many loops at once
+--FILE--
+<?php
+$a=array();
+for ($i=0; $i < 1000; $i++) {
+ $a[$i] = array(array());
+ $a[$i][0] = & $a[$i];
+}
+var_dump(gc_collect_cycles());
+unset($a);
+var_dump(gc_collect_cycles());
+echo "ok\n";
+--EXPECT--
+int(0)
+int(1000)
+ok
--- /dev/null
+--TEST--
+GC 013: Too many cycles in one array
+--FILE--
+<?php
+$a = array();
+for ($i = 0; $i < 10001; $i++) {
+ $a[$i] =& $a;
+}
+$a[] = "xxx";
+unset($a);
+var_dump(gc_collect_cycles());
+echo "ok\n";
+?>
+--EXPECT--
+int(2)
+ok
--- /dev/null
+--TEST--
+GC 014: Too many cycles in one object
+--FILE--
+<?php
+$a = new stdClass();
+for ($i = 0; $i < 10001; $i++) {
+ $b =& $a;
+ $a->{"a".$i} = $a;
+}
+unset($b);
+$a->b = "xxx";
+unset($a);
+var_dump(gc_collect_cycles());
+echo "ok\n";
+?>
+--EXPECT--
+int(10002)
+ok
--- /dev/null
+--TEST--
+GC 015: Object as root of cycle
+--FILE--
+<?php
+$a = new stdClass();
+$c =& $a;
+$b = $a;
+$a->a = $a;
+$a->b = "xxx";
+unset($c);
+unset($a);
+unset($b);
+var_dump(gc_collect_cycles());
+echo "ok\n";
+?>
+--EXPECT--
+int(2)
+ok
--- /dev/null
+--TEST--
+GC 016: nested GC calls
+--FILE--
+<?php
+class Foo {
+ public $a;
+ function __destruct() {
+ echo "-> ";
+ $a = array();
+ $a[] =& $a;
+ unset($a);
+ var_dump(gc_collect_cycles());
+ }
+}
+$a = new Foo();
+$a->a = $a;
+unset($a);
+var_dump(gc_collect_cycles());
+echo "ok\n"
+?>
+--EXPECT--
+-> int(1)
+int(1)
+ok
--- /dev/null
+--TEST--
+GC 017: GC and destructors with unset
+--FILE--
+<?php
+class Node {
+ public $name;
+ public $children;
+ public $parent;
+ function __construct($name) {
+ $this->name = $name;
+ $this->children = array();
+ $this->parent = null;
+ }
+ function insert($node) {
+ $node->parent = $this;
+ $this->children[] = $node;
+ }
+ function __destruct() {
+ var_dump($this->name);
+ unset($this->name);
+ unset($this->children);
+ unset($this->parent);
+ }
+}
+$a = new Node('A');
+$b = new Node('B');
+$c = new Node('C');
+$a->insert($b);
+$a->insert($c);
+unset($a);
+unset($b);
+unset($c);
+var_dump(gc_collect_cycles());
+echo "ok\n"
+?>
+--EXPECTF--
+string(1) "%s"
+string(1) "%s"
+string(1) "%s"
+int(10)
+ok
--- /dev/null
+--TEST--
+GC 018: GC detach with assign
+--FILE--
+<?php
+$a = array(array());
+$a[0][0] =& $a[0];
+$a = 1;
+var_dump(gc_collect_cycles());
+echo "ok\n"
+?>
+--EXPECT--
+int(1)
+ok
--- /dev/null
+--TEST--
+GC 019: GC detach with assign by reference
+--FILE--
+<?php
+$a = array(array());
+$a[0][0] =& $a[0];
+$b = 1;
+$a =& $b;
+var_dump(gc_collect_cycles());
+echo "ok\n"
+?>
+--EXPECT--
+int(1)
+ok
--- /dev/null
+--TEST--
+GC 020: GC detach reference with assign
+--FILE--
+<?php
+$a = array();
+$a[0] =& $a;
+$a[1] = array();
+$a[1][0] =& $a[1];
+$a = 1;
+var_dump(gc_collect_cycles());
+echo "ok\n"
+?>
+--EXPECT--
+int(1)
+ok
--- /dev/null
+--TEST--
+GC 021: GC detach reference with assign by reference
+--FILE--
+<?php
+$a = array();
+$a[0] =& $a;
+$a[1] = array();
+$a[1][0] =& $a[1];
+$b = 1;
+$a =& $b;
+var_dump(gc_collect_cycles());
+echo "ok\n"
+?>
+--EXPECT--
+int(2)
+ok
--- /dev/null
+--TEST--
+GC 022: GC detach reference in executor's PZVAL_UNLOCK()
+--INI--
+error_reporting=0
+--FILE--
+<?php
+$a = array(array());
+$a[0][0] =& $a[0];
+$s = array(1) + unserialize(serialize(&$a[0]));
+var_dump(gc_collect_cycles());
+echo "ok\n"
+?>
+--EXPECT--
+int(1)
+ok
--- /dev/null
+--TEST--
+GC 023: Root buffer overflow (automatic collection)
+--FILE--
+<?php
+$a=array();
+for ($i=0; $i < 9999; $i++) {
+ $a[$i] = array(array());
+ $a[$i][0] = & $a[$i];
+}
+var_dump(gc_collect_cycles());
+unset($a);
+var_dump(gc_collect_cycles());
+$a=array();
+for ($i=0; $i < 10001; $i++) {
+ $a[$i] = array(array());
+ $a[$i][0] = & $a[$i];
+}
+var_dump(gc_collect_cycles());
+unset($a); // 10000 zvals collected automatic
+var_dump(gc_collect_cycles());
+echo "ok\n";
+--EXPECT--
+int(0)
+int(9999)
+int(0)
+int(1)
+ok
--- /dev/null
+--TEST--
+GC 024: GC and objects with non-standard handlers
+--SKIPIF--
+<?php if (!extension_loaded("spl")) print "skip"; ?>
+--FILE--
+<?php
+$a = new ArrayObject();
+$a[0] = $a;
+unset($a);
+var_dump(gc_collect_cycles());
+echo "ok\n";
+?>
+--EXPECT--
+int(1)
+ok
--- /dev/null
+--TEST--
+GC 025: Automatic GC on request shutdown
+--FILE--
+<?php
+$a = array(array());
+$a[0][0] =& $a[0];
+unset($a);
+echo "ok\n"
+?>
+--EXPECT--
+ok
--- /dev/null
+--TEST--
+GC 026: Automatic GC on request shutdown (GC enabled at run-time)
+--INI--
+zend.enable_gc=0
+--FILE--
+<?php
+gc_enable();
+$a = array(array());
+$a[0][0] =& $a[0];
+unset($a);
+echo "ok\n"
+?>
+--EXPECT--
+ok
}
/* }}} */
+static ZEND_INI_MH(OnUpdateGCEnabled) /* {{{ */
+{
+ OnUpdateBool(entry, new_value, new_value_length, mh_arg1, mh_arg2, mh_arg3, stage TSRMLS_CC);
+
+ if (GC_G(gc_enabled)) {
+ gc_init(TSRMLS_C);
+ }
+
+ return SUCCESS;
+}
+/* }}} */
+
ZEND_INI_BEGIN()
ZEND_INI_ENTRY("error_reporting", NULL, ZEND_INI_ALL, OnUpdateErrorReporting)
+ STD_ZEND_INI_BOOLEAN("zend.enable_gc", "1", ZEND_INI_ALL, OnUpdateGCEnabled, gc_enabled, zend_gc_globals, gc_globals)
STD_ZEND_INI_BOOLEAN("zend.ze1_compatibility_mode", "0", ZEND_INI_ALL, OnUpdateBool, ze1_compatibility_mode, zend_executor_globals, executor_globals)
#ifdef ZEND_MULTIBYTE
STD_ZEND_INI_BOOLEAN("detect_unicode", "1", ZEND_INI_ALL, OnUpdateBool, detect_unicode, zend_compiler_globals, compiler_globals)
void zend_activate(TSRMLS_D) /* {{{ */
{
+ gc_reset(TSRMLS_C);
init_compiler(TSRMLS_C);
init_executor(TSRMLS_C);
startup_scanner(TSRMLS_C);
zend_destroy_rsrc_list(&EG(regular_list) TSRMLS_CC);
+#ifdef ZEND_DEBUG
+ if (GC_G(gc_enabled) && !CG(unclean_shutdown)) {
+ gc_collect_cycles(TSRMLS_C);
+ }
+#endif
+
zend_try {
zend_ini_deactivate(TSRMLS_C);
} zend_end_try();
#define ZEND_MAX_RESERVED_RESOURCES 4
+#include "zend_gc.h"
#include "zend_operators.h"
#include "zend_variables.h"
static ZEND_FUNCTION(zend_thread_id);
#endif
#endif
+static ZEND_FUNCTION(gc_collect_cycles);
+static ZEND_FUNCTION(gc_enabled);
+static ZEND_FUNCTION(gc_enable);
+static ZEND_FUNCTION(gc_disable);
#include "zend_arg_defs.c"
ZEND_FE(zend_thread_id, NULL)
#endif
#endif
+ ZEND_FE(gc_collect_cycles, NULL)
+ ZEND_FE(gc_enabled, NULL)
+ ZEND_FE(gc_enable, NULL)
+ ZEND_FE(gc_disable, NULL)
{ NULL, NULL, NULL }
};
}
/* }}} */
+/* {{{ proto int gc_collect_cycles(void)
+ Forces collection of any existing garbage cycles.
+ Returns number of freed zvals */
+ZEND_FUNCTION(gc_collect_cycles)
+{
+ RETURN_LONG(gc_collect_cycles(TSRMLS_C));
+}
+/* }}} */
+
+/* {{{ proto void gc_enabled(void)
+ Returns status of the circular reference collector */
+ZEND_FUNCTION(gc_enabled)
+{
+ RETURN_BOOL(GC_G(gc_enabled));
+}
+/* }}} */
+
+/* {{{ proto void gc_enable(void)
+ Activates the circular reference collector */
+ZEND_FUNCTION(gc_enable)
+{
+ zend_alter_ini_entry("zend.enable_gc", sizeof("zend.enable_gc"), "1", sizeof("1")-1, ZEND_INI_USER, ZEND_INI_STAGE_RUNTIME);
+}
+/* }}} */
+
+/* {{{ proto void gc_disable(void)
+ Deactivates the circular reference collector */
+ZEND_FUNCTION(gc_disable)
+{
+ zend_alter_ini_entry("zend.enable_gc", sizeof("zend.enable_gc"), "0", sizeof("0")-1, ZEND_INI_USER, ZEND_INI_STAGE_RUNTIME);
+}
+/* }}} */
/* {{{ proto int func_num_args(void)
Get the number of arguments that were passed to the function */
#define TEMP_VAR_STACK_LIMIT 2000
-static inline void zend_pzval_unlock_func(zval *z, zend_free_op *should_free, int unref)
+static inline void zend_pzval_unlock_func(zval *z, zend_free_op *should_free, int unref TSRMLS_DC)
{
if (!Z_DELREF_P(z)) {
Z_SET_REFCOUNT_P(z, 1);
if (unref && Z_ISREF_P(z) && Z_REFCOUNT_P(z) == 1) {
Z_UNSET_ISREF_P(z);
}
+ GC_ZVAL_CHECK_POSSIBLE_ROOT(z);
}
}
static inline void zend_pzval_unlock_free_func(zval *z)
{
if (!Z_DELREF_P(z)) {
+ GC_REMOVE_ZVAL_FROM_BUFFER(z);
zval_dtor(z);
safe_free_zval_ptr(z);
}
}
-#define PZVAL_UNLOCK(z, f) zend_pzval_unlock_func(z, f, 1)
-#define PZVAL_UNLOCK_EX(z, f, u) zend_pzval_unlock_func(z, f, u)
+#define PZVAL_UNLOCK(z, f) zend_pzval_unlock_func(z, f, 1 TSRMLS_CC)
+#define PZVAL_UNLOCK_EX(z, f, u) zend_pzval_unlock_func(z, f, u TSRMLS_CC)
#define PZVAL_UNLOCK_FREE(z) zend_pzval_unlock_free_func(z)
#define PZVAL_LOCK(z) Z_ADDREF_P((z))
#define RETURN_VALUE_UNUSED(pzn) (((pzn)->u.EA.type & EXT_TYPE_UNUSED))
} else {
Z_ADDREF_P(value);
*variable_ptr_ptr = value;
+ GC_REMOVE_ZVAL_FROM_BUFFER(variable_ptr);
zendi_zval_dtor(*variable_ptr);
safe_free_zval_ptr(variable_ptr);
return value;
#endif
Z_DELREF_PP(zval_ptr);
if (Z_REFCOUNT_PP(zval_ptr) == 0) {
+ GC_REMOVE_ZVAL_FROM_BUFFER(*zval_ptr);
zval_dtor(*zval_ptr);
safe_free_zval_ptr_rel(*zval_ptr ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_CC);
- } else if (Z_REFCOUNT_PP(zval_ptr) == 1) {
- if ((*zval_ptr)->type == IS_OBJECT) {
- TSRMLS_FETCH();
+ } else {
+ TSRMLS_FETCH();
- if (EG(ze1_compatibility_mode)) {
- return;
+ if (Z_REFCOUNT_PP(zval_ptr) == 1) {
+ if ((*zval_ptr)->type == IS_OBJECT) {
+ if (EG(ze1_compatibility_mode)) {
+ return;
+ }
}
+ Z_UNSET_ISREF_PP(zval_ptr);
}
- Z_UNSET_ISREF_PP(zval_ptr);
+
+ GC_ZVAL_CHECK_POSSIBLE_ROOT(*zval_ptr);
}
}
/* }}} */
--- /dev/null
+/*
+ +----------------------------------------------------------------------+
+ | Zend Engine |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1998-2007 Zend Technologies Ltd. (http://www.zend.com) |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 2.00 of the Zend license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.zend.com/license/2_00.txt. |
+ | If you did not receive a copy of the Zend license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@zend.com so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: David Wang <planetbeing@gmail.com> |
+ | Dmitry Stogov <dmitry@zend.com> |
+ +----------------------------------------------------------------------+
+*/
+
+/* $Id$ */
+
+#include "zend.h"
+#include "zend_API.h"
+
+#define GC_ROOT_BUFFER_MAX_ENTRIES 10000
+
+#ifdef ZTS
+ZEND_API int gc_globals_id;
+#else
+ZEND_API zend_gc_globals gc_globals;
+#endif
+
+/* Forward declarations */
+static int children_scan_black(zval **pz TSRMLS_DC);
+static int children_mark_grey(zval **pz TSRMLS_DC);
+static int children_collect_white(zval **pz TSRMLS_DC);
+static int children_scan(zval **pz TSRMLS_DC);
+
+static void root_buffer_dtor(zend_gc_globals *gc_globals TSRMLS_DC)
+{
+ if (gc_globals->buf) {
+ free(gc_globals->buf);
+ gc_globals->buf = NULL;
+ }
+}
+
+static void gc_globals_ctor_ex(zend_gc_globals *gc_globals TSRMLS_DC)
+{
+ gc_globals->gc_enabled = 0;
+
+ gc_globals->buf = NULL;
+
+ gc_globals->roots.next = NULL;
+ gc_globals->roots.prev = NULL;
+ gc_globals->unused = NULL;
+ gc_globals->zval_to_free = NULL;
+
+ gc_globals->gc_runs = 0;
+ gc_globals->collected = 0;
+
+#if GC_BENCH
+ gc_globals->root_buf_length = 0;
+ gc_globals->root_buf_peak = 0;
+ gc_globals->zval_possible_root = 0;
+ gc_globals->zobj_possible_root = 0;
+ gc_globals->zval_buffered = 0;
+ gc_globals->zobj_buffered = 0;
+ gc_globals->zval_remove_from_buffer = 0;
+ gc_globals->zobj_remove_from_buffer = 0;
+ gc_globals->zval_marked_grey = 0;
+ gc_globals->zobj_marked_grey = 0;
+#endif
+}
+
+ZEND_API void gc_globals_ctor(TSRMLS_D)
+{
+#ifdef ZTS
+ ts_allocate_id(&gc_globals_id, sizeof(zend_gc_globals), (ts_allocate_ctor) gc_globals_ctor_ex, (ts_allocate_dtor) root_buffer_dtor);
+#else
+ gc_globals_ctor_ex(&gc_globals);
+#endif
+}
+
+ZEND_API void gc_globals_dtor(TSRMLS_D)
+{
+#ifndef ZTS
+ root_buffer_dtor(&gc_globals TSRMLS_DC);
+#endif
+}
+
+ZEND_API void gc_reset(TSRMLS_D)
+{
+ int i;
+
+ GC_G(gc_runs) = 0;
+ GC_G(collected) = 0;
+
+#if GC_BENCH
+ GC_G(root_buf_length) = 0;
+ GC_G(root_buf_peak) = 0;
+ GC_G(zval_possible_root) = 0;
+ GC_G(zobj_possible_root) = 0;
+ GC_G(zval_buffered) = 0;
+ GC_G(zobj_buffered) = 0;
+ GC_G(zval_remove_from_buffer) = 0;
+ GC_G(zobj_remove_from_buffer) = 0;
+ GC_G(zval_marked_grey) = 0;
+ GC_G(zobj_marked_grey) = 0;
+#endif
+
+ if (GC_G(buf) &&
+ (GC_G(roots).next != &GC_G(roots) ||
+ GC_G(roots).prev != &GC_G(roots))) {
+
+ GC_G(roots).next = &GC_G(roots);
+ GC_G(roots).prev = &GC_G(roots);
+
+ GC_G(unused) = &GC_G(buf)[0];
+ for (i = 0; i < GC_ROOT_BUFFER_MAX_ENTRIES-1; i++) {
+ GC_G(buf)[i].prev = &GC_G(buf)[i+1];
+ }
+ GC_G(buf)[GC_ROOT_BUFFER_MAX_ENTRIES-1].prev = NULL;
+
+ GC_G(zval_to_free) = NULL;
+ }
+}
+
+ZEND_API void gc_init(TSRMLS_D)
+{
+ if (GC_G(buf) == NULL && GC_G(gc_enabled)) {
+ GC_G(buf) = (gc_root_buffer*) malloc(sizeof(gc_root_buffer) * GC_ROOT_BUFFER_MAX_ENTRIES);
+ gc_reset(TSRMLS_C);
+ }
+}
+
+ZEND_API void gc_zval_possible_root(zval *zv TSRMLS_DC)
+{
+ if (zv->type == IS_OBJECT) {
+ GC_ZOBJ_CHECK_POSSIBLE_ROOT(zv);
+ return;
+ }
+
+ GC_BENCH_INC(zval_possible_root);
+
+ if (GC_ZVAL_GET_COLOR(zv) != GC_PURPLE) {
+ GC_ZVAL_SET_PURPLE(zv);
+
+ if (!GC_ZVAL_ADDRESS(zv)) {
+ gc_root_buffer *newRoot = GC_G(unused);
+
+ if (!newRoot) {
+ if (!GC_G(gc_enabled)) {
+ GC_ZVAL_SET_BLACK(zv);
+ return;
+ }
+ zv->refcount__gc++;
+ gc_collect_cycles(TSRMLS_C);
+ zv->refcount__gc--;
+ GC_ZVAL_SET_PURPLE(zv);
+ newRoot = GC_G(unused);
+ }
+
+ GC_G(unused) = newRoot->prev;
+
+ newRoot->next = GC_G(roots).next;
+ newRoot->prev = &GC_G(roots);
+ GC_G(roots).next->prev = newRoot;
+ GC_G(roots).next = newRoot;
+
+ GC_ZVAL_SET_ADDRESS(zv, newRoot);
+
+ newRoot->handle = 0;
+ newRoot->u.pz = zv;
+
+ GC_BENCH_INC(zval_buffered);
+ GC_BENCH_INC(root_buf_length);
+ GC_BENCH_PEAK(root_buf_peak, root_buf_length);
+ }
+ }
+}
+
+ZEND_API void gc_zobj_possible_root(zval *zv TSRMLS_DC)
+{
+ struct _store_object *obj;
+
+ if (UNEXPECTED(Z_OBJ_HT_P(zv)->get_properties == NULL)) {
+ return;
+ }
+
+ GC_BENCH_INC(zobj_possible_root);
+
+ obj = &EG(objects_store).object_buckets[Z_OBJ_HANDLE_P(zv)].bucket.obj;
+ if (GC_GET_COLOR(obj->buffered) != GC_PURPLE) {
+ GC_SET_PURPLE(obj->buffered);
+ if (!GC_ADDRESS(obj->buffered)) {
+ gc_root_buffer *newRoot = GC_G(unused);
+
+ if (!newRoot) {
+ if (!GC_G(gc_enabled)) {
+ GC_ZVAL_SET_BLACK(zv);
+ return;
+ }
+ zv->refcount__gc++;
+ gc_collect_cycles(TSRMLS_C);
+ zv->refcount__gc--;
+ GC_SET_PURPLE(obj->buffered);
+ newRoot = GC_G(unused);
+ }
+
+ GC_G(unused) = newRoot->prev;
+
+ newRoot->next = GC_G(roots).next;
+ newRoot->prev = &GC_G(roots);
+ GC_G(roots).next->prev = newRoot;
+ GC_G(roots).next = newRoot;
+
+ GC_SET_ADDRESS(obj->buffered, newRoot);
+
+ newRoot->handle = Z_OBJ_HANDLE_P(zv);
+ newRoot->u.handlers = Z_OBJ_HT_P(zv);
+
+ GC_BENCH_INC(zobj_buffered);
+ GC_BENCH_INC(root_buf_length);
+ GC_BENCH_PEAK(root_buf_peak, root_buf_length);
+ }
+ }
+}
+
+static void zobj_scan_black(struct _store_object *obj, zval *pz TSRMLS_DC)
+{
+ GC_SET_BLACK(obj->buffered);
+
+ if (EXPECTED(Z_OBJ_HANDLER_P(pz, get_properties) != NULL)) {
+ zend_hash_apply(Z_OBJPROP_P(pz), (apply_func_t) children_scan_black TSRMLS_CC);
+ }
+}
+
+static void zval_scan_black(zval *pz TSRMLS_DC)
+{
+ GC_ZVAL_SET_BLACK(pz);
+
+ if (Z_TYPE_P(pz) == IS_OBJECT) {
+ struct _store_object *obj = &EG(objects_store).object_buckets[Z_OBJ_HANDLE_P(pz)].bucket.obj;
+
+ obj->refcount++;
+ if (GC_GET_COLOR(obj->buffered) != GC_BLACK) {
+ zobj_scan_black(obj, pz TSRMLS_CC);
+ }
+ } else if (Z_TYPE_P(pz) == IS_ARRAY) {
+ if (Z_ARRVAL_P(pz) != &EG(symbol_table)) {
+ zend_hash_apply(Z_ARRVAL_P(pz), (apply_func_t) children_scan_black TSRMLS_CC);
+ }
+ }
+}
+
+static int children_scan_black(zval **pz TSRMLS_DC)
+{
+ (*pz)->refcount__gc++;
+
+ if (GC_ZVAL_GET_COLOR(*pz) != GC_BLACK) {
+ zval_scan_black(*pz TSRMLS_CC);
+ }
+
+ return 0;
+}
+
+static void zobj_mark_grey(struct _store_object *obj, zval *pz TSRMLS_DC)
+{
+ if (GC_GET_COLOR(obj->buffered) != GC_GREY) {
+ GC_BENCH_INC(zobj_marked_grey);
+ GC_SET_COLOR(obj->buffered, GC_GREY);
+ if (EXPECTED(Z_OBJ_HANDLER_P(pz, get_properties) != NULL)) {
+ zend_hash_apply(Z_OBJPROP_P(pz), (apply_func_t) children_mark_grey TSRMLS_CC);
+ }
+ }
+}
+
+static void zval_mark_grey(zval *pz TSRMLS_DC)
+{
+ if (GC_ZVAL_GET_COLOR(pz) != GC_GREY) {
+ GC_BENCH_INC(zval_marked_grey);
+ GC_ZVAL_SET_COLOR(pz, GC_GREY);
+
+ if (Z_TYPE_P(pz) == IS_OBJECT) {
+ struct _store_object *obj = &EG(objects_store).object_buckets[Z_OBJ_HANDLE_P(pz)].bucket.obj;
+
+ obj->refcount--;
+ zobj_mark_grey(obj, pz TSRMLS_CC);
+ } else if (Z_TYPE_P(pz) == IS_ARRAY) {
+ if (Z_ARRVAL_P(pz) == &EG(symbol_table)) {
+ GC_ZVAL_SET_BLACK(pz);
+ } else {
+ zend_hash_apply(Z_ARRVAL_P(pz), (apply_func_t) children_mark_grey TSRMLS_CC);
+ }
+ }
+ }
+}
+
+static int children_mark_grey(zval **pz TSRMLS_DC)
+{
+ (*pz)->refcount__gc--;
+ zval_mark_grey(*pz TSRMLS_CC);
+ return 0;
+}
+
+static void gc_mark_roots(TSRMLS_D)
+{
+ gc_root_buffer *current = GC_G(roots).next;
+
+ while (current != &GC_G(roots)) {
+ if (current->handle) {
+ struct _store_object *obj = &EG(objects_store).object_buckets[current->handle].bucket.obj;
+
+ if (GC_GET_COLOR(obj->buffered) == GC_PURPLE) {
+ zval z;
+
+ INIT_PZVAL(&z);
+ Z_OBJ_HANDLE(z) = current->handle;
+ Z_OBJ_HT(z) = current->u.handlers;
+ zobj_mark_grey(obj, &z TSRMLS_CC);
+ } else {
+ GC_SET_ADDRESS(obj->buffered, NULL);
+ GC_REMOVE_FROM_BUFFER(current);
+ }
+ } else {
+ if (GC_ZVAL_GET_COLOR(current->u.pz) == GC_PURPLE) {
+ zval_mark_grey(current->u.pz TSRMLS_CC);
+ } else {
+ GC_ZVAL_SET_ADDRESS(current->u.pz, NULL);
+ GC_REMOVE_FROM_BUFFER(current);
+ }
+ }
+ current = current->next;
+ }
+}
+
+static void zobj_scan(zval *pz TSRMLS_DC)
+{
+ struct _store_object *obj = &EG(objects_store).object_buckets[Z_OBJ_HANDLE_P(pz)].bucket.obj;
+
+ if (GC_GET_COLOR(obj->buffered) == GC_GREY) {
+ if (obj->refcount > 0) {
+ zobj_scan_black(obj, pz TSRMLS_CC);
+ } else {
+ GC_SET_COLOR(obj->buffered, GC_WHITE);
+ if (EXPECTED(Z_OBJ_HANDLER_P(pz, get_properties) != NULL)) {
+ zend_hash_apply(Z_OBJPROP_P(pz), (apply_func_t) children_scan TSRMLS_CC);
+ }
+ }
+ }
+}
+
+static int zval_scan(zval *pz TSRMLS_DC)
+{
+ if (GC_ZVAL_GET_COLOR(pz) == GC_GREY) {
+ if (pz->refcount__gc > 0) {
+ 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) {
+ if (Z_ARRVAL_P(pz) == &EG(symbol_table)) {
+ GC_ZVAL_SET_BLACK(pz);
+ } else {
+ zend_hash_apply(Z_ARRVAL_P(pz), (apply_func_t) children_scan TSRMLS_CC);
+ }
+ }
+ }
+ }
+ return 0;
+}
+
+static int children_scan(zval **pz TSRMLS_DC)
+{
+ zval_scan(*pz TSRMLS_CC);
+ return 0;
+}
+
+static void gc_scan_roots(TSRMLS_D)
+{
+ gc_root_buffer *current = GC_G(roots).next;
+
+ while (current != &GC_G(roots)) {
+ if (current->handle) {
+ zval z;
+
+ INIT_PZVAL(&z);
+ Z_OBJ_HANDLE(z) = current->handle;
+ Z_OBJ_HT(z) = current->u.handlers;
+ zobj_scan(&z TSRMLS_CC);
+ } else {
+ zval_scan(current->u.pz TSRMLS_CC);
+ }
+ current = current->next;
+ }
+}
+
+static void zobj_collect_white(zval *pz TSRMLS_DC)
+{
+ zend_object_handle handle = Z_OBJ_HANDLE_P(pz);
+ struct _store_object *obj = &EG(objects_store).object_buckets[handle].bucket.obj;
+
+ if (obj->buffered == (gc_root_buffer*)GC_WHITE) {
+ GC_SET_BLACK(obj->buffered);
+
+ if (EXPECTED(Z_OBJ_HANDLER_P(pz, get_properties) != NULL)) {
+ zend_hash_apply(Z_OBJPROP_P(pz), (apply_func_t) children_collect_white TSRMLS_CC);
+ }
+ }
+}
+
+static void zval_collect_white(zval *pz TSRMLS_DC)
+{
+ if (((zval_gc_info*)(pz))->u.buffered == (gc_root_buffer*)GC_WHITE) {
+ GC_ZVAL_SET_BLACK(pz);
+
+ if (Z_TYPE_P(pz) == IS_OBJECT) {
+ zobj_collect_white(pz TSRMLS_CC);
+ } else {
+ if (Z_TYPE_P(pz) == IS_ARRAY) {
+ if (Z_ARRVAL_P(pz) == &EG(symbol_table)) {
+ return;
+ }
+ zend_hash_apply(Z_ARRVAL_P(pz), (apply_func_t) children_collect_white TSRMLS_CC);
+ }
+ /* restore refcount */
+ pz->refcount__gc++;
+ }
+
+ ((zval_gc_info*)pz)->u.next = GC_G(zval_to_free);
+ GC_G(zval_to_free) = (zval_gc_info*)pz;
+ }
+}
+
+static int children_collect_white(zval **pz TSRMLS_DC)
+{
+ zval_collect_white(*pz TSRMLS_CC);
+ return 0;
+}
+
+static void gc_collect_roots(TSRMLS_D)
+{
+ gc_root_buffer *current = GC_G(roots).next;
+
+ while (current != &GC_G(roots)) {
+ if (current->handle) {
+ struct _store_object *obj = &EG(objects_store).object_buckets[current->handle].bucket.obj;
+ zval z;
+
+ GC_SET_ADDRESS(obj->buffered, NULL);
+ INIT_PZVAL(&z);
+ Z_OBJ_HANDLE(z) = current->handle;
+ Z_OBJ_HT(z) = current->u.handlers;
+ zobj_collect_white(&z TSRMLS_CC);
+ } else {
+ GC_ZVAL_SET_ADDRESS(current->u.pz, NULL);
+ zval_collect_white(current->u.pz TSRMLS_CC);
+ }
+
+ GC_REMOVE_FROM_BUFFER(current);
+ current = current->next;
+ }
+}
+
+ZEND_API int gc_collect_cycles(TSRMLS_D)
+{
+ int count = 0;
+
+ if (GC_G(roots).next != &GC_G(roots)) {
+ zval_gc_info *p, *q;
+
+ GC_G(gc_runs)++;
+ GC_G(zval_to_free) = NULL;
+ gc_mark_roots(TSRMLS_C);
+ gc_scan_roots(TSRMLS_C);
+ gc_collect_roots(TSRMLS_C);
+
+ p = GC_G(zval_to_free);
+ GC_G(zval_to_free) = NULL;
+ 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 <= 1) {
+ if (EXPECTED(Z_OBJ_HANDLER(p->z, get_properties) != NULL)) {
+ Z_OBJPROP(p->z)->pDestructor = NULL;
+ }
+ 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);
+ }
+ } else {
+ if (Z_TYPE(p->z) == IS_ARRAY) {
+ Z_ARRVAL(p->z)->pDestructor = NULL;
+ }
+ zval_dtor(&p->z);
+ }
+ FREE_ZVAL_EX(&p->z);
+ p = q;
+ count++;
+ }
+ GC_G(collected) += count;
+ }
+
+ return count;
+}
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * indent-tabs-mode: t
+ * End:
+ */
--- /dev/null
+/*
+ +----------------------------------------------------------------------+
+ | Zend Engine |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1998-2007 Zend Technologies Ltd. (http://www.zend.com) |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 2.00 of the Zend license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.zend.com/license/2_00.txt. |
+ | If you did not receive a copy of the Zend license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@zend.com so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: David Wang <planetbeing@gmail.com> |
+ | Dmitry Stogov <dmitry@zend.com> |
+ +----------------------------------------------------------------------+
+*/
+
+/* $Id$ */
+
+#ifndef ZEND_GC_H
+#define ZEND_GC_H
+
+#ifndef GC_BENCH
+# define GC_BENCH 0
+#endif
+
+#if GC_BENCH
+# define GC_BENCH_INC(counter) GC_G(counter)++
+# define GC_BENCH_DEC(counter) GC_G(counter)--
+# define GC_BENCH_PEAK(peak, counter) do { \
+ if (GC_G(counter) > GC_G(peak)) { \
+ GC_G(peak) = GC_G(counter); \
+ } \
+ } while (0)
+#else
+# define GC_BENCH_INC(counter)
+# define GC_BENCH_DEC(counter)
+# define GC_BENCH_PEAK(peak, counter)
+#endif
+
+#define GC_COLOR 0x03
+
+#define GC_BLACK 0x00
+#define GC_WHITE 0x01
+#define GC_GREY 0x02
+#define GC_PURPLE 0x03
+
+#define GC_ADDRESS(v) \
+ ((gc_root_buffer*)(((zend_uintptr_t)(v)) & ~GC_COLOR))
+#define GC_SET_ADDRESS(v, a) \
+ (v) = ((gc_root_buffer*)((((zend_uintptr_t)(v)) & GC_COLOR) | ((zend_uintptr_t)(a))))
+#define GC_GET_COLOR(v) \
+ (((zend_uintptr_t)(v)) & GC_COLOR)
+#define GC_SET_COLOR(v, c) \
+ (v) = ((gc_root_buffer*)((((zend_uintptr_t)(v)) & ~GC_COLOR) | (c)))
+#define GC_SET_BLACK(v) \
+ (v) = ((gc_root_buffer*)(((zend_uintptr_t)(v)) & ~GC_COLOR))
+#define GC_SET_PURPLE(v) \
+ (v) = ((gc_root_buffer*)(((zend_uintptr_t)(v)) | GC_PURPLE))
+
+#define GC_ZVAL_INIT(z) \
+ ((zval_gc_info*)(z))->u.buffered = NULL
+#define GC_ZVAL_ADDRESS(v) \
+ GC_ADDRESS(((zval_gc_info*)(v))->u.buffered)
+#define GC_ZVAL_SET_ADDRESS(v, a) \
+ GC_SET_ADDRESS(((zval_gc_info*)(v))->u.buffered, (a))
+#define GC_ZVAL_GET_COLOR(v) \
+ GC_GET_COLOR(((zval_gc_info*)(v))->u.buffered)
+#define GC_ZVAL_SET_COLOR(v, c) \
+ GC_SET_COLOR(((zval_gc_info*)(v))->u.buffered, (c))
+#define GC_ZVAL_SET_BLACK(v) \
+ GC_SET_BLACK(((zval_gc_info*)(v))->u.buffered)
+#define GC_ZVAL_SET_PURPLE(v) \
+ GC_SET_PURPLE(((zval_gc_info*)(v))->u.buffered)
+
+#define GC_OBJ_INIT(z) \
+ (z)->buffered = NULL
+
+typedef struct _gc_root_buffer {
+ struct _gc_root_buffer *prev; /* double-linked list */
+ struct _gc_root_buffer *next;
+ zend_object_handle handle; /* must be 0 for zval */
+ union {
+ zval *pz;
+ zend_object_handlers *handlers;
+ } u;
+} gc_root_buffer;
+
+typedef struct _zval_gc_info {
+ zval z;
+ union {
+ gc_root_buffer *buffered;
+ struct _zval_gc_info *next;
+ } u;
+} zval_gc_info;
+
+typedef struct _zend_gc_globals {
+ zend_bool gc_enabled;
+
+ gc_root_buffer *buf; /* preallocated arrays of buffers */
+ gc_root_buffer roots; /* list of possible roots of cycles */
+ gc_root_buffer *unused; /* list of unused buffers */
+
+ zval_gc_info *zval_to_free; /* temporaryt list of zvals to free */
+
+ zend_uint gc_runs;
+ zend_uint collected;
+
+#if GC_BENCH
+ zend_uint root_buf_length;
+ zend_uint root_buf_peak;
+ zend_uint zval_possible_root;
+ zend_uint zobj_possible_root;
+ zend_uint zval_buffered;
+ zend_uint zobj_buffered;
+ zend_uint zval_remove_from_buffer;
+ zend_uint zobj_remove_from_buffer;
+ zend_uint zval_marked_grey;
+ zend_uint zobj_marked_grey;
+#endif
+
+} zend_gc_globals;
+
+#ifdef ZTS
+BEGIN_EXTERN_C()
+ZEND_API extern int gc_globals_id;
+END_EXTERN_C()
+#define GC_G(v) TSRMG(gc_globals_id, zend_gc_globals *, v)
+#else
+#define GC_G(v) (gc_globals.v)
+extern ZEND_API zend_gc_globals gc_globals;
+#endif
+
+BEGIN_EXTERN_C()
+ZEND_API int gc_collect_cycles(TSRMLS_D);
+ZEND_API void gc_zval_possible_root(zval *zv TSRMLS_DC);
+ZEND_API void gc_zobj_possible_root(zval *zv TSRMLS_DC);
+ZEND_API void gc_globals_ctor(TSRMLS_D);
+ZEND_API void gc_globals_dtor(TSRMLS_D);
+ZEND_API void gc_init(TSRMLS_D);
+ZEND_API void gc_reset(TSRMLS_D);
+END_EXTERN_C()
+
+#define GC_ZVAL_CHECK_POSSIBLE_ROOT(z) \
+ gc_zval_check_possible_root((z) TSRMLS_CC)
+
+#define GC_REMOVE_FROM_BUFFER(current) \
+ gc_remove_from_buffer((current) TSRMLS_CC)
+
+#define GC_REMOVE_ZVAL_FROM_BUFFER(z) \
+ gc_remove_zval_from_buffer(z)
+
+#define GC_ZOBJ_CHECK_POSSIBLE_ROOT(zobject) \
+ do { \
+ if (EXPECTED(EG(objects_store).object_buckets != NULL) && \
+ EG(objects_store).object_buckets[Z_OBJ_HANDLE_P(zobject)].valid) { \
+ gc_zobj_possible_root(zobject TSRMLS_CC); \
+ } \
+ } while (0)
+
+#define GC_REMOVE_ZOBJ_FROM_BUFFER(obj) \
+ do { \
+ if (GC_ADDRESS((obj)->buffered)) { \
+ GC_BENCH_INC(zobj_remove_from_buffer); \
+ GC_REMOVE_FROM_BUFFER(GC_ADDRESS((obj)->buffered)); \
+ (obj)->buffered = NULL; \
+ } \
+ } while (0)
+
+static zend_always_inline void gc_zval_check_possible_root(zval *z TSRMLS_DC)
+{
+ if (z->type == IS_ARRAY || z->type == IS_OBJECT) {
+ gc_zval_possible_root(z TSRMLS_CC);
+ }
+}
+
+static zend_always_inline void gc_remove_from_buffer(gc_root_buffer *root TSRMLS_DC)
+{
+ root->next->prev = root->prev;
+ root->prev->next = root->next;
+ root->prev = GC_G(unused);
+ GC_G(unused) = root;
+ GC_BENCH_DEC(root_buf_length);
+}
+
+static zend_always_inline void gc_remove_zval_from_buffer(zval* z)
+{
+ gc_root_buffer* root_buffer;
+
+ root_buffer = GC_ADDRESS(((zval_gc_info*)z)->u.buffered);
+ if (root_buffer) {
+ TSRMLS_FETCH();
+
+ GC_BENCH_INC(zval_remove_from_buffer);
+ GC_REMOVE_FROM_BUFFER(root_buffer);
+ ((zval_gc_info*)z)->u.buffered = NULL;
+ }
+}
+
+/* The following macroses override macroses from zend_alloc.h */
+#undef ALLOC_ZVAL
+#define ALLOC_ZVAL(z) \
+ do { \
+ (z) = (zval*)emalloc(sizeof(zval_gc_info)); \
+ GC_ZVAL_INIT(z); \
+ } while (0)
+
+#undef FREE_ZVAL
+#define FREE_ZVAL(z) \
+ do { \
+ GC_REMOVE_ZVAL_FROM_BUFFER(z); \
+ efree(z); \
+ } while (0)
+
+#undef ALLOC_ZVAL_REL
+#define ALLOC_ZVAL_REL(z) \
+ do { \
+ (z) = (zval*)emalloc_rel(sizeof(zval_gc_info)); \
+ GC_ZVAL_INIT(z); \
+ } while (0)
+
+#undef FREE_ZVAL_REL
+#define FREE_ZVAL_REL(z) \
+ do { \
+ GC_REMOVE_ZVAL_FROM_BUFFER(z); \
+ efree_rel(z); \
+ } while (0)
+
+#define FREE_ZVAL_EX(z) \
+ efree(z)
+
+#define FREE_ZVAL_REL_EX(z) \
+ efree_rel(z)
+
+#endif /* ZEND_GC_H */
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * indent-tabs-mode: t
+ * End:
+ */
if (objects->object_buckets[i].valid) {
struct _store_object *obj = &objects->object_buckets[i].bucket.obj;
+ GC_REMOVE_ZOBJ_FROM_BUFFER(obj);
+
objects->object_buckets[i].valid = 0;
if (obj->free_storage) {
obj->free_storage(obj->object TSRMLS_CC);
EG(objects_store).object_buckets[handle].valid = 1;
obj->refcount = 1;
+ GC_OBJ_INIT(obj);
obj->object = object;
obj->dtor = dtor?dtor:(zend_objects_store_dtor_t)zend_objects_destroy_object;
obj->free_storage = free_storage;
Z_ADDREF_P(zobject);
zend_objects_store_del_ref_by_handle(handle TSRMLS_CC);
Z_DELREF_P(zobject);
+
+ GC_ZOBJ_CHECK_POSSIBLE_ROOT(zobject);
}
/*
}
}
if (obj->refcount == 1) {
+ GC_REMOVE_ZOBJ_FROM_BUFFER(obj);
if (obj->free_storage) {
zend_try {
obj->free_storage(obj->object TSRMLS_CC);
zend_objects_free_object_storage_t free_storage;
zend_objects_store_clone_t clone;
zend_uint refcount;
+ gc_root_buffer *buffered;
} obj;
struct {
int next;
static inline void zend_free_obj_get_result(zval *op)
{
if (Z_REFCOUNT_P(op) == 0) {
+ GC_REMOVE_ZVAL_FROM_BUFFER(op);
zval_dtor(op);
FREE_ZVAL(op);
} else {
zval *value = Z_OBJ_HT_P(z)->get(z TSRMLS_CC);
if (Z_REFCOUNT_P(z) == 0) {
+ GC_REMOVE_ZVAL_FROM_BUFFER(z);
zval_dtor(z);
FREE_ZVAL(z);
}
zval *value = Z_OBJ_HT_P(z)->get(z TSRMLS_CC);
if (Z_REFCOUNT_P(z) == 0) {
+ GC_REMOVE_ZVAL_FROM_BUFFER(z);
zval_dtor(z);
FREE_ZVAL(z);
}
zval *value = Z_OBJ_HT_P(z)->get(z TSRMLS_CC);
if (Z_REFCOUNT_P(z) == 0) {
+ GC_REMOVE_ZVAL_FROM_BUFFER(z);
zval_dtor(z);
FREE_ZVAL(z);
}
if (RETURN_VALUE_UNUSED(&opline->result)) {
if (Z_REFCOUNT_P(retval) == 0) {
+ GC_REMOVE_ZVAL_FROM_BUFFER(retval);
zval_dtor(retval);
FREE_ZVAL(retval);
}
zval *value = Z_OBJ_HT_P(z)->get(z TSRMLS_CC);
if (Z_REFCOUNT_P(z) == 0) {
+ GC_REMOVE_ZVAL_FROM_BUFFER(z);
zval_dtor(z);
FREE_ZVAL(z);
}
zval *value = Z_OBJ_HT_P(z)->get(z TSRMLS_CC);
if (Z_REFCOUNT_P(z) == 0) {
+ GC_REMOVE_ZVAL_FROM_BUFFER(z);
zval_dtor(z);
FREE_ZVAL(z);
}
zval *value = Z_OBJ_HT_P(z)->get(z TSRMLS_CC);
if (Z_REFCOUNT_P(z) == 0) {
+ GC_REMOVE_ZVAL_FROM_BUFFER(z);
zval_dtor(z);
FREE_ZVAL(z);
}
if (RETURN_VALUE_UNUSED(&opline->result)) {
if (Z_REFCOUNT_P(retval) == 0) {
+ GC_REMOVE_ZVAL_FROM_BUFFER(retval);
zval_dtor(retval);
FREE_ZVAL(retval);
}
zval *value = Z_OBJ_HT_P(z)->get(z TSRMLS_CC);
if (Z_REFCOUNT_P(z) == 0) {
+ GC_REMOVE_ZVAL_FROM_BUFFER(z);
zval_dtor(z);
FREE_ZVAL(z);
}
zval *value = Z_OBJ_HT_P(z)->get(z TSRMLS_CC);
if (Z_REFCOUNT_P(z) == 0) {
+ GC_REMOVE_ZVAL_FROM_BUFFER(z);
zval_dtor(z);
FREE_ZVAL(z);
}
zval *value = Z_OBJ_HT_P(z)->get(z TSRMLS_CC);
if (Z_REFCOUNT_P(z) == 0) {
+ GC_REMOVE_ZVAL_FROM_BUFFER(z);
zval_dtor(z);
FREE_ZVAL(z);
}
if (RETURN_VALUE_UNUSED(&opline->result)) {
if (Z_REFCOUNT_P(retval) == 0) {
+ GC_REMOVE_ZVAL_FROM_BUFFER(retval);
zval_dtor(retval);
FREE_ZVAL(retval);
}
zval *value = Z_OBJ_HT_P(z)->get(z TSRMLS_CC);
if (Z_REFCOUNT_P(z) == 0) {
+ GC_REMOVE_ZVAL_FROM_BUFFER(z);
zval_dtor(z);
FREE_ZVAL(z);
}
zval *value = Z_OBJ_HT_P(z)->get(z TSRMLS_CC);
if (Z_REFCOUNT_P(z) == 0) {
+ GC_REMOVE_ZVAL_FROM_BUFFER(z);
zval_dtor(z);
FREE_ZVAL(z);
}
zval *value = Z_OBJ_HT_P(z)->get(z TSRMLS_CC);
if (Z_REFCOUNT_P(z) == 0) {
+ GC_REMOVE_ZVAL_FROM_BUFFER(z);
zval_dtor(z);
FREE_ZVAL(z);
}
if (RETURN_VALUE_UNUSED(&opline->result)) {
if (Z_REFCOUNT_P(retval) == 0) {
+ GC_REMOVE_ZVAL_FROM_BUFFER(retval);
zval_dtor(retval);
FREE_ZVAL(retval);
}
zval *value = Z_OBJ_HT_P(z)->get(z TSRMLS_CC);
if (Z_REFCOUNT_P(z) == 0) {
+ GC_REMOVE_ZVAL_FROM_BUFFER(z);
zval_dtor(z);
FREE_ZVAL(z);
}
zval *value = Z_OBJ_HT_P(z)->get(z TSRMLS_CC);
if (Z_REFCOUNT_P(z) == 0) {
+ GC_REMOVE_ZVAL_FROM_BUFFER(z);
zval_dtor(z);
FREE_ZVAL(z);
}
zval *value = Z_OBJ_HT_P(z)->get(z TSRMLS_CC);
if (Z_REFCOUNT_P(z) == 0) {
+ GC_REMOVE_ZVAL_FROM_BUFFER(z);
zval_dtor(z);
FREE_ZVAL(z);
}
zval *value = Z_OBJ_HT_P(z)->get(z TSRMLS_CC);
if (Z_REFCOUNT_P(z) == 0) {
+ GC_REMOVE_ZVAL_FROM_BUFFER(z);
zval_dtor(z);
FREE_ZVAL(z);
}
if (RETURN_VALUE_UNUSED(&opline->result)) {
if (Z_REFCOUNT_P(retval) == 0) {
+ GC_REMOVE_ZVAL_FROM_BUFFER(retval);
zval_dtor(retval);
FREE_ZVAL(retval);
}
zval *value = Z_OBJ_HT_P(z)->get(z TSRMLS_CC);
if (Z_REFCOUNT_P(z) == 0) {
+ GC_REMOVE_ZVAL_FROM_BUFFER(z);
zval_dtor(z);
FREE_ZVAL(z);
}
zval *value = Z_OBJ_HT_P(z)->get(z TSRMLS_CC);
if (Z_REFCOUNT_P(z) == 0) {
+ GC_REMOVE_ZVAL_FROM_BUFFER(z);
zval_dtor(z);
FREE_ZVAL(z);
}
zval *value = Z_OBJ_HT_P(z)->get(z TSRMLS_CC);
if (Z_REFCOUNT_P(z) == 0) {
+ GC_REMOVE_ZVAL_FROM_BUFFER(z);
zval_dtor(z);
FREE_ZVAL(z);
}
if (RETURN_VALUE_UNUSED(&opline->result)) {
if (Z_REFCOUNT_P(retval) == 0) {
+ GC_REMOVE_ZVAL_FROM_BUFFER(retval);
zval_dtor(retval);
FREE_ZVAL(retval);
}
zval *value = Z_OBJ_HT_P(z)->get(z TSRMLS_CC);
if (Z_REFCOUNT_P(z) == 0) {
+ GC_REMOVE_ZVAL_FROM_BUFFER(z);
zval_dtor(z);
FREE_ZVAL(z);
}
zval *value = Z_OBJ_HT_P(z)->get(z TSRMLS_CC);
if (Z_REFCOUNT_P(z) == 0) {
+ GC_REMOVE_ZVAL_FROM_BUFFER(z);
zval_dtor(z);
FREE_ZVAL(z);
}
zval *value = Z_OBJ_HT_P(z)->get(z TSRMLS_CC);
if (Z_REFCOUNT_P(z) == 0) {
+ GC_REMOVE_ZVAL_FROM_BUFFER(z);
zval_dtor(z);
FREE_ZVAL(z);
}
if (RETURN_VALUE_UNUSED(&opline->result)) {
if (Z_REFCOUNT_P(retval) == 0) {
+ GC_REMOVE_ZVAL_FROM_BUFFER(retval);
zval_dtor(retval);
FREE_ZVAL(retval);
}
zval *value = Z_OBJ_HT_P(z)->get(z TSRMLS_CC);
if (Z_REFCOUNT_P(z) == 0) {
+ GC_REMOVE_ZVAL_FROM_BUFFER(z);
zval_dtor(z);
FREE_ZVAL(z);
}
zval *value = Z_OBJ_HT_P(z)->get(z TSRMLS_CC);
if (Z_REFCOUNT_P(z) == 0) {
+ GC_REMOVE_ZVAL_FROM_BUFFER(z);
zval_dtor(z);
FREE_ZVAL(z);
}
zval *value = Z_OBJ_HT_P(z)->get(z TSRMLS_CC);
if (Z_REFCOUNT_P(z) == 0) {
+ GC_REMOVE_ZVAL_FROM_BUFFER(z);
zval_dtor(z);
FREE_ZVAL(z);
}
if (RETURN_VALUE_UNUSED(&opline->result)) {
if (Z_REFCOUNT_P(retval) == 0) {
+ GC_REMOVE_ZVAL_FROM_BUFFER(retval);
zval_dtor(retval);
FREE_ZVAL(retval);
}
zval *value = Z_OBJ_HT_P(z)->get(z TSRMLS_CC);
if (Z_REFCOUNT_P(z) == 0) {
+ GC_REMOVE_ZVAL_FROM_BUFFER(z);
zval_dtor(z);
FREE_ZVAL(z);
}
zval *value = Z_OBJ_HT_P(z)->get(z TSRMLS_CC);
if (Z_REFCOUNT_P(z) == 0) {
+ GC_REMOVE_ZVAL_FROM_BUFFER(z);
zval_dtor(z);
FREE_ZVAL(z);
}
zval *value = Z_OBJ_HT_P(z)->get(z TSRMLS_CC);
if (Z_REFCOUNT_P(z) == 0) {
+ GC_REMOVE_ZVAL_FROM_BUFFER(z);
zval_dtor(z);
FREE_ZVAL(z);
}
zval *value = Z_OBJ_HT_P(z)->get(z TSRMLS_CC);
if (Z_REFCOUNT_P(z) == 0) {
+ GC_REMOVE_ZVAL_FROM_BUFFER(z);
zval_dtor(z);
FREE_ZVAL(z);
}
if (RETURN_VALUE_UNUSED(&opline->result)) {
if (Z_REFCOUNT_P(retval) == 0) {
+ GC_REMOVE_ZVAL_FROM_BUFFER(retval);
zval_dtor(retval);
FREE_ZVAL(retval);
}
zval *value = Z_OBJ_HT_P(z)->get(z TSRMLS_CC);
if (Z_REFCOUNT_P(z) == 0) {
+ GC_REMOVE_ZVAL_FROM_BUFFER(z);
zval_dtor(z);
FREE_ZVAL(z);
}
zval *value = Z_OBJ_HT_P(z)->get(z TSRMLS_CC);
if (Z_REFCOUNT_P(z) == 0) {
+ GC_REMOVE_ZVAL_FROM_BUFFER(z);
zval_dtor(z);
FREE_ZVAL(z);
}
zval *value = Z_OBJ_HT_P(z)->get(z TSRMLS_CC);
if (Z_REFCOUNT_P(z) == 0) {
+ GC_REMOVE_ZVAL_FROM_BUFFER(z);
zval_dtor(z);
FREE_ZVAL(z);
}
if (RETURN_VALUE_UNUSED(&opline->result)) {
if (Z_REFCOUNT_P(retval) == 0) {
+ GC_REMOVE_ZVAL_FROM_BUFFER(retval);
zval_dtor(retval);
FREE_ZVAL(retval);
}
zval *value = Z_OBJ_HT_P(z)->get(z TSRMLS_CC);
if (Z_REFCOUNT_P(z) == 0) {
+ GC_REMOVE_ZVAL_FROM_BUFFER(z);
zval_dtor(z);
FREE_ZVAL(z);
}
zval *value = Z_OBJ_HT_P(z)->get(z TSRMLS_CC);
if (Z_REFCOUNT_P(z) == 0) {
+ GC_REMOVE_ZVAL_FROM_BUFFER(z);
zval_dtor(z);
FREE_ZVAL(z);
}
zval *value = Z_OBJ_HT_P(z)->get(z TSRMLS_CC);
if (Z_REFCOUNT_P(z) == 0) {
+ GC_REMOVE_ZVAL_FROM_BUFFER(z);
zval_dtor(z);
FREE_ZVAL(z);
}
if (RETURN_VALUE_UNUSED(&opline->result)) {
if (Z_REFCOUNT_P(retval) == 0) {
+ GC_REMOVE_ZVAL_FROM_BUFFER(retval);
zval_dtor(retval);
FREE_ZVAL(retval);
}
zval *value = Z_OBJ_HT_P(z)->get(z TSRMLS_CC);
if (Z_REFCOUNT_P(z) == 0) {
+ GC_REMOVE_ZVAL_FROM_BUFFER(z);
zval_dtor(z);
FREE_ZVAL(z);
}
zval *value = Z_OBJ_HT_P(z)->get(z TSRMLS_CC);
if (Z_REFCOUNT_P(z) == 0) {
+ GC_REMOVE_ZVAL_FROM_BUFFER(z);
zval_dtor(z);
FREE_ZVAL(z);
}
zval *value = Z_OBJ_HT_P(z)->get(z TSRMLS_CC);
if (Z_REFCOUNT_P(z) == 0) {
+ GC_REMOVE_ZVAL_FROM_BUFFER(z);
zval_dtor(z);
FREE_ZVAL(z);
}
if (RETURN_VALUE_UNUSED(&opline->result)) {
if (Z_REFCOUNT_P(retval) == 0) {
+ GC_REMOVE_ZVAL_FROM_BUFFER(retval);
zval_dtor(retval);
FREE_ZVAL(retval);
}
zval *value = Z_OBJ_HT_P(z)->get(z TSRMLS_CC);
if (Z_REFCOUNT_P(z) == 0) {
+ GC_REMOVE_ZVAL_FROM_BUFFER(z);
zval_dtor(z);
FREE_ZVAL(z);
}
zval *value = Z_OBJ_HT_P(z)->get(z TSRMLS_CC);
if (Z_REFCOUNT_P(z) == 0) {
+ GC_REMOVE_ZVAL_FROM_BUFFER(z);
zval_dtor(z);
FREE_ZVAL(z);
}
zval *value = Z_OBJ_HT_P(z)->get(z TSRMLS_CC);
if (Z_REFCOUNT_P(z) == 0) {
+ GC_REMOVE_ZVAL_FROM_BUFFER(z);
zval_dtor(z);
FREE_ZVAL(z);
}
zval *value = Z_OBJ_HT_P(z)->get(z TSRMLS_CC);
if (Z_REFCOUNT_P(z) == 0) {
+ GC_REMOVE_ZVAL_FROM_BUFFER(z);
zval_dtor(z);
FREE_ZVAL(z);
}
if (RETURN_VALUE_UNUSED(&opline->result)) {
if (Z_REFCOUNT_P(retval) == 0) {
+ GC_REMOVE_ZVAL_FROM_BUFFER(retval);
zval_dtor(retval);
FREE_ZVAL(retval);
}
zend_variables.c zend.c zend_API.c zend_extensions.c zend_hash.c \
zend_list.c zend_indent.c zend_builtin_functions.c zend_sprintf.c \
zend_ini.c zend_qsort.c zend_multibyte.c zend_ts_hash.c zend_stream.c \
- zend_iterators.c zend_interfaces.c zend_exceptions.c zend_strtod.c)
+ zend_iterators.c zend_interfaces.c zend_exceptions.c zend_strtod.c zend_gc.c)
if test -r "$abs_srcdir/Zend/zend_objects.c"; then
PHP_ADD_SOURCES(Zend, zend_objects.c zend_object_handlers.c zend_objects_API.c \
ts_allocate_id(&php_win32_core_globals_id, sizeof(php_win32_core_globals), (ts_allocate_ctor) php_win32_core_globals_ctor, (ts_allocate_dtor) php_win32_core_globals_dtor);
#endif
#endif
+ gc_globals_ctor(TSRMLS_C);
+
EG(bailout) = NULL;
EG(error_reporting) = E_ALL & ~E_NOTICE;
zend_ini_shutdown(TSRMLS_C);
shutdown_memory_manager(CG(unclean_shutdown), 1 TSRMLS_CC);
core_globals_dtor(&core_globals TSRMLS_CC);
+ gc_globals_dtor(TSRMLS_C);
#else
zend_ini_global_shutdown(TSRMLS_C);
ts_free_id(core_globals_id);