]> granicus.if.org Git - php/commitdiff
Added garbage collector
authorDmitry Stogov <dmitry@php.net>
Tue, 22 Jan 2008 09:29:29 +0000 (09:29 +0000)
committerDmitry Stogov <dmitry@php.net>
Tue, 22 Jan 2008 09:29:29 +0000 (09:29 +0000)
40 files changed:
Zend/tests/gc_001.phpt [new file with mode: 0644]
Zend/tests/gc_002.phpt [new file with mode: 0644]
Zend/tests/gc_003.phpt [new file with mode: 0644]
Zend/tests/gc_004.phpt [new file with mode: 0644]
Zend/tests/gc_005.phpt [new file with mode: 0644]
Zend/tests/gc_006.phpt [new file with mode: 0644]
Zend/tests/gc_007.phpt [new file with mode: 0644]
Zend/tests/gc_008.phpt [new file with mode: 0644]
Zend/tests/gc_009.phpt [new file with mode: 0644]
Zend/tests/gc_010.phpt [new file with mode: 0644]
Zend/tests/gc_011.phpt [new file with mode: 0644]
Zend/tests/gc_012.phpt [new file with mode: 0644]
Zend/tests/gc_013.phpt [new file with mode: 0644]
Zend/tests/gc_014.phpt [new file with mode: 0644]
Zend/tests/gc_015.phpt [new file with mode: 0644]
Zend/tests/gc_016.phpt [new file with mode: 0644]
Zend/tests/gc_017.phpt [new file with mode: 0644]
Zend/tests/gc_018.phpt [new file with mode: 0644]
Zend/tests/gc_019.phpt [new file with mode: 0644]
Zend/tests/gc_020.phpt [new file with mode: 0644]
Zend/tests/gc_021.phpt [new file with mode: 0644]
Zend/tests/gc_022.phpt [new file with mode: 0644]
Zend/tests/gc_023.phpt [new file with mode: 0644]
Zend/tests/gc_024.phpt [new file with mode: 0644]
Zend/tests/gc_025.phpt [new file with mode: 0644]
Zend/tests/gc_026.phpt [new file with mode: 0644]
Zend/zend.c
Zend/zend.h
Zend/zend_builtin_functions.c
Zend/zend_execute.c
Zend/zend_execute_API.c
Zend/zend_gc.c [new file with mode: 0644]
Zend/zend_gc.h [new file with mode: 0644]
Zend/zend_objects_API.c
Zend/zend_objects_API.h
Zend/zend_operators.c
Zend/zend_vm_def.h
Zend/zend_vm_execute.h
configure.in
main/main.c

diff --git a/Zend/tests/gc_001.phpt b/Zend/tests/gc_001.phpt
new file mode 100644 (file)
index 0000000..0523c73
--- /dev/null
@@ -0,0 +1,12 @@
+--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)
diff --git a/Zend/tests/gc_002.phpt b/Zend/tests/gc_002.phpt
new file mode 100644 (file)
index 0000000..439520c
--- /dev/null
@@ -0,0 +1,16 @@
+--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
diff --git a/Zend/tests/gc_003.phpt b/Zend/tests/gc_003.phpt
new file mode 100644 (file)
index 0000000..c2df57b
--- /dev/null
@@ -0,0 +1,16 @@
+--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
diff --git a/Zend/tests/gc_004.phpt b/Zend/tests/gc_004.phpt
new file mode 100644 (file)
index 0000000..ce6a94b
--- /dev/null
@@ -0,0 +1,24 @@
+--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
diff --git a/Zend/tests/gc_005.phpt b/Zend/tests/gc_005.phpt
new file mode 100644 (file)
index 0000000..4e0f02d
--- /dev/null
@@ -0,0 +1,31 @@
+--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
+--UEXPECT--
+object(stdClass)#1 (1) {
+  [u"a"]=>
+  object(stdClass)#1 (1) {
+    [u"a"]=>
+    *RECURSION*
+  }
+}
+int(1)
+ok
diff --git a/Zend/tests/gc_006.phpt b/Zend/tests/gc_006.phpt
new file mode 100644 (file)
index 0000000..50c68e3
--- /dev/null
@@ -0,0 +1,44 @@
+--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
+--UEXPECT--
+object(stdClass)#1 (1) {
+  [u"a"]=>
+  array(1) {
+    [0]=>
+    &object(stdClass)#1 (1) {
+      [u"a"]=>
+      array(1) {
+        [0]=>
+        *RECURSION*
+      }
+    }
+  }
+}
+int(2)
+ok
diff --git a/Zend/tests/gc_007.phpt b/Zend/tests/gc_007.phpt
new file mode 100644 (file)
index 0000000..4baa5e6
--- /dev/null
@@ -0,0 +1,26 @@
+--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
diff --git a/Zend/tests/gc_008.phpt b/Zend/tests/gc_008.phpt
new file mode 100644 (file)
index 0000000..efe132a
--- /dev/null
@@ -0,0 +1,35 @@
+--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
+--UEXPECT--
+object(stdClass)#2 (1) {
+  [u"a"]=>
+  object(stdClass)#2 (1) {
+    [u"a"]=>
+    *RECURSION*
+  }
+}
+int(0)
+int(1)
+ok
diff --git a/Zend/tests/gc_009.phpt b/Zend/tests/gc_009.phpt
new file mode 100644 (file)
index 0000000..3b7ed06
--- /dev/null
@@ -0,0 +1,48 @@
+--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
+--UEXPECT--
+object(stdClass)#1 (1) {
+  [u"a"]=>
+  array(1) {
+    [0]=>
+    &object(stdClass)#1 (1) {
+      [u"a"]=>
+      array(1) {
+        [0]=>
+        *RECURSION*
+      }
+    }
+  }
+}
+int(0)
+int(2)
+ok
diff --git a/Zend/tests/gc_010.phpt b/Zend/tests/gc_010.phpt
new file mode 100644 (file)
index 0000000..d39ef8a
--- /dev/null
@@ -0,0 +1,25 @@
+--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
diff --git a/Zend/tests/gc_011.phpt b/Zend/tests/gc_011.phpt
new file mode 100644 (file)
index 0000000..ab6ee41
--- /dev/null
@@ -0,0 +1,39 @@
+--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
+--UEXPECT--
+object(Foo)#1 (1) {
+  [u"a"]=>
+  object(Foo)#1 (1) {
+    [u"a"]=>
+    *RECURSION*
+  }
+}
+__destruct
+int(1)
+ok
diff --git a/Zend/tests/gc_012.phpt b/Zend/tests/gc_012.phpt
new file mode 100644 (file)
index 0000000..eff76ef
--- /dev/null
@@ -0,0 +1,17 @@
+--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
diff --git a/Zend/tests/gc_013.phpt b/Zend/tests/gc_013.phpt
new file mode 100644 (file)
index 0000000..1f75df3
--- /dev/null
@@ -0,0 +1,16 @@
+--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
diff --git a/Zend/tests/gc_014.phpt b/Zend/tests/gc_014.phpt
new file mode 100644 (file)
index 0000000..a9d30f1
--- /dev/null
@@ -0,0 +1,18 @@
+--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
diff --git a/Zend/tests/gc_015.phpt b/Zend/tests/gc_015.phpt
new file mode 100644 (file)
index 0000000..76db51e
--- /dev/null
@@ -0,0 +1,18 @@
+--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
diff --git a/Zend/tests/gc_016.phpt b/Zend/tests/gc_016.phpt
new file mode 100644 (file)
index 0000000..6d6a363
--- /dev/null
@@ -0,0 +1,24 @@
+--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
diff --git a/Zend/tests/gc_017.phpt b/Zend/tests/gc_017.phpt
new file mode 100644 (file)
index 0000000..a0af412
--- /dev/null
@@ -0,0 +1,47 @@
+--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
+--UEXPECTF--
+unicode(1) "%s"
+unicode(1) "%s"
+unicode(1) "%s"
+int(10)
+ok
diff --git a/Zend/tests/gc_018.phpt b/Zend/tests/gc_018.phpt
new file mode 100644 (file)
index 0000000..c202d8c
--- /dev/null
@@ -0,0 +1,13 @@
+--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
diff --git a/Zend/tests/gc_019.phpt b/Zend/tests/gc_019.phpt
new file mode 100644 (file)
index 0000000..12400d2
--- /dev/null
@@ -0,0 +1,14 @@
+--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
diff --git a/Zend/tests/gc_020.phpt b/Zend/tests/gc_020.phpt
new file mode 100644 (file)
index 0000000..76e8aff
--- /dev/null
@@ -0,0 +1,15 @@
+--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
diff --git a/Zend/tests/gc_021.phpt b/Zend/tests/gc_021.phpt
new file mode 100644 (file)
index 0000000..4e6c750
--- /dev/null
@@ -0,0 +1,16 @@
+--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
diff --git a/Zend/tests/gc_022.phpt b/Zend/tests/gc_022.phpt
new file mode 100644 (file)
index 0000000..0418bf9
--- /dev/null
@@ -0,0 +1,15 @@
+--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
diff --git a/Zend/tests/gc_023.phpt b/Zend/tests/gc_023.phpt
new file mode 100644 (file)
index 0000000..3c05801
--- /dev/null
@@ -0,0 +1,27 @@
+--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
diff --git a/Zend/tests/gc_024.phpt b/Zend/tests/gc_024.phpt
new file mode 100644 (file)
index 0000000..ee3e562
--- /dev/null
@@ -0,0 +1,16 @@
+--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;
+//var_dump($a);
+unset($a);
+var_dump(gc_collect_cycles());
+echo "ok\n";
+?>
+--EXPECT--
+int(1)
+ok
diff --git a/Zend/tests/gc_025.phpt b/Zend/tests/gc_025.phpt
new file mode 100644 (file)
index 0000000..5ae6527
--- /dev/null
@@ -0,0 +1,11 @@
+--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
diff --git a/Zend/tests/gc_026.phpt b/Zend/tests/gc_026.phpt
new file mode 100644 (file)
index 0000000..330c4fa
--- /dev/null
@@ -0,0 +1,14 @@
+--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
index 7046f66708ac493df2d6c1db0e59f8fe16f9f423..18ef490c23cb38189e75a13bf46efb927ac15f37 100644 (file)
@@ -172,9 +172,23 @@ void zend_update_converters_error_behavior(TSRMLS_D) /* {{{ */
 }
 /* }}} */
 
+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)
+
        /* Unicode .ini entries */
        STD_ZEND_INI_BOOLEAN("unicode.semantics",                       "0",    ZEND_INI_SYSTEM, OnUpdateBool, unicode, zend_unicode_globals, unicode_globals)
        STD_ZEND_INI_ENTRY("unicode.fallback_encoding",         NULL,   ZEND_INI_ALL, OnUpdateEncoding, fallback_encoding_conv, zend_unicode_globals, unicode_globals)
@@ -1324,6 +1338,7 @@ static void shutdown_unicode_request_globals(TSRMLS_D) /* {{{ */
 
 void zend_activate(TSRMLS_D) /* {{{ */
 {
+       gc_reset(TSRMLS_C);
        init_unicode_request_globals(TSRMLS_C);
        init_unicode_strings();
        init_compiler(TSRMLS_C);
@@ -1377,6 +1392,12 @@ void zend_deactivate(TSRMLS_D) /* {{{ */
 
        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();
index f06959047d56a552bce75cf09c34b6b4596db54b..2d3be029aef18d05a947467e5d681a645ec97575 100644 (file)
@@ -762,6 +762,7 @@ END_EXTERN_C()
 
 #define ZEND_INTERNAL_ENCODING "UTF-16"
 
+#include "zend_gc.h"
 #include "zend_operators.h"
 #include "zend_variables.h"
 
index 6bbff917f4c2632004cfe9b9cb0f1b9de006f0dd..a770961b6e09b81f7da0efbf6bdcfeeaf29a1f5e 100644 (file)
@@ -85,6 +85,10 @@ static ZEND_FUNCTION(zend_test_func);
 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"
@@ -148,6 +152,10 @@ static const zend_function_entry builtin_functions[] = { /* {{{ */
        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 }
 };
 /* }}} */
@@ -2326,6 +2334,39 @@ ZEND_FUNCTION(get_extension_funcs)
 }
 /* }}} */
 
+/* {{{ 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);
+}
+/* }}} */
+
 /*
  * Local variables:
  * tab-width: 4
index d5baedd1865fe5beef7434b9ffb3e08ad91a3a5b..c1e2022f78e3bd918d3f82ffb0bd51ff6d480029 100644 (file)
@@ -65,7 +65,7 @@ static void zend_extension_fcall_end_handler(zend_extension *extension, zend_op_
 
 #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);
@@ -77,6 +77,7 @@ static inline void zend_pzval_unlock_func(zval *z, zend_free_op *should_free, in
                if (unref && Z_ISREF_P(z) && Z_REFCOUNT_P(z) == 1) {
                        Z_UNSET_ISREF_P(z);
                }
+               GC_ZVAL_CHECK_POSSIBLE_ROOT(z);
        }
 }
 /* }}} */
@@ -84,14 +85,15 @@ static inline void zend_pzval_unlock_func(zval *z, zend_free_op *should_free, in
 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))
@@ -792,6 +794,7 @@ static inline zval* zend_assign_to_variable(zval **variable_ptr_ptr, zval *value
                                } 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;
index acf0e36afb71847760fa7972c2f3caba2d89e94e..aa83e59feaf511d27c85154c41c38c6a7240731d 100644 (file)
@@ -445,10 +445,16 @@ ZEND_API void _zval_ptr_dtor(zval **zval_ptr ZEND_FILE_LINE_DC) /* {{{ */
 #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) {
-               Z_UNSET_ISREF_PP(zval_ptr);
+       } else {
+               TSRMLS_FETCH();
+
+               if (Z_REFCOUNT_PP(zval_ptr) == 1) {
+                       Z_UNSET_ISREF_PP(zval_ptr);
+               }
+               GC_ZVAL_CHECK_POSSIBLE_ROOT(*zval_ptr);
        }
 }
 /* }}} */
diff --git a/Zend/zend_gc.c b/Zend/zend_gc.c
new file mode 100644 (file)
index 0000000..48afebd
--- /dev/null
@@ -0,0 +1,516 @@
+/*
+   +----------------------------------------------------------------------+
+   | 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:
+ */
diff --git a/Zend/zend_gc.h b/Zend/zend_gc.h
new file mode 100644 (file)
index 0000000..d6050b0
--- /dev/null
@@ -0,0 +1,245 @@
+/*
+   +----------------------------------------------------------------------+
+   | 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:
+ */
index dcb88b2c72a4e139764916f56abb84653cfe87a4..cd17ec2b1d0a80b3222f5f07e4fe03749ed1325f 100644 (file)
@@ -88,6 +88,8 @@ ZEND_API void zend_objects_store_free_object_storage(zend_objects_store *objects
                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);
@@ -119,6 +121,7 @@ ZEND_API zend_object_handle zend_objects_store_put(void *object, zend_objects_st
        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;
@@ -183,6 +186,8 @@ ZEND_API void zend_objects_store_del_ref(zval *zobject TSRMLS_DC) /* {{{ */
        Z_ADDREF_P(zobject);
        zend_objects_store_del_ref_by_handle(handle TSRMLS_CC);
        Z_DELREF_P(zobject);
+
+       GC_ZOBJ_CHECK_POSSIBLE_ROOT(zobject);
 }
 /* }}} */
 
@@ -218,6 +223,7 @@ ZEND_API void zend_objects_store_del_ref_by_handle(zend_object_handle handle TSR
                                }
                        }
                        if (obj->refcount == 1) {
+                               GC_REMOVE_ZOBJ_FROM_BUFFER(obj);
                                if (obj->free_storage) {
                                        zend_try {
                                                obj->free_storage(obj->object TSRMLS_CC);
index 5e6e08da68fee3391dcdf85d830ec98a4207e978..438f305cb7538871d5527a76b134a216a5070e9e 100644 (file)
@@ -38,6 +38,7 @@ typedef struct _zend_object_store_bucket {
                        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;
index 6c12ec55c28f14ac19a271343db5321f5e06cf87..4fdff9f928b26e3c9a4d057965def98ae3202e66 100644 (file)
@@ -1920,6 +1920,7 @@ ZEND_API int numeric_compare_function(zval *result, zval *op1, zval *op2 TSRMLS_
 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 {
index de8537e83100b34e707af0bbeafb87a8138dc1ad..6577c30b620470c57a06e9e58354d8cb3b84b8fc 100644 (file)
@@ -349,6 +349,7 @@ ZEND_VM_HELPER_EX(zend_binary_assign_op_obj_helper, VAR|UNUSED|CV, CONST|TMP|VAR
                                        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);
                                        }
@@ -587,6 +588,7 @@ ZEND_VM_HELPER_EX(zend_pre_incdec_property_helper, VAR|UNUSED|CV, CONST|TMP|VAR|
                                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);
                                }
@@ -677,6 +679,7 @@ ZEND_VM_HELPER_EX(zend_post_incdec_property_helper, VAR|UNUSED|CV, CONST|TMP|VAR
                                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);
                                }
@@ -1262,6 +1265,7 @@ ZEND_VM_HELPER_EX(zend_fetch_property_address_read_helper, VAR|UNUSED|CV, CONST|
 
                if (RETURN_VALUE_UNUSED(&opline->result)) {
                        if (Z_REFCOUNT_P(retval) == 0) {
+                               GC_REMOVE_ZVAL_FROM_BUFFER(retval);
                                zval_dtor(retval);
                                FREE_ZVAL(retval);
                        }
index 4f0390044fd4b7adf27ddca00e691e2d944eeace..eb228ae6dd11a46388e7277ad9d078bc1ce1d468 100644 (file)
@@ -9158,6 +9158,7 @@ static int zend_binary_assign_op_obj_helper_SPEC_VAR_CONST(int (*binary_op)(zval
                                        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);
                                        }
@@ -9395,6 +9396,7 @@ static int zend_pre_incdec_property_helper_SPEC_VAR_CONST(incdec_t incdec_op, ZE
                                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);
                                }
@@ -9485,6 +9487,7 @@ static int zend_post_incdec_property_helper_SPEC_VAR_CONST(incdec_t incdec_op, 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);
                                }
@@ -9718,6 +9721,7 @@ static int zend_fetch_property_address_read_helper_SPEC_VAR_CONST(int type, ZEND
 
                if (RETURN_VALUE_UNUSED(&opline->result)) {
                        if (Z_REFCOUNT_P(retval) == 0) {
+                               GC_REMOVE_ZVAL_FROM_BUFFER(retval);
                                zval_dtor(retval);
                                FREE_ZVAL(retval);
                        }
@@ -10980,6 +10984,7 @@ static int zend_binary_assign_op_obj_helper_SPEC_VAR_TMP(int (*binary_op)(zval *
                                        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);
                                        }
@@ -11218,6 +11223,7 @@ static int zend_pre_incdec_property_helper_SPEC_VAR_TMP(incdec_t incdec_op, ZEND
                                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);
                                }
@@ -11308,6 +11314,7 @@ static int zend_post_incdec_property_helper_SPEC_VAR_TMP(incdec_t incdec_op, ZEN
                                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);
                                }
@@ -11541,6 +11548,7 @@ static int zend_fetch_property_address_read_helper_SPEC_VAR_TMP(int type, ZEND_O
 
                if (RETURN_VALUE_UNUSED(&opline->result)) {
                        if (Z_REFCOUNT_P(retval) == 0) {
+                               GC_REMOVE_ZVAL_FROM_BUFFER(retval);
                                zval_dtor(retval);
                                FREE_ZVAL(retval);
                        }
@@ -12708,6 +12716,7 @@ static int zend_binary_assign_op_obj_helper_SPEC_VAR_VAR(int (*binary_op)(zval *
                                        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);
                                        }
@@ -12946,6 +12955,7 @@ static int zend_pre_incdec_property_helper_SPEC_VAR_VAR(incdec_t incdec_op, ZEND
                                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);
                                }
@@ -13036,6 +13046,7 @@ static int zend_post_incdec_property_helper_SPEC_VAR_VAR(incdec_t incdec_op, ZEN
                                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);
                                }
@@ -13269,6 +13280,7 @@ static int zend_fetch_property_address_read_helper_SPEC_VAR_VAR(int type, ZEND_O
 
                if (RETURN_VALUE_UNUSED(&opline->result)) {
                        if (Z_REFCOUNT_P(retval) == 0) {
+                               GC_REMOVE_ZVAL_FROM_BUFFER(retval);
                                zval_dtor(retval);
                                FREE_ZVAL(retval);
                        }
@@ -14239,6 +14251,7 @@ static int zend_binary_assign_op_obj_helper_SPEC_VAR_UNUSED(int (*binary_op)(zva
                                        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);
                                        }
@@ -15043,6 +15056,7 @@ static int zend_binary_assign_op_obj_helper_SPEC_VAR_CV(int (*binary_op)(zval *r
                                        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);
                                        }
@@ -15280,6 +15294,7 @@ static int zend_pre_incdec_property_helper_SPEC_VAR_CV(incdec_t incdec_op, ZEND_
                                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);
                                }
@@ -15370,6 +15385,7 @@ static int zend_post_incdec_property_helper_SPEC_VAR_CV(incdec_t incdec_op, ZEND
                                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);
                                }
@@ -15603,6 +15619,7 @@ static int zend_fetch_property_address_read_helper_SPEC_VAR_CV(int type, ZEND_OP
 
                if (RETURN_VALUE_UNUSED(&opline->result)) {
                        if (Z_REFCOUNT_P(retval) == 0) {
+                               GC_REMOVE_ZVAL_FROM_BUFFER(retval);
                                zval_dtor(retval);
                                FREE_ZVAL(retval);
                        }
@@ -16650,6 +16667,7 @@ static int zend_binary_assign_op_obj_helper_SPEC_UNUSED_CONST(int (*binary_op)(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);
                                        }
@@ -16886,6 +16904,7 @@ static int zend_pre_incdec_property_helper_SPEC_UNUSED_CONST(incdec_t incdec_op,
                                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);
                                }
@@ -16976,6 +16995,7 @@ static int zend_post_incdec_property_helper_SPEC_UNUSED_CONST(incdec_t incdec_op
                                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);
                                }
@@ -17055,6 +17075,7 @@ static int zend_fetch_property_address_read_helper_SPEC_UNUSED_CONST(int type, 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);
                        }
@@ -17768,6 +17789,7 @@ static int zend_binary_assign_op_obj_helper_SPEC_UNUSED_TMP(int (*binary_op)(zva
                                        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);
                                        }
@@ -18005,6 +18027,7 @@ static int zend_pre_incdec_property_helper_SPEC_UNUSED_TMP(incdec_t incdec_op, 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);
                                }
@@ -18095,6 +18118,7 @@ static int zend_post_incdec_property_helper_SPEC_UNUSED_TMP(incdec_t incdec_op,
                                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);
                                }
@@ -18174,6 +18198,7 @@ static int zend_fetch_property_address_read_helper_SPEC_UNUSED_TMP(int type, ZEN
 
                if (RETURN_VALUE_UNUSED(&opline->result)) {
                        if (Z_REFCOUNT_P(retval) == 0) {
+                               GC_REMOVE_ZVAL_FROM_BUFFER(retval);
                                zval_dtor(retval);
                                FREE_ZVAL(retval);
                        }
@@ -18816,6 +18841,7 @@ static int zend_binary_assign_op_obj_helper_SPEC_UNUSED_VAR(int (*binary_op)(zva
                                        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);
                                        }
@@ -19053,6 +19079,7 @@ static int zend_pre_incdec_property_helper_SPEC_UNUSED_VAR(incdec_t incdec_op, 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);
                                }
@@ -19143,6 +19170,7 @@ static int zend_post_incdec_property_helper_SPEC_UNUSED_VAR(incdec_t incdec_op,
                                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);
                                }
@@ -19222,6 +19250,7 @@ static int zend_fetch_property_address_read_helper_SPEC_UNUSED_VAR(int type, ZEN
 
                if (RETURN_VALUE_UNUSED(&opline->result)) {
                        if (Z_REFCOUNT_P(retval) == 0) {
+                               GC_REMOVE_ZVAL_FROM_BUFFER(retval);
                                zval_dtor(retval);
                                FREE_ZVAL(retval);
                        }
@@ -19864,6 +19893,7 @@ static int zend_binary_assign_op_obj_helper_SPEC_UNUSED_UNUSED(int (*binary_op)(
                                        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);
                                        }
@@ -20130,6 +20160,7 @@ static int zend_binary_assign_op_obj_helper_SPEC_UNUSED_CV(int (*binary_op)(zval
                                        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);
                                        }
@@ -20366,6 +20397,7 @@ static int zend_pre_incdec_property_helper_SPEC_UNUSED_CV(incdec_t incdec_op, ZE
                                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);
                                }
@@ -20456,6 +20488,7 @@ static int zend_post_incdec_property_helper_SPEC_UNUSED_CV(incdec_t incdec_op, 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);
                                }
@@ -20535,6 +20568,7 @@ static int zend_fetch_property_address_read_helper_SPEC_UNUSED_CV(int type, ZEND
 
                if (RETURN_VALUE_UNUSED(&opline->result)) {
                        if (Z_REFCOUNT_P(retval) == 0) {
+                               GC_REMOVE_ZVAL_FROM_BUFFER(retval);
                                zval_dtor(retval);
                                FREE_ZVAL(retval);
                        }
@@ -22749,6 +22783,7 @@ static int zend_binary_assign_op_obj_helper_SPEC_CV_CONST(int (*binary_op)(zval
                                        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);
                                        }
@@ -22985,6 +23020,7 @@ static int zend_pre_incdec_property_helper_SPEC_CV_CONST(incdec_t incdec_op, ZEN
                                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);
                                }
@@ -23075,6 +23111,7 @@ static int zend_post_incdec_property_helper_SPEC_CV_CONST(incdec_t incdec_op, ZE
                                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);
                                }
@@ -23307,6 +23344,7 @@ static int zend_fetch_property_address_read_helper_SPEC_CV_CONST(int type, ZEND_
 
                if (RETURN_VALUE_UNUSED(&opline->result)) {
                        if (Z_REFCOUNT_P(retval) == 0) {
+                               GC_REMOVE_ZVAL_FROM_BUFFER(retval);
                                zval_dtor(retval);
                                FREE_ZVAL(retval);
                        }
@@ -24397,6 +24435,7 @@ static int zend_binary_assign_op_obj_helper_SPEC_CV_TMP(int (*binary_op)(zval *r
                                        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);
                                        }
@@ -24634,6 +24673,7 @@ static int zend_pre_incdec_property_helper_SPEC_CV_TMP(incdec_t incdec_op, ZEND_
                                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);
                                }
@@ -24724,6 +24764,7 @@ static int zend_post_incdec_property_helper_SPEC_CV_TMP(incdec_t incdec_op, ZEND
                                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);
                                }
@@ -24956,6 +24997,7 @@ static int zend_fetch_property_address_read_helper_SPEC_CV_TMP(int type, ZEND_OP
 
                if (RETURN_VALUE_UNUSED(&opline->result)) {
                        if (Z_REFCOUNT_P(retval) == 0) {
+                               GC_REMOVE_ZVAL_FROM_BUFFER(retval);
                                zval_dtor(retval);
                                FREE_ZVAL(retval);
                        }
@@ -26023,6 +26065,7 @@ static int zend_binary_assign_op_obj_helper_SPEC_CV_VAR(int (*binary_op)(zval *r
                                        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);
                                        }
@@ -26260,6 +26303,7 @@ static int zend_pre_incdec_property_helper_SPEC_CV_VAR(incdec_t incdec_op, ZEND_
                                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);
                                }
@@ -26350,6 +26394,7 @@ static int zend_post_incdec_property_helper_SPEC_CV_VAR(incdec_t incdec_op, ZEND
                                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);
                                }
@@ -26582,6 +26627,7 @@ static int zend_fetch_property_address_read_helper_SPEC_CV_VAR(int type, ZEND_OP
 
                if (RETURN_VALUE_UNUSED(&opline->result)) {
                        if (Z_REFCOUNT_P(retval) == 0) {
+                               GC_REMOVE_ZVAL_FROM_BUFFER(retval);
                                zval_dtor(retval);
                                FREE_ZVAL(retval);
                        }
@@ -27451,6 +27497,7 @@ static int zend_binary_assign_op_obj_helper_SPEC_CV_UNUSED(int (*binary_op)(zval
                                        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);
                                        }
@@ -28163,6 +28210,7 @@ static int zend_binary_assign_op_obj_helper_SPEC_CV_CV(int (*binary_op)(zval *re
                                        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);
                                        }
@@ -28399,6 +28447,7 @@ static int zend_pre_incdec_property_helper_SPEC_CV_CV(incdec_t incdec_op, ZEND_O
                                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);
                                }
@@ -28489,6 +28538,7 @@ static int zend_post_incdec_property_helper_SPEC_CV_CV(incdec_t incdec_op, ZEND_
                                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);
                                }
@@ -28721,6 +28771,7 @@ static int zend_fetch_property_address_read_helper_SPEC_CV_CV(int type, ZEND_OPC
 
                if (RETURN_VALUE_UNUSED(&opline->result)) {
                        if (Z_REFCOUNT_P(retval) == 0) {
+                               GC_REMOVE_ZVAL_FROM_BUFFER(retval);
                                zval_dtor(retval);
                                FREE_ZVAL(retval);
                        }
index ddfede7fbcf81427fda923feba399005b943b9f1..e9bd1edbc37c1a456c188cb162c8dc24d2b2a169 100644 (file)
@@ -1235,7 +1235,8 @@ PHP_ADD_SOURCES(Zend, \
     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_ts_hash.c zend_stream.c \
-    zend_iterators.c zend_interfaces.c zend_exceptions.c zend_strtod.c zend_strtol.c)
+    zend_iterators.c zend_interfaces.c zend_exceptions.c zend_strtod.c \
+    zend_strtol.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 \
index af9fbf51463924bd142fe8243725748994415ede..99f951e7c9177cfc780b4343bb77c1a576a698ef 100644 (file)
@@ -1822,6 +1822,8 @@ int php_module_startup(sapi_module_struct *sf, zend_module_entry *additional_mod
        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;
 
@@ -2070,6 +2072,7 @@ void php_module_shutdown(TSRMLS_D)
 
 #ifndef ZTS
        core_globals_dtor(&core_globals TSRMLS_CC);
+       gc_globals_dtor(TSRMLS_C);
 #else
        ts_free_id(core_globals_id);
 #endif