Completely hide GC implementation details into zend_gc.c

This commit is contained in:
Dmitry Stogov 2018-02-26 12:49:58 +03:00
parent ef255c9f0f
commit baa9890112
7 changed files with 137 additions and 117 deletions

View File

@ -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)

View File

@ -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());
}
/* }}} */

View File

@ -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

View File

@ -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)

View File

@ -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)

View File

@ -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 */

View File

@ -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;