summaryrefslogtreecommitdiff
path: root/Zend/zend_gc.c
diff options
context:
space:
mode:
Diffstat (limited to 'Zend/zend_gc.c')
-rw-r--r--Zend/zend_gc.c110
1 files changed, 103 insertions, 7 deletions
diff --git a/Zend/zend_gc.c b/Zend/zend_gc.c
index 39ed6a96a9..eb7f0ab414 100644
--- a/Zend/zend_gc.c
+++ b/Zend/zend_gc.c
@@ -72,22 +72,110 @@
#include "zend.h"
#include "zend_API.h"
+#ifndef GC_BENCH
+# define GC_BENCH 0
+#endif
+
+#ifndef ZEND_GC_DEBUG
+# define ZEND_GC_DEBUG 0
+#endif
+
+#define GC_COLOR 0xc000
+
+#define GC_BLACK 0x0000 /* must be zero */
+#define GC_WHITE 0x8000
+#define GC_GREY 0x4000
+#define GC_PURPLE 0xc000
+
+#define GC_ADDRESS(v) \
+ ((v) & ~GC_COLOR)
+#define GC_INFO_GET_COLOR(v) \
+ (((zend_uintptr_t)(v)) & GC_COLOR)
+#define GC_INFO_SET_ADDRESS(v, a) \
+ do {(v) = ((v) & GC_COLOR) | (a);} while (0)
+#define GC_INFO_SET_COLOR(v, c) \
+ do {(v) = ((v) & ~GC_COLOR) | (c);} while (0)
+#define GC_INFO_SET_BLACK(v) \
+ do {(v) = (v) & ~GC_COLOR;} while (0)
+#define GC_INFO_SET_PURPLE(v) \
+ do {(v) = (v) | GC_COLOR;} while (0)
+
/* one (0) is reserved */
#define GC_ROOT_BUFFER_MAX_ENTRIES 10001
#define GC_HAS_DESTRUCTORS (1<<0)
-#ifndef ZEND_GC_DEBUG
-# define ZEND_GC_DEBUG 0
+#define GC_NUM_ADDITIONAL_ENTRIES \
+ ((4096 - ZEND_MM_OVERHEAD - sizeof(void*) * 2) / sizeof(gc_root_buffer))
+
+ZEND_API int (*gc_collect_cycles)(void);
+
+typedef struct _gc_root_buffer {
+ zend_refcounted *ref;
+ struct _gc_root_buffer *next; /* double-linked list */
+ struct _gc_root_buffer *prev;
+ uint32_t refcount;
+} gc_root_buffer;
+
+typedef struct _gc_additional_bufer gc_additional_buffer;
+
+struct _gc_additional_bufer {
+ uint32_t used;
+ gc_additional_buffer *next;
+ gc_root_buffer buf[GC_NUM_ADDITIONAL_ENTRIES];
+};
+
+typedef struct _zend_gc_globals {
+ zend_bool gc_enabled;
+ zend_bool gc_active;
+ zend_bool gc_full;
+
+ 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 */
+ gc_root_buffer *first_unused; /* pointer to first unused buffer */
+ gc_root_buffer *last_unused; /* pointer to last unused buffer */
+
+ gc_root_buffer to_free; /* list to free */
+ gc_root_buffer *next_to_free;
+
+ uint32_t gc_runs;
+ uint32_t collected;
+
+#if GC_BENCH
+ uint32_t root_buf_length;
+ uint32_t root_buf_peak;
+ uint32_t zval_possible_root;
+ uint32_t zval_buffered;
+ uint32_t zval_remove_from_buffer;
+ uint32_t zval_marked_grey;
#endif
+ gc_additional_buffer *additional_buffer;
+
+} zend_gc_globals;
+
#ifdef ZTS
-ZEND_API int gc_globals_id;
+static int gc_globals_id;
+#define GC_G(v) ZEND_TSRMG(gc_globals_id, zend_gc_globals *, v)
#else
-ZEND_API zend_gc_globals gc_globals;
+#define GC_G(v) (gc_globals.v)
+static zend_gc_globals gc_globals;
#endif
-ZEND_API int (*gc_collect_cycles)(void);
+#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
#if ZEND_GC_DEBUG > 1
# define GC_TRACE(format, ...) fprintf(stderr, format "\n", ##__VA_ARGS__);
@@ -249,13 +337,21 @@ ZEND_API void gc_reset(void)
GC_G(additional_buffer) = NULL;
}
-ZEND_API void gc_init(void)
+ZEND_API zend_bool gc_set_enabled(zend_bool enable)
{
- if (GC_G(buf) == NULL && GC_G(gc_enabled)) {
+ zend_bool old_enabled = GC_G(gc_enabled);
+ GC_G(gc_enabled) = enable;
+ if (enable && !old_enabled && GC_G(buf) == NULL) {
GC_G(buf) = (gc_root_buffer*) malloc(sizeof(gc_root_buffer) * GC_ROOT_BUFFER_MAX_ENTRIES);
GC_G(last_unused) = &GC_G(buf)[GC_ROOT_BUFFER_MAX_ENTRIES];
gc_reset();
}
+ return old_enabled;
+}
+
+ZEND_API zend_bool gc_enabled(void)
+{
+ return GC_G(gc_enabled);
}
ZEND_API void ZEND_FASTCALL gc_possible_root(zend_refcounted *ref)