Re: [PHP-DEV] Garbage collector patch

12 views
Skip to first unread message

Dmitry Stogov

unread,
Dec 3, 2007, 7:16:20 PM12/3/07
to
--------------000100010008040602010309
Content-Type: text/plain; charset=ISO-8859-1; format=flowed
Content-Transfer-Encoding: 7bit

attached.

Dmitry.

Andi Gutmans wrote:
> That'd be great.
> Dmitry, David, can you please send the updated patch to the list?
>
> Andi
>
>> -----Original Message-----
>> From: Markus Fischer [mailto:mar...@fischer.name]
>> Sent: Monday, December 03, 2007 1:31 PM
>> To: Daniel Brown
>> Cc: Andi Gutmans; inte...@lists.php.net
>> Subject: Re: [PHP-DEV] Garbage collector patch
>>
>> -----BEGIN PGP SIGNED MESSAGE-----
>> Hash: SHA1
>>
>> Hi,
>>
>> Daniel Brown wrote:
>>> I don't know about how it worked for anyone else, but the tables
>>> didn't display properly on Gmail, so I had a hard time keeping up
>> with
>>> the performance differences. If you have this in a separate file,
>>> could you send it to me as an attachment?
>> Same problem, all in one row, to "table" to read. We're having
>> non-apache application which run up to one hour to do their task and I
>> think our QA can test the new GC there (and memory consumption is an
>> issue there definitely).
>>
>> thanks
>> -----BEGIN PGP SIGNATURE-----
>> Version: GnuPG v1.4.6 (MingW32)
>> Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org
>>
>> iD8DBQFHVHWh1nS0RcInK9ARAmPlAJ9XnOzFmLSl8qDxY5bLBfJBcmgqRACfZnsn
>> R3cFSHfkMpoffq+f5vMxI3g=
>> =OkMW
>> -----END PGP SIGNATURE-----
>

--------------000100010008040602010309
Content-Type: text/plain;
name="gc-5.3.diff.txt"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
filename="gc-5.3.diff.txt"

Index: configure.in
===================================================================
RCS file: /repository/php-src/configure.in,v
retrieving revision 1.579.2.52.2.77.2.6
diff -u -p -d -r1.579.2.52.2.77.2.6 configure.in
--- configure.in 6 Nov 2007 11:50:51 -0000 1.579.2.52.2.77.2.6
+++ configure.in 23 Nov 2007 12:32:59 -0000
@@ -1326,7 +1326,7 @@ 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_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 \
Index: Zend/zend.c
===================================================================
RCS file: /repository/ZendEngine2/zend.c,v
retrieving revision 1.308.2.12.2.35.2.3
diff -u -p -d -r1.308.2.12.2.35.2.3 zend.c
--- Zend/zend.c 2 Nov 2007 19:40:37 -0000 1.308.2.12.2.35.2.3
+++ Zend/zend.c 23 Nov 2007 12:33:00 -0000
@@ -74,8 +74,21 @@ static ZEND_INI_MH(OnUpdateErrorReportin
}
/* }}} */

+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)
@@ -822,6 +835,7 @@ ZEND_API char *get_zend_version(void) /*

void zend_activate(TSRMLS_D) /* {{{ */
{
+ gc_reset(TSRMLS_C);
init_compiler(TSRMLS_C);
init_executor(TSRMLS_C);
startup_scanner(TSRMLS_C);
@@ -871,6 +885,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: Zend/zend.h
===================================================================
RCS file: /repository/ZendEngine2/zend.h,v
retrieving revision 1.293.2.11.2.9.2.12
diff -u -p -d -r1.293.2.11.2.9.2.12 zend.h
--- Zend/zend.h 22 Nov 2007 13:27:11 -0000 1.293.2.11.2.9.2.12
+++ Zend/zend.h 23 Nov 2007 12:33:00 -0000
@@ -706,6 +706,7 @@ END_EXTERN_C()

#define ZEND_MAX_RESERVED_RESOURCES 4

+#include "zend_gc.h"
#include "zend_operators.h"
#include "zend_variables.h"

Index: Zend/zend_builtin_functions.c
===================================================================
RCS file: /repository/ZendEngine2/zend_builtin_functions.c,v
retrieving revision 1.277.2.12.2.25.2.6
diff -u -p -d -r1.277.2.12.2.25.2.6 zend_builtin_functions.c
--- Zend/zend_builtin_functions.c 22 Nov 2007 13:27:11 -0000 1.277.2.12.2.25.2.6
+++ Zend/zend_builtin_functions.c 23 Nov 2007 12:33:00 -0000
@@ -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
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 }
};

@@ -166,6 +174,38 @@ ZEND_FUNCTION(zend_version)
}
/* }}} */

+/* {{{ 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 */
Index: Zend/zend_execute.c
===================================================================
RCS file: /repository/ZendEngine2/zend_execute.c,v
retrieving revision 1.716.2.12.2.24.2.8
diff -u -p -d -r1.716.2.12.2.24.2.8 zend_execute.c
--- Zend/zend_execute.c 22 Nov 2007 13:27:11 -0000 1.716.2.12.2.24.2.8
+++ Zend/zend_execute.c 23 Nov 2007 12:33:00 -0000
@@ -64,7 +64,7 @@ static void zend_extension_fcall_end_han

#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);
@@ -76,6 +76,7 @@ static inline void zend_pzval_unlock_fun
if (unref && Z_ISREF_P(z) && Z_REFCOUNT_P(z) == 1) {
Z_UNSET_ISREF_P(z);
}
+ GC_ZVAL_CHECK_POSSIBLE_ROOT(z);
}
}

@@ -87,8 +88,8 @@ static inline void zend_pzval_unlock_fre
}
}

-#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))
Index: Zend/zend_execute_API.c
===================================================================
RCS file: /repository/ZendEngine2/zend_execute_API.c,v
retrieving revision 1.331.2.20.2.24.2.13
diff -u -p -d -r1.331.2.20.2.24.2.13 zend_execute_API.c
--- Zend/zend_execute_API.c 22 Nov 2007 13:27:11 -0000 1.331.2.20.2.24.2.13
+++ Zend/zend_execute_API.c 23 Nov 2007 12:33:00 -0000
@@ -420,15 +420,19 @@ ZEND_API void _zval_ptr_dtor(zval **zval
if (Z_REFCOUNT_PP(zval_ptr) == 0) {
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);
}
}
/* }}} */
Index: Zend/zend_gc.c
===================================================================
RCS file: Zend/zend_gc.c
diff -N Zend/zend_gc.c
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ Zend/zend_gc.c 23 Nov 2007 12:33:00 -0000
@@ -0,0 +1,515 @@
+/*
+ +----------------------------------------------------------------------+
+ | 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 |
+ | lic...@zend.com so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: David Wang <plane...@gmail.com> |
+ | Dmitry Stogov <dmi...@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) {
+ 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:
+ */
Index: Zend/zend_gc.h
===================================================================
RCS file: Zend/zend_gc.h
diff -N Zend/zend_gc.h
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ Zend/zend_gc.h 23 Nov 2007 12:33:00 -0000
@@ -0,0 +1,240 @@
+/*
+ +----------------------------------------------------------------------+
+ | 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 |
+ | lic...@zend.com so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: David Wang <plane...@gmail.com> |
+ | Dmitry Stogov <dmi...@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)); \
+ } \
+ } 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);
+ }
+}
+
+/* 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) \
+
+#endif /* ZEND_GC_H */
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * indent-tabs-mode: t
+ * End:
+ */
Index: Zend/zend_objects_API.c
===================================================================
RCS file: /repository/ZendEngine2/zend_objects_API.c,v
retrieving revision 1.47.2.6.2.6.2.1
diff -u -p -d -r1.47.2.6.2.6.2.1 zend_objects_API.c
--- Zend/zend_objects_API.c 7 Oct 2007 05:22:03 -0000 1.47.2.6.2.6.2.1
+++ Zend/zend_objects_API.c 23 Nov 2007 12:33:00 -0000
@@ -84,6 +84,8 @@ ZEND_API void zend_objects_store_free_ob
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);
@@ -116,6 +118,7 @@ ZEND_API zend_object_handle zend_objects
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;
@@ -167,6 +170,8 @@ ZEND_API void zend_objects_store_del_ref
Z_ADDREF_P(zobject);
zend_objects_store_del_ref_by_handle(handle TSRMLS_CC);
Z_DELREF_P(zobject);
+
+ GC_ZOBJ_CHECK_POSSIBLE_ROOT(zobject);
}

/*
@@ -201,6 +206,7 @@ ZEND_API void zend_objects_store_del_ref
}
}
if (obj->refcount == 1) {
+ GC_REMOVE_ZOBJ_FROM_BUFFER(obj);
if (obj->free_storage) {
zend_try {
obj->free_storage(obj->object TSRMLS_CC);
Index: Zend/zend_objects_API.h
===================================================================
RCS file: /repository/ZendEngine2/zend_objects_API.h,v
retrieving revision 1.20.2.1.2.4
diff -u -p -d -r1.20.2.1.2.4 zend_objects_API.h
--- Zend/zend_objects_API.h 21 Jul 2007 00:35:14 -0000 1.20.2.1.2.4
+++ Zend/zend_objects_API.h 23 Nov 2007 12:33:00 -0000
@@ -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: Zend/tests/gc_001.phpt
===================================================================
RCS file: Zend/tests/gc_001.phpt
diff -N Zend/tests/gc_001.phpt
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ Zend/tests/gc_001.phpt 23 Nov 2007 12:33:00 -0000
@@ -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)
Index: Zend/tests/gc_002.phpt
===================================================================
RCS file: Zend/tests/gc_002.phpt
diff -N Zend/tests/gc_002.phpt
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ Zend/tests/gc_002.phpt 23 Nov 2007 12:33:00 -0000
@@ -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
Index: Zend/tests/gc_003.phpt
===================================================================
RCS file: Zend/tests/gc_003.phpt
diff -N Zend/tests/gc_003.phpt
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ Zend/tests/gc_003.phpt 23 Nov 2007 12:33:00 -0000
@@ -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
Index: Zend/tests/gc_004.phpt
===================================================================
RCS file: Zend/tests/gc_004.phpt
diff -N Zend/tests/gc_004.phpt
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ Zend/tests/gc_004.phpt 23 Nov 2007 12:33:00 -0000
@@ -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
Index: Zend/tests/gc_005.phpt
===================================================================
RCS file: Zend/tests/gc_005.phpt
diff -N Zend/tests/gc_005.phpt
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ Zend/tests/gc_005.phpt 23 Nov 2007 12:33:00 -0000
@@ -0,0 +1,21 @@
+--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
Index: Zend/tests/gc_006.phpt
===================================================================
RCS file: Zend/tests/gc_006.phpt
diff -N Zend/tests/gc_006.phpt
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ Zend/tests/gc_006.phpt 23 Nov 2007 12:33:00 -0000
@@ -0,0 +1,28 @@
+--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
Index: Zend/tests/gc_007.phpt
===================================================================
RCS file: Zend/tests/gc_007.phpt
diff -N Zend/tests/gc_007.phpt
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ Zend/tests/gc_007.phpt 23 Nov 2007 12:33:00 -0000
@@ -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
Index: Zend/tests/gc_008.phpt
===================================================================
RCS file: Zend/tests/gc_008.phpt
diff -N Zend/tests/gc_008.phpt
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ Zend/tests/gc_008.phpt 23 Nov 2007 12:33:00 -0000
@@ -0,0 +1,24 @@
+--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
Index: Zend/tests/gc_009.phpt
===================================================================
RCS file: Zend/tests/gc_009.phpt
diff -N Zend/tests/gc_009.phpt
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ Zend/tests/gc_009.phpt 23 Nov 2007 12:33:00 -0000
@@ -0,0 +1,31 @@
+--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
Index: Zend/tests/gc_010.phpt
===================================================================
RCS file: Zend/tests/gc_010.phpt
diff -N Zend/tests/gc_010.phpt
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ Zend/tests/gc_010.phpt 23 Nov 2007 12:33:00 -0000
@@ -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
Index: Zend/tests/gc_011.phpt
===================================================================
RCS file: Zend/tests/gc_011.phpt
diff -N Zend/tests/gc_011.phpt
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ Zend/tests/gc_011.phpt 23 Nov 2007 12:33:00 -0000
@@ -0,0 +1,28 @@
+--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
Index: Zend/tests/gc_012.phpt
===================================================================
RCS file: Zend/tests/gc_012.phpt
diff -N Zend/tests/gc_012.phpt
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ Zend/tests/gc_012.phpt 23 Nov 2007 12:33:00 -0000
@@ -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
Index: Zend/tests/gc_013.phpt
===================================================================
RCS file: Zend/tests/gc_013.phpt
diff -N Zend/tests/gc_013.phpt
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ Zend/tests/gc_013.phpt 23 Nov 2007 12:33:00 -0000
@@ -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
Index: Zend/tests/gc_014.phpt
===================================================================
RCS file: Zend/tests/gc_014.phpt
diff -N Zend/tests/gc_014.phpt
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ Zend/tests/gc_014.phpt 23 Nov 2007 12:33:00 -0000
@@ -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
Index: Zend/tests/gc_015.phpt
===================================================================
RCS file: Zend/tests/gc_015.phpt
diff -N Zend/tests/gc_015.phpt
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ Zend/tests/gc_015.phpt 23 Nov 2007 12:33:00 -0000
@@ -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
Index: Zend/tests/gc_016.phpt
===================================================================
RCS file: Zend/tests/gc_016.phpt
diff -N Zend/tests/gc_016.phpt
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ Zend/tests/gc_016.phpt 23 Nov 2007 12:33:00 -0000
@@ -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
Index: Zend/tests/gc_017.phpt
===================================================================
RCS file: Zend/tests/gc_017.phpt
diff -N Zend/tests/gc_017.phpt
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ Zend/tests/gc_017.phpt 23 Nov 2007 12:33:00 -0000
@@ -0,0 +1,41 @@
+--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
Index: Zend/tests/gc_018.phpt
===================================================================
RCS file: Zend/tests/gc_018.phpt
diff -N Zend/tests/gc_018.phpt
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ Zend/tests/gc_018.phpt 23 Nov 2007 12:33:00 -0000
@@ -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
Index: Zend/tests/gc_019.phpt
===================================================================
RCS file: Zend/tests/gc_019.phpt
diff -N Zend/tests/gc_019.phpt
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ Zend/tests/gc_019.phpt 23 Nov 2007 12:33:00 -0000
@@ -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
Index: Zend/tests/gc_020.phpt
===================================================================
RCS file: Zend/tests/gc_020.phpt
diff -N Zend/tests/gc_020.phpt
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ Zend/tests/gc_020.phpt 23 Nov 2007 12:33:00 -0000
@@ -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
Index: Zend/tests/gc_021.phpt
===================================================================
RCS file: Zend/tests/gc_021.phpt
diff -N Zend/tests/gc_021.phpt
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ Zend/tests/gc_021.phpt 23 Nov 2007 12:33:00 -0000
@@ -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
Index: Zend/tests/gc_022.phpt
===================================================================
RCS file: Zend/tests/gc_022.phpt
diff -N Zend/tests/gc_022.phpt
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ Zend/tests/gc_022.phpt 23 Nov 2007 12:33:00 -0000
@@ -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
Index: Zend/tests/gc_023.phpt
===================================================================
RCS file: Zend/tests/gc_023.phpt
diff -N Zend/tests/gc_023.phpt
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ Zend/tests/gc_023.phpt 23 Nov 2007 12:33:00 -0000
@@ -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
Index: Zend/tests/gc_024.phpt
===================================================================
RCS file: Zend/tests/gc_024.phpt
diff -N Zend/tests/gc_024.phpt
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ Zend/tests/gc_024.phpt 23 Nov 2007 12:33:00 -0000
@@ -0,0 +1,15 @@
+--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
Index: Zend/tests/gc_025.phpt
===================================================================
RCS file: Zend/tests/gc_025.phpt
diff -N Zend/tests/gc_025.phpt
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ Zend/tests/gc_025.phpt 23 Nov 2007 12:33:00 -0000
@@ -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
Index: Zend/tests/gc_026.phpt
===================================================================
RCS file: Zend/tests/gc_026.phpt
diff -N Zend/tests/gc_026.phpt
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ Zend/tests/gc_026.phpt 23 Nov 2007 12:33:00 -0000
@@ -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: main/main.c
===================================================================
RCS file: /repository/php-src/main/main.c,v
retrieving revision 1.640.2.23.2.57.2.4
diff -u -p -d -r1.640.2.23.2.57.2.4 main.c
--- main/main.c 22 Nov 2007 13:27:13 -0000 1.640.2.23.2.57.2.4
+++ main/main.c 23 Nov 2007 12:33:01 -0000
@@ -1701,6 +1701,8 @@ int php_module_startup(sapi_module_struc
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;

@@ -1902,6 +1904,7 @@ void php_module_shutdown(TSRMLS_D)
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);

--------------000100010008040602010309
Content-Type: text/plain;
name="gc-6.diff.txt"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
filename="gc-6.diff.txt"

Index: configure.in
===================================================================
RCS file: /repository/php-src/configure.in,v
retrieving revision 1.642
diff -u -p -d -r1.642 configure.in
--- configure.in 5 Oct 2007 14:49:40 -0000 1.642
+++ configure.in 23 Nov 2007 13:04:22 -0000
@@ -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: Zend/zend.c
===================================================================
RCS file: /repository/ZendEngine2/zend.c,v
retrieving revision 1.402
diff -u -p -d -r1.402 zend.c
--- Zend/zend.c 2 Nov 2007 15:59:58 -0000 1.402
+++ Zend/zend.c 23 Nov 2007 13:04:22 -0000
@@ -172,9 +172,23 @@ void zend_update_converters_error_behavi
}
/* }}} */

+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_glo

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: Zend/zend.h
===================================================================
RCS file: /repository/ZendEngine2/zend.h,v
retrieving revision 1.345
diff -u -p -d -r1.345 zend.h
--- Zend/zend.h 22 Nov 2007 13:33:53 -0000 1.345
+++ Zend/zend.h 23 Nov 2007 13:04:22 -0000
@@ -759,6 +759,7 @@ END_EXTERN_C()

#define ZEND_INTERNAL_ENCODING "UTF-16"

+#include "zend_gc.h"
#include "zend_operators.h"
#include "zend_variables.h"

Index: Zend/zend_builtin_functions.c
===================================================================
RCS file: /repository/ZendEngine2/zend_builtin_functions.c,v
retrieving revision 1.357
diff -u -p -d -r1.357 zend_builtin_functions.c
--- Zend/zend_builtin_functions.c 7 Oct 2007 05:15:02 -0000 1.357
+++ Zend/zend_builtin_functions.c 23 Nov 2007 13:04:22 -0000
@@ -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
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: Zend/zend_execute.c
===================================================================
RCS file: /repository/ZendEngine2/zend_execute.c,v
retrieving revision 1.775
diff -u -p -d -r1.775 zend_execute.c
--- Zend/zend_execute.c 22 Nov 2007 13:33:53 -0000 1.775
+++ Zend/zend_execute.c 23 Nov 2007 13:04:22 -0000
@@ -65,7 +65,7 @@ static void zend_extension_fcall_end_han

#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_fun
if (unref && Z_ISREF_P(z) && Z_REFCOUNT_P(z) == 1) {
Z_UNSET_ISREF_P(z);
}
+ GC_ZVAL_CHECK_POSSIBLE_ROOT(z);
}
}
/* }}} */
@@ -90,8 +91,8 @@ static inline void zend_pzval_unlock_fre
}
/* }}} */

-#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))
Index: Zend/zend_execute_API.c
===================================================================
RCS file: /repository/ZendEngine2/zend_execute_API.c,v
retrieving revision 1.424
diff -u -p -d -r1.424 zend_execute_API.c
--- Zend/zend_execute_API.c 20 Nov 2007 09:51:43 -0000 1.424
+++ Zend/zend_execute_API.c 23 Nov 2007 13:04:23 -0000
@@ -447,8 +447,13 @@ ZEND_API void _zval_ptr_dtor(zval **zval
if (Z_REFCOUNT_PP(zval_ptr) == 0) {
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);
}
}
/* }}} */
Index: Zend/zend_gc.c
===================================================================
RCS file: Zend/zend_gc.c
diff -N Zend/zend_gc.c
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ Zend/zend_gc.c 23 Nov 2007 13:04:23 -0000
@@ -0,0 +1,515 @@
+/*
+ +----------------------------------------------------------------------+
+ | 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 |
+ | lic...@zend.com so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: David Wang <plane...@gmail.com> |
+ | Dmitry Stogov <dmi...@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) {
+ 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:
+ */
Index: Zend/zend_gc.h
===================================================================
RCS file: Zend/zend_gc.h
diff -N Zend/zend_gc.h
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ Zend/zend_gc.h 23 Nov 2007 13:04:23 -0000
@@ -0,0 +1,240 @@
+/*
+ +----------------------------------------------------------------------+
+ | 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 |
+ | lic...@zend.com so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: David Wang <plane...@gmail.com> |
+ | Dmitry Stogov <dmi...@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)); \
+ } \
+ } 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);
+ }
+}
+
+/* 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) \
+
+#endif /* ZEND_GC_H */
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * indent-tabs-mode: t
+ * End:
+ */
Index: Zend/zend_objects_API.c
===================================================================
RCS file: /repository/ZendEngine2/zend_objects_API.c,v
retrieving revision 1.67
diff -u -p -d -r1.67 zend_objects_API.c
--- Zend/zend_objects_API.c 7 Oct 2007 05:15:03 -0000 1.67
+++ Zend/zend_objects_API.c 23 Nov 2007 13:04:23 -0000
@@ -88,6 +88,8 @@ ZEND_API void zend_objects_store_free_ob
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
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
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
}
}
if (obj->refcount == 1) {
+ GC_REMOVE_ZOBJ_FROM_BUFFER(obj);
if (obj->free_storage) {
zend_try {
obj->free_storage(obj->object TSRMLS_CC);
Index: Zend/zend_objects_API.h
===================================================================
RCS file: /repository/ZendEngine2/zend_objects_API.h,v
retrieving revision 1.28
diff -u -p -d -r1.28 zend_objects_API.h
--- Zend/zend_objects_API.h 21 Jul 2007 00:34:41 -0000 1.28
+++ Zend/zend_objects_API.h 23 Nov 2007 13:04:23 -0000
@@ -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: Zend/tests/gc_001.phpt
===================================================================
RCS file: Zend/tests/gc_001.phpt
diff -N Zend/tests/gc_001.phpt
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ Zend/tests/gc_001.phpt 23 Nov 2007 13:04:23 -0000
@@ -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)
Index: Zend/tests/gc_002.phpt
===================================================================
RCS file: Zend/tests/gc_002.phpt
diff -N Zend/tests/gc_002.phpt
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ Zend/tests/gc_002.phpt 23 Nov 2007 13:04:23 -0000
@@ -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
Index: Zend/tests/gc_003.phpt
===================================================================
RCS file: Zend/tests/gc_003.phpt
diff -N Zend/tests/gc_003.phpt
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ Zend/tests/gc_003.phpt 23 Nov 2007 13:04:23 -0000
@@ -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
Index: Zend/tests/gc_004.phpt
===================================================================
RCS file: Zend/tests/gc_004.phpt
diff -N Zend/tests/gc_004.phpt
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ Zend/tests/gc_004.phpt 23 Nov 2007 13:04:23 -0000
@@ -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
Index: Zend/tests/gc_005.phpt
===================================================================
RCS file: Zend/tests/gc_005.phpt
diff -N Zend/tests/gc_005.phpt
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ Zend/tests/gc_005.phpt 23 Nov 2007 13:04:23 -0000
@@ -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
Index: Zend/tests/gc_006.phpt
===================================================================
RCS file: Zend/tests/gc_006.phpt
diff -N Zend/tests/gc_006.phpt
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ Zend/tests/gc_006.phpt 23 Nov 2007 13:04:23 -0000
@@ -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
Index: Zend/tests/gc_007.phpt
===================================================================
RCS file: Zend/tests/gc_007.phpt
diff -N Zend/tests/gc_007.phpt
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ Zend/tests/gc_007.phpt 23 Nov 2007 13:04:23 -0000
@@ -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
Index: Zend/tests/gc_008.phpt
===================================================================
RCS file: Zend/tests/gc_008.phpt
diff -N Zend/tests/gc_008.phpt
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ Zend/tests/gc_008.phpt 23 Nov 2007 13:04:23 -0000
@@ -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
Index: Zend/tests/gc_009.phpt
===================================================================
RCS file: Zend/tests/gc_009.phpt
diff -N Zend/tests/gc_009.phpt
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ Zend/tests/gc_009.phpt 23 Nov 2007 13:04:23 -0000
@@ -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
Index: Zend/tests/gc_010.phpt
===================================================================
RCS file: Zend/tests/gc_010.phpt
diff -N Zend/tests/gc_010.phpt
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ Zend/tests/gc_010.phpt 23 Nov 2007 13:04:23 -0000
@@ -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
Index: Zend/tests/gc_011.phpt
===================================================================
RCS file: Zend/tests/gc_011.phpt
diff -N Zend/tests/gc_011.phpt
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ Zend/tests/gc_011.phpt 23 Nov 2007 13:04:23 -0000
@@ -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
Index: Zend/tests/gc_012.phpt
===================================================================
RCS file: Zend/tests/gc_012.phpt
diff -N Zend/tests/gc_012.phpt
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ Zend/tests/gc_012.phpt 23 Nov 2007 13:04:23 -0000
@@ -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
Index: Zend/tests/gc_013.phpt
===================================================================
RCS file: Zend/tests/gc_013.phpt
diff -N Zend/tests/gc_013.phpt
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ Zend/tests/gc_013.phpt 23 Nov 2007 13:04:23 -0000
@@ -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
Index: Zend/tests/gc_014.phpt
===================================================================
RCS file: Zend/tests/gc_014.phpt
diff -N Zend/tests/gc_014.phpt
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ Zend/tests/gc_014.phpt 23 Nov 2007 13:04:23 -0000
@@ -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
Index: Zend/tests/gc_015.phpt
===================================================================
RCS file: Zend/tests/gc_015.phpt
diff -N Zend/tests/gc_015.phpt
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ Zend/tests/gc_015.phpt 23 Nov 2007 13:04:23 -0000
@@ -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
Index: Zend/tests/gc_016.phpt
===================================================================
RCS file: Zend/tests/gc_016.phpt
diff -N Zend/tests/gc_016.phpt
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ Zend/tests/gc_016.phpt 23 Nov 2007 13:04:23 -0000
@@ -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
Index: Zend/tests/gc_017.phpt
===================================================================
RCS file: Zend/tests/gc_017.phpt
diff -N Zend/tests/gc_017.phpt
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ Zend/tests/gc_017.phpt 23 Nov 2007 13:04:23 -0000
@@ -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
Index: Zend/tests/gc_018.phpt
===================================================================
RCS file: Zend/tests/gc_018.phpt
diff -N Zend/tests/gc_018.phpt
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ Zend/tests/gc_018.phpt 23 Nov 2007 13:04:23 -0000
@@ -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
Index: Zend/tests/gc_019.phpt
===================================================================
RCS file: Zend/tests/gc_019.phpt
diff -N Zend/tests/gc_019.phpt
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ Zend/tests/gc_019.phpt 23 Nov 2007 13:04:23 -0000
@@ -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
Index: Zend/tests/gc_020.phpt
===================================================================
RCS file: Zend/tests/gc_020.phpt
diff -N Zend/tests/gc_020.phpt
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ Zend/tests/gc_020.phpt 23 Nov 2007 13:04:23 -0000
@@ -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
Index: Zend/tests/gc_021.phpt
===================================================================
RCS file: Zend/tests/gc_021.phpt
diff -N Zend/tests/gc_021.phpt
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ Zend/tests/gc_021.phpt 23 Nov 2007 13:04:23 -0000
@@ -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
Index: Zend/tests/gc_022.phpt
===================================================================
RCS file: Zend/tests/gc_022.phpt
diff -N Zend/tests/gc_022.phpt
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ Zend/tests/gc_022.phpt 23 Nov 2007 13:04:23 -0000
@@ -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
Index: Zend/tests/gc_023.phpt
===================================================================
RCS file: Zend/tests/gc_023.phpt
diff -N Zend/tests/gc_023.phpt
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ Zend/tests/gc_023.phpt 23 Nov 2007 13:04:23 -0000
@@ -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
Index: Zend/tests/gc_024.phpt
===================================================================
RCS file: Zend/tests/gc_024.phpt
diff -N Zend/tests/gc_024.phpt
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ Zend/tests/gc_024.phpt 23 Nov 2007 13:04:23 -0000
@@ -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
Index: Zend/tests/gc_025.phpt
===================================================================
RCS file: Zend/tests/gc_025.phpt
diff -N Zend/tests/gc_025.phpt
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ Zend/tests/gc_025.phpt 23 Nov 2007 13:04:23 -0000
@@ -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
Index: Zend/tests/gc_026.phpt
===================================================================
RCS file: Zend/tests/gc_026.phpt
diff -N Zend/tests/gc_026.phpt
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ Zend/tests/gc_026.phpt 23 Nov 2007 13:04:23 -0000
@@ -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: main/main.c
===================================================================
RCS file: /repository/php-src/main/main.c,v
retrieving revision 1.753
diff -u -p -d -r1.753 main.c
--- main/main.c 22 Nov 2007 13:33:53 -0000 1.753
+++ main/main.c 23 Nov 2007 13:04:25 -0000
@@ -1820,6 +1820,8 @@ int php_module_startup(sapi_module_struc
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;

@@ -2068,6 +2070,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


--------------000100010008040602010309
Content-Type: text/plain; charset=us-ascii

--
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: http://www.php.net/unsub.php
--------------000100010008040602010309--

Markus Fischer

unread,
Dec 3, 2007, 7:17:10 PM12/3/07
to
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Hi,

Daniel Brown wrote:
> I don't know about how it worked for anyone else, but the tables
> didn't display properly on Gmail, so I had a hard time keeping up with
> the performance differences. If you have this in a separate file,
> could you send it to me as an attachment?

Same problem, all in one row, to "table" to read. We're having
non-apache application which run up to one hour to do their task and I
think our QA can test the new GC there (and memory consumption is an
issue there definitely).

thanks
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.6 (MingW32)
Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org

iD8DBQFHVHWh1nS0RcInK9ARAmPlAJ9XnOzFmLSl8qDxY5bLBfJBcmgqRACfZnsn
R3cFSHfkMpoffq+f5vMxI3g=
=OkMW
-----END PGP SIGNATURE-----

--

Andi Gutmans

unread,
Dec 3, 2007, 8:29:23 PM12/3/07
to
------_=_NextPart_001_01C835F4.D63CFC6B
Content-Type: text/plain;
charset="US-ASCII"
Content-Transfer-Encoding: quoted-printable

Sorry about that. Does the attached PDFed screenshot work for you?

Andi

> -----Original Message-----
> From: Daniel Brown [mailto:para...@gmail.com]
> Sent: Monday, December 03, 2007 1:21 PM
> To: Andi Gutmans
> Cc: inte...@lists.php.net
> Subject: Re: [PHP-DEV] Garbage collector patch

>=20
> On Dec 3, 2007 4:01 PM, Andi Gutmans <an...@zend.com> wrote:
> > Hi all,
> >
> >
> >
> > Was hoping to send this off earlier but I was travelling for the
past
> > week and had very limited email access.
> >
> > As promised in the past few weeks we have spent a significant amount
> of
> > time in reviewing the garbage collector work and testing it in our
> > performance lab. Dmitry has been exchanging ideas and patches with
> David
> > Wang during this time. Suffice to say that as I've mentioned in the
> > past, memory management is an extremely sensitive piece of PHP,
which
> is
> > why it was important for us to review this work in great depth
before
> > committing it to the code base.
> >
> >
> >
> > The updated patch still retains the same algorithm as David's
> original
> > implementation however the data structures have been changed in
order
> to
> > work faster and use less memory.
> >
> >
> >
> > The revised patch has the following advantages:
> > - It fixes several crash bugs
> >
> > - Enhances performance by removing several unnecessary checks
> > - The memory overhead was reduced (from 12 to 4 bytes for each
> > heap-allocated zval)
> > - The speed of "clean" PHP code (code that doesn't create cycles)
was
> > improved
> > - Additional test cases were created
> >
> > The end result is a more stable and faster GC patch. That said we
> have
> > yet to find real-life applications that create significant cycles
> which
> > would benefit from this patch. In fact as you'll see from the
results
> > our tests show an increase in maximum memory use and slower
execution
> > (to be fair they are marginal).
> >
> >
> >
> > We have tested both PHP_5_3 without any patches, the original patch
> and
> > the new patch.
> >
> >
> >
> > The following table shows execution time (seconds for N requests)
and
> > slowdown.
> >
> >
> >
> > PHP_5_3
> >
> > Original GC patch
> >
> > Current GC patch
> >
> >
> >
> >
> >
> > slowdown
> >
> >
> >
> > slowdown
> >
> > bench
> >
> > 11,550
> >
> > 12,310
> >
> > 6,58%
> >
> > 12,170
> >
> > 5,37%
> >
> > hello
> >
> > 8,781
> >
> > 8,852
> >
> > 0,81%
> >
> > 8,813
> >
> > 0,36%
> >
> > xoops
> >
> > 128,500
> >
> > 135,100
> >
> > 5,14%
> >
> > 130,200
> >
> > 1,32%
> >
> > static
> >
> > 18,540
> >
> > 20,840
> >
> > 12,41%
> >
> > 18,920
> >
> > 2,05%
> >
> > qdig
> >
> > 29,320
> >
> > 30,270
> >
> > 3,24%
> >
> > 29,610
> >
> > 0,99%
> >
> > qdig2
> >
> > 13,960
> >
> > 14,100
> >
> > 1,00%
> >
> > 14,090
> >
> > 0,93%
> >
> > The next table shows memory usage in MB and overhead
> >
> >
> >
> > PHP_5_3
> >
> > Original GC patch
> >
> > Current GC patch
> >
> >
> >
> >
> >
> > overhead
> >
> >
> >
> > overhead
> >
> > hello
> >
> > 13,750
> >
> > 13,945
> >
> > 1,42%
> >
> > 13,765
> >
> > 0,11%
> >
> > xoops
> >
> > 18,036
> >
> > 18,636
> >
> > 3,33%
> >
> > 18,568
> >
> > 2,95%
> >
> > static
> >
> > 15,300
> >
> > 16,000
> >
> > 4,58%
> >
> > 15,308
> >
> > 0,05%
> >
> > qdig
> >
> > 14,820
> >
> > 15,008
> >
> > 1,27%
> >
> > 14,828
> >
> > 0,05%
> >
> > qdig2
> >
> > 14,824
> >
> > 15,012
> >
> > 1,27%
> >
> > 14,838
> >
> > 0,09%
> >
> >
> >
> > To summarize the patch lead to approx. 5% slowdown and 3% memory
> > overhead for typical applications (as always, you mileage may vary
> > depending on your system's architecture and OS although my
> guesstimate
> > is that you will see even worse results in a 64bit environment). We
> also
> > tested SugarCRM to get another sanity for these results and we got
> > similar results.
> >
> >
> >
> > I am not quite sure where this leaves us with this patch. On one
hand
> I
> > think now the effort has been made it's a good thing to offer it as
> part
> > of PHP. The downside is of course some performance degradation and
> > possible instabilities as this patch continues to stabilize (it took
> > about two releases for us to stabilize the Zend Memory Manager even
> > after in-depth testing due to edge cases in allocation patterns and
> > various extensions, etc...).I'd be surprised if our team has found
> all
> > possible bugs.
> >
> >
> >
> > Personally I think the decision should be either in or out. Adding
> this
> > as a compile option is not a good idea as it would create binary
> > compatibility issues and would be a pain for the community. I think
> my
> > inclination is to commit the patch, not have it #ifdef'ed but always
> > compiled but to have the garbage collection itself turned off by
> default
> > (mainly for stability reasons. Note: the increased memory footprint
> and
> > performance loss also exists with the collection itself turned off).
> We
> > can turn it on when we're in dev for snapshots so that we iron out
> bugs.
> > That said, as you can see from the results most people and real-life
> > applications will be worse off than today.
> >
> >
> >
> > Thanks to David & Dmitry for working hard on this (and everyone else
> who
> > contributed).
> >
> >
> >
> > The stage is open for ideas/thoughts/suggestions J
> >
> >
> > Andi
> >
> >
>=20
> Andi,
>=20


> I don't know about how it worked for anyone else, but the tables
> didn't display properly on Gmail, so I had a hard time keeping up with
> the performance differences. If you have this in a separate file,

> could you send it to me as an attachment? I have a few real-world
> benchmarks I'd like to try from the angle of the shared-webhost
> industry (lots of users with horrible code running simultaneously, et
> cetera) which I'd like to compare to your notes.
>=20
> Thanks.
>=20
> --
> Daniel P. Brown
> [office] (570-) 587-7080 Ext. 272
> [mobile] (570-) 766-8107
>=20
> If at first you don't succeed, stick to what you know best so that you
> can make enough money to pay someone else to do it for you.


------_=_NextPart_001_01C835F4.D63CFC6B
Content-Type: text/plain; charset=us-ascii

--
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: http://www.php.net/unsub.php

------_=_NextPart_001_01C835F4.D63CFC6B--

Daniel Brown

unread,
Dec 3, 2007, 8:31:59 PM12/3/07
to
On Dec 3, 2007 4:49 PM, Derick Rethans <der...@php.net> wrote:

> On Mon, 3 Dec 2007, Andi Gutmans wrote:
>
> > Sorry about that. Does the attached PDFed screenshot work for you?
>
> No, as you can't attach files here....
>
> Derick
>
> --
> Derick Rethans
> http://derickrethans.nl | http://ez.no | http://xdebug.org
>

I didn't think it would work on the list, but I figured if Andi
either sent it to me, or clicked Reply-All, either way it would be
delivered directly to my inbox, which it was. So now it's here:
http://www.pilotpig.net/pdfs/

--
Daniel P. Brown
[office] (570-) 587-7080 Ext. 272
[mobile] (570-) 766-8107

If at first you don't succeed, stick to what you know best so that you


can make enough money to pay someone else to do it for you.

--

Andi Gutmans

unread,
Dec 3, 2007, 8:32:20 PM12/3/07
to
------_=_NextPart_001_01C835EF.BF4ABD7F

Content-Type: text/plain;
charset="US-ASCII"
Content-Transfer-Encoding: quoted-printable

Hi all,

=20

Was hoping to send this off earlier but I was travelling for the past
week and had very limited email access.

As promised in the past few weeks we have spent a significant amount of
time in reviewing the garbage collector work and testing it in our
performance lab. Dmitry has been exchanging ideas and patches with David
Wang during this time. Suffice to say that as I've mentioned in the
past, memory management is an extremely sensitive piece of PHP, which is
why it was important for us to review this work in great depth before
committing it to the code base.

=20

The updated patch still retains the same algorithm as David's original
implementation however the data structures have been changed in order to
work faster and use less memory.

=20

The revised patch has the following advantages:
- It fixes several crash bugs

- Enhances performance by removing several unnecessary checks=20


- The memory overhead was reduced (from 12 to 4 bytes for each
heap-allocated zval)
- The speed of "clean" PHP code (code that doesn't create cycles) was
improved
- Additional test cases were created

The end result is a more stable and faster GC patch. That said we have
yet to find real-life applications that create significant cycles which
would benefit from this patch. In fact as you'll see from the results
our tests show an increase in maximum memory use and slower execution
(to be fair they are marginal).

=20

We have tested both PHP_5_3 without any patches, the original patch and
the new patch.

=20

The following table shows execution time (seconds for N requests) and
slowdown.

=20

PHP_5_3

Original GC patch=20

Current GC patch

=20

=20

slowdown

=20

slowdown

bench

11,550

12,310

6,58%

12,170

5,37%

hello

8,781

8,852

0,81%

8,813

0,36%

xoops

128,500

135,100

5,14%

130,200

1,32%

static

18,540

20,840

12,41%

18,920

2,05%

qdig

29,320

30,270

3,24%

29,610

0,99%

qdig2

13,960

14,100

1,00%

14,090

0,93%

=20

PHP_5_3

Original GC patch

Current GC patch

=20

=20

overhead

=20

overhead

hello

13,750

13,945

1,42%

13,765

0,11%

xoops

18,036

18,636

3,33%

18,568

2,95%

static

15,300

16,000

4,58%

15,308

0,05%

qdig

14,820

15,008

1,27%

14,828

0,05%

qdig2

14,824

15,012

1,27%

14,838

0,09%

=20

To summarize the patch lead to approx. 5% slowdown and 3% memory
overhead for typical applications (as always, you mileage may vary
depending on your system's architecture and OS although my guesstimate
is that you will see even worse results in a 64bit environment). We also
tested SugarCRM to get another sanity for these results and we got
similar results.

=20

I am not quite sure where this leaves us with this patch. On one hand I
think now the effort has been made it's a good thing to offer it as part
of PHP. The downside is of course some performance degradation and
possible instabilities as this patch continues to stabilize (it took
about two releases for us to stabilize the Zend Memory Manager even
after in-depth testing due to edge cases in allocation patterns and
various extensions, etc...).I'd be surprised if our team has found all
possible bugs.

=20

Personally I think the decision should be either in or out. Adding this
as a compile option is not a good idea as it would create binary
compatibility issues and would be a pain for the community. I think my
inclination is to commit the patch, not have it #ifdef'ed but always
compiled but to have the garbage collection itself turned off by default
(mainly for stability reasons. Note: the increased memory footprint and
performance loss also exists with the collection itself turned off). We
can turn it on when we're in dev for snapshots so that we iron out bugs.
That said, as you can see from the results most people and real-life
applications will be worse off than today.

=20

Thanks to David & Dmitry for working hard on this (and everyone else who
contributed).

=20

The stage is open for ideas/thoughts/suggestions J


Andi


------_=_NextPart_001_01C835EF.BF4ABD7F--

Daniel Brown

unread,
Dec 3, 2007, 8:32:29 PM12/3/07
to
Markus,

If for some reason the PDF attachment didn't come through to you
on the list, let me know and I'll upload it to one of my servers for
you to download and use, as well.

On Dec 3, 2007 4:40 PM, Daniel Brown <para...@gmail.com> wrote:
> That looks great, Andi.
>
> Thanks.


>
>
> On Dec 3, 2007 4:38 PM, Andi Gutmans <an...@zend.com> wrote:
> > Sorry about that. Does the attached PDFed screenshot work for you?
> >

> > Andi
> >
> >
> > > -----Original Message-----
> > > From: Daniel Brown [mailto:para...@gmail.com]
> > > Sent: Monday, December 03, 2007 1:21 PM
> > > To: Andi Gutmans
> > > Cc: inte...@lists.php.net
> > > Subject: Re: [PHP-DEV] Garbage collector patch
> > >

> > > On Dec 3, 2007 4:01 PM, Andi Gutmans <an...@zend.com> wrote:
> > > > Hi all,
> > > >
> > > >
> > > >

> > > > Was hoping to send this off earlier but I was travelling for the
> > past
> > > > week and had very limited email access.
> > > >
> > > > As promised in the past few weeks we have spent a significant amount
> > > of
> > > > time in reviewing the garbage collector work and testing it in our
> > > > performance lab. Dmitry has been exchanging ideas and patches with
> > > David
> > > > Wang during this time. Suffice to say that as I've mentioned in the
> > > > past, memory management is an extremely sensitive piece of PHP,
> > which
> > > is
> > > > why it was important for us to review this work in great depth
> > before
> > > > committing it to the code base.
> > > >
> > > >
> > > >

> > > > The updated patch still retains the same algorithm as David's
> > > original
> > > > implementation however the data structures have been changed in
> > order
> > > to
> > > > work faster and use less memory.
> > > >
> > > >
> > > >

> > > > The revised patch has the following advantages:
> > > > - It fixes several crash bugs
> > > >
> > > > - Enhances performance by removing several unnecessary checks

> > > > - The memory overhead was reduced (from 12 to 4 bytes for each
> > > > heap-allocated zval)
> > > > - The speed of "clean" PHP code (code that doesn't create cycles)
> > was
> > > > improved
> > > > - Additional test cases were created
> > > >
> > > > The end result is a more stable and faster GC patch. That said we
> > > have
> > > > yet to find real-life applications that create significant cycles
> > > which
> > > > would benefit from this patch. In fact as you'll see from the
> > results
> > > > our tests show an increase in maximum memory use and slower
> > execution
> > > > (to be fair they are marginal).
> > > >
> > > >
> > > >

> > > > We have tested both PHP_5_3 without any patches, the original patch
> > > and
> > > > the new patch.
> > > >
> > > >
> > > >

> > > > The following table shows execution time (seconds for N requests)
> > and
> > > > slowdown.
> > > >
> > > >
> > > >

> > > > PHP_5_3
> > > >
> > > > Original GC patch
> > > >
> > > > Current GC patch
> > > >
> > > >
> > > >
> > > >
> > > >

> > > > slowdown

> > > > PHP_5_3
> > > >
> > > > Original GC patch
> > > >
> > > > Current GC patch
> > > >
> > > >
> > > >
> > > >
> > > >

> > > > overhead

> > > > To summarize the patch lead to approx. 5% slowdown and 3% memory
> > > > overhead for typical applications (as always, you mileage may vary
> > > > depending on your system's architecture and OS although my
> > > guesstimate
> > > > is that you will see even worse results in a 64bit environment). We
> > > also
> > > > tested SugarCRM to get another sanity for these results and we got
> > > > similar results.
> > > >
> > > >
> > > >

> > > > I am not quite sure where this leaves us with this patch. On one
> > hand
> > > I
> > > > think now the effort has been made it's a good thing to offer it as
> > > part
> > > > of PHP. The downside is of course some performance degradation and
> > > > possible instabilities as this patch continues to stabilize (it took
> > > > about two releases for us to stabilize the Zend Memory Manager even
> > > > after in-depth testing due to edge cases in allocation patterns and
> > > > various extensions, etc...).I'd be surprised if our team has found
> > > all
> > > > possible bugs.
> > > >
> > > >
> > > >

> > > > Personally I think the decision should be either in or out. Adding
> > > this
> > > > as a compile option is not a good idea as it would create binary
> > > > compatibility issues and would be a pain for the community. I think
> > > my
> > > > inclination is to commit the patch, not have it #ifdef'ed but always
> > > > compiled but to have the garbage collection itself turned off by
> > > default
> > > > (mainly for stability reasons. Note: the increased memory footprint
> > > and
> > > > performance loss also exists with the collection itself turned off).
> > > We
> > > > can turn it on when we're in dev for snapshots so that we iron out
> > > bugs.
> > > > That said, as you can see from the results most people and real-life
> > > > applications will be worse off than today.
> > > >
> > > >
> > > >

> > > > Thanks to David & Dmitry for working hard on this (and everyone else
> > > who
> > > > contributed).
> > > >
> > > >
> > > >

> > > > The stage is open for ideas/thoughts/suggestions J
> > > >
> > > >
> > > > Andi
> > > >
> > > >
> > >

> > > Andi,


> > >
> > > I don't know about how it worked for anyone else, but the tables
> > > didn't display properly on Gmail, so I had a hard time keeping up with
> > > the performance differences. If you have this in a separate file,
> > > could you send it to me as an attachment? I have a few real-world
> > > benchmarks I'd like to try from the angle of the shared-webhost
> > > industry (lots of users with horrible code running simultaneously, et
> > > cetera) which I'd like to compare to your notes.
> > >

> > > Thanks.

Daniel Brown

unread,
Dec 3, 2007, 8:33:24 PM12/3/07
to

Andi,

Thanks.

--

Sean Coates

unread,
Dec 3, 2007, 8:33:38 PM12/3/07
to
> Sorry about that. Does the attached PDFed screenshot work for you?

If only we knew how to publish documents on that 'web thing.........

(-:

S

Edward Z. Yang

unread,
Dec 3, 2007, 8:34:30 PM12/3/07
to
Daniel Brown wrote:
> If for some reason the PDF attachment didn't come through to you
> on the list, let me know and I'll upload it to one of my servers for
> you to download and use, as well.

The PDF didn't make it through for me. Can you upload it?

--
Edward Z. Yang GnuPG: 0x869C48DA
HTML Purifier <http://htmlpurifier.org> Anti-XSS Filter
[[ 3FA8 E9A9 7385 B691 A6FC B3CB A933 BE7D 869C 48DA ]]

Derick Rethans

unread,
Dec 3, 2007, 8:34:39 PM12/3/07
to
On Mon, 3 Dec 2007, Andi Gutmans wrote:

> Sorry about that. Does the attached PDFed screenshot work for you?

No, as you can't attach files here....

Derick

--

Markus Fischer

unread,
Dec 3, 2007, 8:35:11 PM12/3/07
to
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Hi,

yes exactly, there was no PDF attachment. Interestingly the signature
was a separate attachment ...

thanks
- - Markus

Daniel Brown wrote:
> Markus,


>
> If for some reason the PDF attachment didn't come through to you
> on the list, let me know and I'll upload it to one of my servers for
> you to download and use, as well.
>

> On Dec 3, 2007 4:40 PM, Daniel Brown <para...@gmail.com> wrote:
>> That looks great, Andi.
>>
>> Thanks.
>>
>>

>> On Dec 3, 2007 4:38 PM, Andi Gutmans <an...@zend.com> wrote:
>>> Sorry about that. Does the attached PDFed screenshot work for you?
>>>

>>> Andi
>>>
>>>
>>>> -----Original Message-----
>>>> From: Daniel Brown [mailto:para...@gmail.com]
>>>> Sent: Monday, December 03, 2007 1:21 PM
>>>> To: Andi Gutmans
>>>> Cc: inte...@lists.php.net
>>>> Subject: Re: [PHP-DEV] Garbage collector patch
>>>>

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.6 (MingW32)
Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org

iD8DBQFHVHi/1nS0RcInK9ARAp4KAKDKG2s1T4JZgPlAQJpsQsj7iVoSqACfQ9qt
2aF9QsCyV1tGU02vYInHQNU=
=AUjQ
-----END PGP SIGNATURE-----

Andi Gutmans

unread,
Dec 3, 2007, 8:35:19 PM12/3/07
to
That'd be great.
Dmitry, David, can you please send the updated patch to the list?

Andi

> -----Original Message-----
> From: Markus Fischer [mailto:mar...@fischer.name]
> Sent: Monday, December 03, 2007 1:31 PM
> To: Daniel Brown

> Cc: Andi Gutmans; inte...@lists.php.net
> Subject: Re: [PHP-DEV] Garbage collector patch
>=20
> -----BEGIN PGP SIGNED MESSAGE-----
> Hash: SHA1

>=20
> Hi,
>=20


> Daniel Brown wrote:
> > I don't know about how it worked for anyone else, but the tables
> > didn't display properly on Gmail, so I had a hard time keeping up
> with
> > the performance differences. If you have this in a separate file,
> > could you send it to me as an attachment?

>=20


> Same problem, all in one row, to "table" to read. We're having
> non-apache application which run up to one hour to do their task and I
> think our QA can test the new GC there (and memory consumption is an
> issue there definitely).

>=20
> thanks


> -----BEGIN PGP SIGNATURE-----
> Version: GnuPG v1.4.6 (MingW32)
> Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org

>=20
> iD8DBQFHVHWh1nS0RcInK9ARAmPlAJ9XnOzFmLSl8qDxY5bLBfJBcmgqRACfZnsn
> R3cFSHfkMpoffq+f5vMxI3g=3D
> =3DOkMW

Daniel Brown

unread,
Dec 3, 2007, 8:35:22 PM12/3/07
to
That looks great, Andi.

Thanks.

On Dec 3, 2007 4:38 PM, Andi Gutmans <an...@zend.com> wrote:
> Sorry about that. Does the attached PDFed screenshot work for you?
>
> Andi
>
>
> > -----Original Message-----
> > From: Daniel Brown [mailto:para...@gmail.com]
> > Sent: Monday, December 03, 2007 1:21 PM
> > To: Andi Gutmans

> > Cc: inte...@lists.php.net
> > Subject: Re: [PHP-DEV] Garbage collector patch
> >

> > I don't know about how it worked for anyone else, but the tables
> > didn't display properly on Gmail, so I had a hard time keeping up with
> > the performance differences. If you have this in a separate file,

> > could you send it to me as an attachment? I have a few real-world
> > benchmarks I'd like to try from the angle of the shared-webhost
> > industry (lots of users with horrible code running simultaneously, et
> > cetera) which I'd like to compare to your notes.
> >
> > Thanks.
> >
> > --
> > Daniel P. Brown
> > [office] (570-) 587-7080 Ext. 272
> > [mobile] (570-) 766-8107
> >
> > If at first you don't succeed, stick to what you know best so that you
> > can make enough money to pay someone else to do it for you.
>

--
Daniel P. Brown
[office] (570-) 587-7080 Ext. 272
[mobile] (570-) 766-8107

If at first you don't succeed, stick to what you know best so that you
can make enough money to pay someone else to do it for you.

--

Ilia Alshanetsky

unread,
Dec 3, 2007, 9:44:58 PM12/3/07
to
First of all big thanks for Dmitry and David for spending time on this
project and continuing to improve the original patch. Based on the
results so far, I think the patch does have a value, but certainly not
in a general case. Relative simple scripts have little to gain from it
and only to loose 3-5% of speed depending on a situation and given
insubstantial memory gains it seems of little use in general case.
That said, there are complex applications (perhaps unnecessarily
so ;-) ) that could see some real benefits from this code. My
suggestion is that we make this feature a compile time flag, that's
off by default and people who feel that their applications warrant a
garbage collector can enable it for their install and at the same time
those who don't (general case) have no penalties of having it in place.

Ilia Alshanetsky

Cristian Rodriguez

unread,
Dec 3, 2007, 10:05:18 PM12/3/07
to
2007/12/3, Ilia Alshanetsky <il...@prohost.org>:

> I think the patch does have a value,

yes, it does, what worries me is the introduction of yet another
non-sense ini setting that modified the very engine behaviuor.. I
think we all agree that there are way too many of those do we ?


. My
> suggestion is that we make this feature a compile time flag.

My suggestion is not to add any compile time flag, either provide it or dont.

> and people who feel that their applications warrant a
> garbage collector can enable it for their install and at the same time
> those who don't (general case) have no penalties of having it in place.

People more commonly uses PHP from distributors, in binary form.. they
dont usually decide what is enabled or not, so you have to either
reccommend this feature or mark it clearly as untrusted, experimental.

such switches only add more complexity, confusion for users and
addtional trouble to distributors.

my 2CLP.


--
http://www.kissofjudas.net/

Steph Fox

unread,
Dec 3, 2007, 11:41:57 PM12/3/07
to
> such switches only add more complexity, confusion for users and
> addtional trouble to distributors.

FWIW, amen to that.

- Steph

Ronald Chmara

unread,
Dec 4, 2007, 1:07:54 AM12/4/07
to

On Dec 3, 2007, at 1:01 PM, Andi Gutmans wrote:

> Hi all,
>
> Was hoping to send this off earlier but I was travelling for the past
> week and had very limited email access.
>
> As promised in the past few weeks we have spent a significant
> amount of
> time in reviewing the garbage collector work and testing it in our
> performance lab. Dmitry has been exchanging ideas and patches with
> David
> Wang during this time. Suffice to say that as I've mentioned in the
> past, memory management is an extremely sensitive piece of PHP,
> which is
> why it was important for us to review this work in great depth before
> committing it to the code base.
>
> The updated patch still retains the same algorithm as David's original
> implementation however the data structures have been changed in
> order to
> work faster and use less memory.

> ...


> Personally I think the decision should be either in or out. Adding
> this
> as a compile option is not a good idea as it would create binary
> compatibility issues and would be a pain for the community.

> ...


> The stage is open for ideas/thoughts/suggestions

I'm really hesitant to even *mention* this idea, but....

Could "alternate" memory management systems be made available via
PECL add-ons, or, more to the point:
What is the *actual cost and complexity* involved in implementing
(possibly many) different user-selectable memory management systems,
and what other future benefits/drawbacks might we see by doing such a
thing? (GC is big now, but what about memory pools per mod_auth user,
or SHM/SEM pools, or tuning amounts of memory per function, etc...)

I will now apologize to everybody who I just made cry, scream, or
damage their furniture, as I didn't mean to hurt you, just trying to
stimulate ideas.

-Ronabop

Andi Gutmans

unread,
Dec 4, 2007, 1:32:26 AM12/4/07
to
> -----Original Message-----
> From: Ronald Chmara [mailto:r...@Opus1.COM]
> Sent: Monday, December 03, 2007 10:06 PM
> To: Andi Gutmans
> Cc: inte...@lists.php.net
> Subject: Re: [PHP-DEV] Garbage collector patch
> I'm really hesitant to even *mention* this idea, but....
>=20
> Could "alternate" memory management systems be made available via
> PECL add-ons, or, more to the point:
> What is the *actual cost and complexity* involved in implementing
> (possibly many) different user-selectable memory management systems,
> and what other future benefits/drawbacks might we see by doing such a
> thing? (GC is big now, but what about memory pools per mod_auth user,
> or SHM/SEM pools, or tuning amounts of memory per function, etc...)
>=20

> I will now apologize to everybody who I just made cry, scream, or
> damage their furniture, as I didn't mean to hurt you, just trying to
> stimulate ideas.

Hi Ronald,

PHP 5.2.x already supports the ability to hook in different "page"
managers. In PHP 5.3 you can also override the memory allocation
functions. However, this would not include garbage collection like
algorithms which actually require changes in the core PHP data type such
as zvals. In fact the garbage collection adds memory to the basic
datatypes which is why I suggested to either always make these changes,
or don't make them so that we retain binary compatibility across all
builds of PHP.
So overriding basic memory allocation functions, yes, ability to provide
various GC implementations, no.

Andi

Ronald Chmara

unread,
Dec 4, 2007, 2:47:49 AM12/4/07
to
On Dec 3, 2007, at 10:30 PM, Andi Gutmans wrote:
>> -----Original Message-----
>> From: Ronald Chmara [mailto:r...@Opus1.COM]
>> What is the *actual cost and complexity* involved in implementing
>> (possibly many) different user-selectable memory management systems,
>> and what other future benefits/drawbacks might we see by doing such a
>> thing? (GC is big now, but what about memory pools per mod_auth user,
>> or SHM/SEM pools, or tuning amounts of memory per function, etc...)
>>
>> I will now apologize to everybody who I just made cry, scream, or
>> damage their furniture, as I didn't mean to hurt you, just trying to
>> stimulate ideas.
>
> Hi Ronald,
>
> PHP 5.2.x already supports the ability to hook in different "page"
> managers. In PHP 5.3 you can also override the memory allocation
> functions. However, this would not include garbage collection like
> algorithms which actually require changes in the core PHP data type
> such
> as zvals. In fact the garbage collection adds memory to the basic
> datatypes which is why I suggested to either always make these
> changes,
> or don't make them so that we retain binary compatibility across all
> builds of PHP.
> So overriding basic memory allocation functions, yes, ability to
> provide
> various GC implementations, no.

Okay, so, without this patch, I can allocate mem, and destroy it, on
a per page level, but not on a zval level, and the choice is:
a) zval (etc.) destruction with this patch, which has a 5% speed hit
at times (varies per test)
b) no patch, no change
c) something which hasn't been thought of yet?

I'd have to (sadly) ask that anything that slows down PHP by 5%, to
improve performance for programmers that, uhm, "leak" or otherwise
gobble RAM, that they, uhm, refactor their code as professional
programmers.

Thanks for clearing it up for me, Andi.

-Bop

Antony Dovgal

unread,
Dec 4, 2007, 3:32:11 AM12/4/07
to
On 04.12.2007 06:00, Cristian Rodriguez wrote:
> such switches only add more complexity, confusion for users and
> addtional trouble to distributors.

... which I believe are going to enable this flag by default anyway,
because they don't care about performance too much.

So I agree, either do it or not, yet another engine mode is bad idea.

--
Wbr,
Antony Dovgal

Alexey Zakhlestin

unread,
Dec 4, 2007, 4:48:44 AM12/4/07
to
I believe this has to be done and it should be always on.
Having less headache is good. I will find other ways to speed up my projects


--
Alexey Zakhlestin
http://blog.milkfarmsoft.com/

Jani Taskinen

unread,
Dec 4, 2007, 6:44:08 AM12/4/07
to
On Tue, 2007-12-04 at 00:00 -0300, Cristian Rodriguez wrote:
> 2007/12/3, Ilia Alshanetsky <il...@prohost.org>:
> > I think the patch does have a value,
>
> yes, it does, what worries me is the introduction of yet another
> non-sense ini setting that modified the very engine behaviuor.. I
> think we all agree that there are way too many of those do we ?

Yup. :)

> > My
> > suggestion is that we make this feature a compile time flag.
>
> My suggestion is not to add any compile time flag, either provide it or dont.

I have to agree. Either it's in or not. We've had enough trouble with
giving too much rope for people already..

--
Patches/Donations: http://pecl.php.net/~jani/

David Zülke

unread,
Dec 4, 2007, 9:35:27 AM12/4/07
to
I could be wrong, of course, but my guess is that the ~3% speed decrease
a) is not really significant to 95% of the users (I mean _really_ =20
significant; of course, everyone is going to whine about it first) and
b) is going to be eliminated over time


david

Am 04.12.2007 um 03:43 schrieb Ilia Alshanetsky:

> First of all big thanks for Dmitry and David for spending time on =20
> this project and continuing to improve the original patch. Based on =20=

> the results so far, I think the patch does have a value, but =20
> certainly not in a general case. Relative simple scripts have little =20=

> to gain from it and only to loose 3-5% of speed depending on a =20
> situation and given insubstantial memory gains it seems of little =20
> use in general case. That said, there are complex applications =20
> (perhaps unnecessarily so ;-) ) that could see some real benefits =20
> from this code. My suggestion is that we make this feature a compile =20=

> time flag, that's off by default and people who feel that their =20
> applications warrant a garbage collector can enable it for their =20
> install and at the same time those who don't (general case) have no =20=

> penalties of having it in place.
>
>

> On 3-Dec-07, at 4:01 PM, Andi Gutmans wrote:
>
>> Hi all,
>>
>>
>>
>> Was hoping to send this off earlier but I was travelling for the past
>> week and had very limited email access.
>>

>> As promised in the past few weeks we have spent a significant =20


>> amount of
>> time in reviewing the garbage collector work and testing it in our

>> performance lab. Dmitry has been exchanging ideas and patches with =20=

>> David
>> Wang during this time. Suffice to say that as I've mentioned in the

>> past, memory management is an extremely sensitive piece of PHP, =20


>> which is
>> why it was important for us to review this work in great depth before
>> committing it to the code base.
>>
>>
>>

>> The updated patch still retains the same algorithm as David's =20
>> original
>> implementation however the data structures have been changed in =20


>> order to
>> work faster and use less memory.
>>
>>
>>

>> The revised patch has the following advantages:
>> - It fixes several crash bugs
>>
>> - Enhances performance by removing several unnecessary checks
>> - The memory overhead was reduced (from 12 to 4 bytes for each
>> heap-allocated zval)
>> - The speed of "clean" PHP code (code that doesn't create cycles) was
>> improved
>> - Additional test cases were created
>>

>> The end result is a more stable and faster GC patch. That said we =20
>> have
>> yet to find real-life applications that create significant cycles =20


>> which
>> would benefit from this patch. In fact as you'll see from the results
>> our tests show an increase in maximum memory use and slower execution
>> (to be fair they are marginal).
>>
>>
>>

>> We have tested both PHP_5_3 without any patches, the original patch =20=

>> and
>> the new patch.
>>
>>
>>
>> The following table shows execution time (seconds for N requests) and
>> slowdown.
>>
>>
>>
>> PHP_5_3
>>
>> Original GC patch
>>
>> Current GC patch
>>

>> =09

>> =09

>> depending on your system's architecture and OS although my =20
>> guesstimate
>> is that you will see even worse results in a 64bit environment). We =20=

>> also
>> tested SugarCRM to get another sanity for these results and we got
>> similar results.
>>
>>
>>

>> I am not quite sure where this leaves us with this patch. On one =20
>> hand I
>> think now the effort has been made it's a good thing to offer it as =20=

>> part
>> of PHP. The downside is of course some performance degradation and
>> possible instabilities as this patch continues to stabilize (it took
>> about two releases for us to stabilize the Zend Memory Manager even
>> after in-depth testing due to edge cases in allocation patterns and

>> various extensions, etc...).I'd be surprised if our team has found =20=

>> all
>> possible bugs.
>>
>>
>>
>> Personally I think the decision should be either in or out. Adding =20=

>> this
>> as a compile option is not a good idea as it would create binary

>> compatibility issues and would be a pain for the community. I think =20=

>> my
>> inclination is to commit the patch, not have it #ifdef'ed but always

>> compiled but to have the garbage collection itself turned off by =20
>> default
>> (mainly for stability reasons. Note: the increased memory footprint =20=

>> and
>> performance loss also exists with the collection itself turned =20
>> off). We
>> can turn it on when we're in dev for snapshots so that we iron out =20=

>> bugs.
>> That said, as you can see from the results most people and real-life
>> applications will be worse off than today.
>>
>>
>>

>> Thanks to David & Dmitry for working hard on this (and everyone =20
>> else who
>> contributed).
>>
>>
>>
>> The stage is open for ideas/thoughts/suggestions J
>>
>>
>> Andi
>>
>
> Ilia Alshanetsky
>
> --=20


> PHP Internals - PHP Runtime Development Mailing List
> To unsubscribe, visit: http://www.php.net/unsub.php
>
>

--
David Z=FClke
d...@bitxtender.com
Tel: +49 (0)89 57 08 15 15
bitXtender GbR
Paul-Heyse-Stra=DFe 6
80336 M=FCnchen

Antony Dovgal

unread,
Dec 4, 2007, 10:58:15 AM12/4/07
to
On 04.12.2007 18:31, Andi Gutmans wrote:
> You may be right longer term but shorter term I still believe there may be stability issues with this patch some of which we haven't figured out. Although we did testing and found crash bugs I wouldn't trust our level of testing to deem it stable. If we go down the route of always on we could have a hidden ini file (not listed in php.ini-dist and phpinfo()) which we can advise to turn off if something goes wrong and once we can enough confidence that there aren't any lurking bugs we could remove it.

You mean provide an ini setting to be able to turn it Off?
But why do you call it "always On" then?
And what's the difference comparing to what you've proposed earlier?

Concerning the stability issues, I'd say we have quite good chance
to make it stable enough, as (I hope) 5.3.0 is not going to be released
for at least several months more.

Companies that are especially concerned of performance/stability
are encouraged to step forward and give us a hand.

--
Wbr,
Antony Dovgal

Stanislav Malyshev

unread,
Dec 4, 2007, 1:35:50 PM12/4/07
to
> that could see some real benefits from this code. My suggestion is that
> we make this feature a compile time flag, that's off by default and

What about binary compatibility?

--
Stanislav Malyshev, Zend Software Architect
st...@zend.com http://www.zend.com/
(408)253-8829 MSN: st...@zend.com

Rasmus Lerdorf

unread,
Dec 4, 2007, 2:18:44 PM12/4/07
to
Antony Dovgal wrote:
> On 04.12.2007 18:31, Andi Gutmans wrote:
>> You may be right longer term but shorter term I still believe there may be stability issues with this patch some of which we haven't figured out. Although we did testing and found crash bugs I wouldn't trust our level of testing to deem it stable. If we go down the route of always on we could have a hidden ini file (not listed in php.ini-dist and phpinfo()) which we can advise to turn off if something goes wrong and once we can enough confidence that there aren't any lurking bugs we could remove it.
>
> You mean provide an ini setting to be able to turn it Off?
> But why do you call it "always On" then?
> And what's the difference comparing to what you've proposed earlier?
>
> Concerning the stability issues, I'd say we have quite good chance
> to make it stable enough, as (I hope) 5.3.0 is not going to be released
> for at least several months more.
>
> Companies that are especially concerned of performance/stability
> are encouraged to step forward and give us a hand.

Companies concerned about performance aren't going to use it at all. A
5% hit is significant given that it doesn't fix anything that a company
already concerned about performance/stability cares about. Super leaky
or recursively allocating scripts have long since been weeded out of
those code bases, so it is a 5% performance hit with no gain.

I am not arguing that it isn't useful in the general case. It will make
PHP more robust on loosely controlled servers for what amounts to only a
small penalty, but at the same time it is an easy 5% win for people with
dedicated servers running well-written code.

-Rasmus

scott.m...@synergy8.com

unread,
Dec 4, 2007, 2:48:07 PM12/4/07
to
I think having it configurable is a must. Turning it on / off via a =
compile flag will not suit everyone.

Eg - I have a situation where I would not want to run garbage collection =
for web pages served off my server due to the performance hit, however I =
also have a CLI script which I run to do complex, but repetitive tasks =
for ~half an hour.=20

Now this is not really a big deal to me as I managed to rid most of the =
leaks by nulling out cyclic references, however that took a lot of work =
which could have been avoided by this.

Could I suggest also enabling it by default for CLI?


SCOTT MCNAUGHT
Software Developer

Synergy 8 / +617 3397 5212
scott.m...@synergy8.com


-----Original Message-----
From: Rasmus Lerdorf [mailto:ras...@lerdorf.com]=20
Sent: Wednesday, 5 December 2007 5:17 AM
To: Antony Dovgal
Cc: Andi Gutmans; Cristian Rodriguez; inte...@lists.php.net
Subject: Re: [PHP-DEV] Garbage collector patch

Antony Dovgal wrote:
> On 04.12.2007 18:31, Andi Gutmans wrote:

>> You may be right longer term but shorter term I still believe there =
may be stability issues with this patch some of which we haven't figured =
out. Although we did testing and found crash bugs I wouldn't trust our =
level of testing to deem it stable. If we go down the route of always on =
we could have a hidden ini file (not listed in php.ini-dist and =
phpinfo()) which we can advise to turn off if something goes wrong and =
once we can enough confidence that there aren't any lurking bugs we =
could remove it.
>=20


> You mean provide an ini setting to be able to turn it Off?
> But why do you call it "always On" then?
> And what's the difference comparing to what you've proposed earlier?

>=20
> Concerning the stability issues, I'd say we have quite good chance=20
> to make it stable enough, as (I hope) 5.3.0 is not going to be =
released=20


> for at least several months more.

>=20
> Companies that are especially concerned of performance/stability=20


> are encouraged to step forward and give us a hand.

Companies concerned about performance aren't going to use it at all. A
5% hit is significant given that it doesn't fix anything that a company
already concerned about performance/stability cares about. Super leaky
or recursively allocating scripts have long since been weeded out of
those code bases, so it is a 5% performance hit with no gain.

I am not arguing that it isn't useful in the general case. It will make
PHP more robust on loosely controlled servers for what amounts to only a
small penalty, but at the same time it is an easy 5% win for people with
dedicated servers running well-written code.

-Rasmus

--=20

Stanislav Malyshev

unread,
Dec 4, 2007, 3:18:17 PM12/4/07
to
> 1. Always compile it in but leave undocumented #ifdefs in place for
> performance freaks. Those same performance freaks aren't going to care
> about the binary compatibility issue since they are the same people who
> build all their own stuff.

Note that breaking BC is not only about performance - one your build is
not the same as mainstream PHP, you can't use any binary extension which
would do anything non-performance-related - like interfacing some
external system/library, debugging, profiling, testing, security and so on.
Any commercial module won't be available for the user of this switch,
and all open-source modules one'd have to build by oneself, which may be
serious maintenance issue. I know there are a bunch of companies that
compile PHP with their own options but still use commercial modules,
including both performance and non-performance ones. They couldn't use
this compile switch.


--
Stanislav Malyshev, Zend Software Architect
st...@zend.com http://www.zend.com/
(408)253-8829 MSN: st...@zend.com

--

Rasmus Lerdorf

unread,
Dec 4, 2007, 3:20:06 PM12/4/07
to
Stanislav Malyshev wrote:
>> 1. Always compile it in but leave undocumented #ifdefs in place for
>> performance freaks. Those same performance freaks aren't going to care
>> about the binary compatibility issue since they are the same people who
>> build all their own stuff.
>
> Note that breaking BC is not only about performance - one your build is
> not the same as mainstream PHP, you can't use any binary extension which
> would do anything non-performance-related - like interfacing some
> external system/library, debugging, profiling, testing, security and so on.
> Any commercial module won't be available for the user of this switch,
> and all open-source modules one'd have to build by oneself, which may be
> serious maintenance issue. I know there are a bunch of companies that
> compile PHP with their own options but still use commercial modules,
> including both performance and non-performance ones. They couldn't use
> this compile switch.

Yes, I know what binary compatibility means.

-Rasmus

Reply all
Reply to author
Forward
0 new messages