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 | 29 +++++++-- Zend/zend_builtin_functions.c | 2 +- Zend/zend_execute_API.c | 2 +- Zend/zend_gc.c | 110 +++++++++++++++++++++++++++++++--- Zend/zend_gc.h | 101 +------------------------------ Zend/zend_hash.c | 2 +- Zend/zend_types.h | 2 +- 7 files changed, 134 insertions(+), 114 deletions(-) diff --git a/Zend/zend.c b/Zend/zend.c index efe51b7b47..a714f04786 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); - - if (GC_G(gc_enabled)) { - gc_init(); + zend_bool val; + + 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 bd35d90627..37121663b4 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 f66bd66304..7b012005f0 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 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) diff --git a/Zend/zend_gc.h b/Zend/zend_gc.h index f0bc5610cd..f045026ccf 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 b4a0d00b72..a33818edef 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 1f8845a857..e5ea368764 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; -- 2.40.0