From baa9890112a863f10e62bbb4c5a1623642a5db3c Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Mon, 26 Feb 2018 12:49:58 +0300 Subject: [PATCH] Completely hide GC implementation details into zend_gc.c --- Zend/zend.c | 27 ++++++-- Zend/zend_builtin_functions.c | 2 +- Zend/zend_execute_API.c | 2 +- Zend/zend_gc.c | 118 ++++++++++++++++++++++++++++++---- Zend/zend_gc.h | 101 +---------------------------- Zend/zend_hash.c | 2 +- Zend/zend_types.h | 2 +- 7 files changed, 137 insertions(+), 117 deletions(-) diff --git a/Zend/zend.c b/Zend/zend.c index efe51b7b471..a714f04786e 100644 --- a/Zend/zend.c +++ b/Zend/zend.c @@ -101,16 +101,35 @@ static ZEND_INI_MH(OnUpdateErrorReporting) /* {{{ */ static ZEND_INI_MH(OnUpdateGCEnabled) /* {{{ */ { - OnUpdateBool(entry, new_value, mh_arg1, mh_arg2, mh_arg3, stage); + zend_bool val; - if (GC_G(gc_enabled)) { - gc_init(); + if (ZSTR_LEN(new_value) == 2 && strcasecmp("on", ZSTR_VAL(new_value)) == 0) { + val = 1; + } else if (ZSTR_LEN(new_value) == 3 && strcasecmp("yes", ZSTR_VAL(new_value)) == 0) { + val = 1; + }else if (ZSTR_LEN(new_value) == 4 && strcasecmp("true", ZSTR_VAL(new_value)) == 0) { + val = 1; + } else { + val = (zend_bool) atoi(ZSTR_VAL(new_value)); } + gc_set_enabled(val); + return SUCCESS; } /* }}} */ +static ZEND_INI_DISP(zend_gc_enabled_displayer_cb) /* {{{ */ +{ + if (gc_enabled()) { + ZEND_PUTS("On"); + } else { + ZEND_PUTS("Off"); + } +} +/* }}} */ + + static ZEND_INI_MH(OnUpdateScriptEncoding) /* {{{ */ { if (!CG(multibyte)) { @@ -154,7 +173,7 @@ static ZEND_INI_MH(OnUpdateAssertions) /* {{{ */ ZEND_INI_BEGIN() ZEND_INI_ENTRY("error_reporting", NULL, ZEND_INI_ALL, OnUpdateErrorReporting) STD_ZEND_INI_ENTRY("zend.assertions", "1", ZEND_INI_ALL, OnUpdateAssertions, assertions, zend_executor_globals, executor_globals) - STD_ZEND_INI_BOOLEAN("zend.enable_gc", "1", ZEND_INI_ALL, OnUpdateGCEnabled, gc_enabled, zend_gc_globals, gc_globals) + ZEND_INI_ENTRY3_EX("zend.enable_gc", "1", ZEND_INI_ALL, OnUpdateGCEnabled, NULL, NULL, NULL, zend_gc_enabled_displayer_cb) STD_ZEND_INI_BOOLEAN("zend.multibyte", "0", ZEND_INI_PERDIR, OnUpdateBool, multibyte, zend_compiler_globals, compiler_globals) ZEND_INI_ENTRY("zend.script_encoding", NULL, ZEND_INI_ALL, OnUpdateScriptEncoding) STD_ZEND_INI_BOOLEAN("zend.detect_unicode", "1", ZEND_INI_ALL, OnUpdateBool, detect_unicode, zend_compiler_globals, compiler_globals) diff --git a/Zend/zend_builtin_functions.c b/Zend/zend_builtin_functions.c index bd35d90627c..37121663b43 100644 --- a/Zend/zend_builtin_functions.c +++ b/Zend/zend_builtin_functions.c @@ -361,7 +361,7 @@ ZEND_FUNCTION(gc_collect_cycles) Returns status of the circular reference collector */ ZEND_FUNCTION(gc_enabled) { - RETURN_BOOL(GC_G(gc_enabled)); + RETURN_BOOL(gc_enabled()); } /* }}} */ diff --git a/Zend/zend_execute_API.c b/Zend/zend_execute_API.c index f66bd663045..7b012005f0b 100644 --- a/Zend/zend_execute_API.c +++ b/Zend/zend_execute_API.c @@ -302,7 +302,7 @@ void shutdown_executor(void) /* {{{ */ zend_hash_graceful_reverse_destroy(&EG(symbol_table)); #if ZEND_DEBUG - if (GC_G(gc_enabled) && !CG(unclean_shutdown)) { + if (gc_enabled() && !CG(unclean_shutdown)) { gc_collect_cycles(); } #endif diff --git a/Zend/zend_gc.c b/Zend/zend_gc.c index 39ed6a96a98..eb7f0ab4145 100644 --- a/Zend/zend_gc.c +++ b/Zend/zend_gc.c @@ -72,23 +72,111 @@ #include "zend.h" #include "zend_API.h" -/* one (0) is reserved */ -#define GC_ROOT_BUFFER_MAX_ENTRIES 10001 - -#define GC_HAS_DESTRUCTORS (1<<0) +#ifndef GC_BENCH +# define GC_BENCH 0 +#endif #ifndef ZEND_GC_DEBUG # define ZEND_GC_DEBUG 0 #endif -#ifdef ZTS -ZEND_API int gc_globals_id; -#else -ZEND_API zend_gc_globals gc_globals; -#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) + +#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 +static int gc_globals_id; +#define GC_G(v) ZEND_TSRMG(gc_globals_id, zend_gc_globals *, v) +#else +#define GC_G(v) (gc_globals.v) +static zend_gc_globals gc_globals; +#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 + #if ZEND_GC_DEBUG > 1 # define GC_TRACE(format, ...) fprintf(stderr, format "\n", ##__VA_ARGS__); # define GC_TRACE_REF(ref, format, ...) \ @@ -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) diff --git a/Zend/zend_gc.h b/Zend/zend_gc.h index f0bc5610cdb..f045026ccfd 100644 --- a/Zend/zend_gc.h +++ b/Zend/zend_gc.h @@ -22,102 +22,6 @@ #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 0xc000 - -#define GC_BLACK 0x0000 -#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) - -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; - -#define GC_NUM_ADDITIONAL_ENTRIES \ - ((4096 - ZEND_MM_OVERHEAD - sizeof(void*) * 2) / sizeof(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 -BEGIN_EXTERN_C() -ZEND_API extern int gc_globals_id; -END_EXTERN_C() -#define GC_G(v) ZEND_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 extern int (*gc_collect_cycles)(void); @@ -125,8 +29,9 @@ ZEND_API void ZEND_FASTCALL gc_possible_root(zend_refcounted *ref); ZEND_API void ZEND_FASTCALL gc_remove_from_buffer(zend_refcounted *ref); ZEND_API void gc_globals_ctor(void); ZEND_API void gc_globals_dtor(void); -ZEND_API void gc_init(void); ZEND_API void gc_reset(void); +ZEND_API zend_bool gc_set_enabled(zend_bool enable); +ZEND_API zend_bool gc_enabled(void); /* The default implementation of the gc_collect_cycles callback. */ ZEND_API int zend_gc_collect_cycles(void); @@ -134,7 +39,7 @@ END_EXTERN_C() #define GC_REMOVE_FROM_BUFFER(p) do { \ zend_refcounted *_p = (zend_refcounted*)(p); \ - if (GC_ADDRESS(GC_INFO(_p))) { \ + if (GC_INFO(_p)) { \ gc_remove_from_buffer(_p); \ } \ } while (0) diff --git a/Zend/zend_hash.c b/Zend/zend_hash.c index b4a0d00b724..a33818edefe 100644 --- a/Zend/zend_hash.c +++ b/Zend/zend_hash.c @@ -1363,7 +1363,7 @@ ZEND_API void ZEND_FASTCALL zend_array_destroy(HashTable *ht) /* break possible cycles */ GC_REMOVE_FROM_BUFFER(ht); - GC_TYPE_INFO(ht) = IS_NULL | (GC_WHITE << 16); + GC_TYPE_INFO(ht) = IS_NULL /*???| (GC_WHITE << 16)*/; if (ht->nNumUsed) { /* In some rare cases destructors of regular arrays may be changed */ diff --git a/Zend/zend_types.h b/Zend/zend_types.h index 1f8845a8579..e5ea3687649 100644 --- a/Zend/zend_types.h +++ b/Zend/zend_types.h @@ -210,7 +210,7 @@ typedef struct _zend_refcounted_h { ZEND_ENDIAN_LOHI_3( zend_uchar type, zend_uchar flags, /* used for strings & objects */ - uint16_t gc_info) /* keeps GC root number (or 0) and color */ + uint16_t gc_info) /* keeps GC information, must be initialized by 0 */ } v; uint32_t type_info; } u;