diff options
Diffstat (limited to 'Zend/zend_gc.c')
| -rw-r--r-- | Zend/zend_gc.c | 110 |
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) |
