Skip to content

Instantly share code, notes, and snippets.

@laruence
Last active February 11, 2017 05:38
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save laruence/5c529dd63ed41b7f0621a5858fba662e to your computer and use it in GitHub Desktop.
Save laruence/5c529dd63ed41b7f0621a5858fba662e to your computer and use it in GitHub Desktop.
diff --git a/Zend/zend_gc.c b/Zend/zend_gc.c
index a125c44..8f1255c 100644
--- a/Zend/zend_gc.c
+++ b/Zend/zend_gc.c
@@ -75,26 +75,12 @@
/* one (0) is reserved */
#define GC_ROOT_BUFFER_MAX_ENTRIES 10001
-#define GC_FAKE_BUFFER_FLAG 0x80
-#define GC_TYPE_MASK 0x7f
-
#define GC_HAS_DESTRUCTORS (1<<0)
#ifndef ZEND_GC_DEBUG
# define ZEND_GC_DEBUG 0
#endif
-#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];
-};
-
#ifdef ZTS
ZEND_API int gc_globals_id;
#else
@@ -168,8 +154,10 @@ static zend_always_inline void gc_remove_from_roots(gc_root_buffer *root)
{
root->next->prev = root->prev;
root->prev->next = root->next;
- root->prev = GC_G(unused);
- GC_G(unused) = root;
+ if (EXPECTED(GC_ADDRESS(GC_INFO(root->ref)) < GC_ROOT_BUFFER_MAX_ENTRIES)) {
+ root->prev = GC_G(unused);
+ GC_G(unused) = root;
+ }
GC_BENCH_DEC(root_buf_length);
}
@@ -207,6 +195,7 @@ static void gc_globals_ctor_ex(zend_gc_globals *gc_globals)
gc_globals->zval_remove_from_buffer = 0;
gc_globals->zval_marked_grey = 0;
#endif
+ gc_globals->additional_buf = NULL;
}
ZEND_API void gc_globals_ctor(void)
@@ -326,22 +315,43 @@ ZEND_API void ZEND_FASTCALL gc_possible_root(zend_refcounted *ref)
GC_BENCH_PEAK(root_buf_peak, root_buf_length);
}
+static zend_always_inline gc_root_buffer* gc_find_additional_buf(zend_refcounted *ref)
+{
+ uint32_t i;
+ gc_additional_buffer *additional_buf = GC_G(additional_buf);
+
+ /* We have to traverse the whole additional_buf list
+ * to find which root hold the ref */
+ while (additional_buf) {
+ for (i = 0; i < additional_buf->used; i++) {
+ gc_root_buffer *root = additional_buf->buf + i;
+ if (root->ref == ref) {
+ return root;
+ }
+ }
+ additional_buf = additional_buf->next;
+ }
+
+ ZEND_ASSERT(0);
+ return NULL;
+}
+
ZEND_API void ZEND_FASTCALL gc_remove_from_buffer(zend_refcounted *ref)
{
gc_root_buffer *root;
ZEND_ASSERT(GC_ADDRESS(GC_INFO(ref)));
- ZEND_ASSERT(GC_ADDRESS(GC_INFO(ref)) < GC_ROOT_BUFFER_MAX_ENTRIES);
-
GC_BENCH_INC(zval_remove_from_buffer);
-
- root = GC_G(buf) + GC_ADDRESS(GC_INFO(ref));
- if (GC_REF_GET_COLOR(ref) != GC_BLACK) {
- GC_TRACE_SET_COLOR(ref, GC_PURPLE);
+ if (EXPECTED(GC_ADDRESS(GC_INFO(ref)) < GC_ROOT_BUFFER_MAX_ENTRIES)) {
+ root = GC_G(buf) + GC_ADDRESS(GC_INFO(ref));
+ if (GC_REF_GET_COLOR(ref) != GC_BLACK) {
+ GC_TRACE_SET_COLOR(ref, GC_PURPLE);
+ }
+ } else {
+ root = gc_find_additional_buf(ref);
}
- GC_INFO(ref) = 0;
GC_REMOVE_FROM_ROOTS(root);
-
+ GC_INFO(ref) = 0;
/* updete next root that is going to be freed */
if (GC_G(next_to_free) == root) {
GC_G(next_to_free) = root->next;
@@ -691,7 +701,7 @@ static void gc_scan_roots(void)
}
}
-static void gc_add_garbage(zend_refcounted *ref, gc_additional_buffer **additional_buffer)
+static void gc_add_garbage(zend_refcounted *ref)
{
gc_root_buffer *buf = GC_G(unused);
@@ -717,22 +727,20 @@ static void gc_add_garbage(zend_refcounted *ref, gc_additional_buffer **addition
* set it's address to GC_ROOT_BUFFER_MAX_ENTRIES that have special
* meaning.
*/
- if (!*additional_buffer || (*additional_buffer)->used == GC_NUM_ADDITIONAL_ENTRIES) {
+ if (!GC_G(additional_buf) || GC_G(additional_buf)->used == GC_NUM_ADDITIONAL_ENTRIES) {
gc_additional_buffer *new_buffer = emalloc(sizeof(gc_additional_buffer));
new_buffer->used = 0;
- new_buffer->next = *additional_buffer;
- *additional_buffer = new_buffer;
+ new_buffer->next = GC_G(additional_buf);
+ GC_G(additional_buf) = new_buffer;
}
- buf = (*additional_buffer)->buf + (*additional_buffer)->used;
- (*additional_buffer)->used++;
+ buf = GC_G(additional_buf)->buf + GC_G(additional_buf)->used;
+ GC_G(additional_buf)->used++;
#if 1
/* optimization: color is already GC_BLACK (0) */
GC_INFO(ref) = GC_ROOT_BUFFER_MAX_ENTRIES;
#else
GC_REF_SET_ADDRESS(ref, GC_ROOT_BUFFER_MAX_ENTRIES);
#endif
- /* modify type to prevent indirect destruction */
- GC_TYPE(ref) |= GC_FAKE_BUFFER_FLAG;
}
if (buf) {
buf->ref = ref;
@@ -743,7 +751,7 @@ static void gc_add_garbage(zend_refcounted *ref, gc_additional_buffer **addition
}
}
-static int gc_collect_white(zend_refcounted *ref, uint32_t *flags, gc_additional_buffer **additional_buffer)
+static int gc_collect_white(zend_refcounted *ref, uint32_t *flags)
{
int count = 0;
HashTable *ht;
@@ -776,7 +784,7 @@ tail_call:
#else
if (!GC_ADDRESS(GC_INFO(ref))) {
#endif
- gc_add_garbage(ref, additional_buffer);
+ gc_add_garbage(ref);
}
if (obj->handlers->dtor_obj &&
((obj->handlers->dtor_obj != zend_objects_destroy_object) ||
@@ -800,7 +808,7 @@ tail_call:
if (Z_REFCOUNTED_P(zv)) {
ref = Z_COUNTED_P(zv);
GC_REFCOUNT(ref)++;
- count += gc_collect_white(ref, flags, additional_buffer);
+ count += gc_collect_white(ref, flags);
/* count non-refcounted for compatibility ??? */
} else if (Z_TYPE_P(zv) != IS_UNDEF) {
count++;
@@ -822,7 +830,7 @@ tail_call:
#else
if (!GC_ADDRESS(GC_INFO(ref))) {
#endif
- gc_add_garbage(ref, additional_buffer);
+ gc_add_garbage(ref);
}
ht = (zend_array*)ref;
} else if (GC_TYPE(ref) == IS_REFERENCE) {
@@ -862,7 +870,7 @@ tail_call:
if (Z_REFCOUNTED_P(zv)) {
ref = Z_COUNTED_P(zv);
GC_REFCOUNT(ref)++;
- count += gc_collect_white(ref, flags, additional_buffer);
+ count += gc_collect_white(ref, flags);
/* count non-refcounted for compatibility ??? */
} else if (Z_TYPE_P(zv) != IS_UNDEF) {
count++;
@@ -880,7 +888,7 @@ tail_call:
return count;
}
-static int gc_collect_roots(uint32_t *flags, gc_additional_buffer **additional_buffer)
+static int gc_collect_roots(uint32_t *flags)
{
int count = 0;
gc_root_buffer *current = GC_G(roots).next;
@@ -889,8 +897,8 @@ static int gc_collect_roots(uint32_t *flags, gc_additional_buffer **additional_b
while (current != &GC_G(roots)) {
gc_root_buffer *next = current->next;
if (GC_REF_GET_COLOR(current->ref) == GC_BLACK) {
- GC_INFO(current->ref) = 0; /* reset GC_ADDRESS() and keep GC_BLACK */
GC_REMOVE_FROM_ROOTS(current);
+ GC_INFO(current->ref) = 0; /* reset GC_ADDRESS() and keep GC_BLACK */
}
current = next;
}
@@ -898,7 +906,7 @@ static int gc_collect_roots(uint32_t *flags, gc_additional_buffer **additional_b
current = GC_G(roots).next;
while (current != &GC_G(roots)) {
if (GC_REF_GET_COLOR(current->ref) == GC_WHITE) {
- count += gc_collect_white(current->ref, flags, additional_buffer);
+ count += gc_collect_white(current->ref, flags);
}
current = current->next;
}
@@ -934,12 +942,11 @@ static void gc_remove_nested_data_from_buffer(zend_refcounted *ref, gc_root_buff
tail_call:
if (root ||
(GC_ADDRESS(GC_INFO(ref)) != 0 &&
- GC_REF_GET_COLOR(ref) == GC_BLACK &&
- GC_ADDRESS(GC_INFO(ref)) != GC_ROOT_BUFFER_MAX_ENTRIES)) {
+ GC_REF_GET_COLOR(ref) == GC_BLACK)) {
GC_TRACE_REF(ref, "removing from buffer");
if (root) {
- GC_INFO(ref) = 0;
GC_REMOVE_FROM_ROOTS(root);
+ GC_INFO(ref) = 0;
root = NULL;
} else {
GC_REMOVE_FROM_BUFFER(ref);
@@ -1032,8 +1039,8 @@ ZEND_API int zend_gc_collect_cycles(void)
gc_root_buffer *current, *next, *orig_next_to_free;
zend_refcounted *p;
gc_root_buffer to_free;
+ gc_additional_buffer *additional_buf_snapshot;
uint32_t gc_flags = 0;
- gc_additional_buffer *additional_buffer;
#if ZEND_GC_DEBUG
zend_bool orig_gc_full;
#endif
@@ -1057,8 +1064,8 @@ ZEND_API int zend_gc_collect_cycles(void)
#endif
GC_TRACE("Collecting roots");
- additional_buffer = NULL;
- count = gc_collect_roots(&gc_flags, &additional_buffer);
+ additional_buf_snapshot = GC_G(additional_buf);
+ count = gc_collect_roots(&gc_flags);
#if ZEND_GC_DEBUG
GC_G(gc_full) = orig_gc_full;
#endif
@@ -1102,7 +1109,7 @@ ZEND_API int zend_gc_collect_cycles(void)
while (current != &to_free) {
p = current->ref;
GC_G(next_to_free) = current->next;
- if ((GC_TYPE(p) & GC_TYPE_MASK) == IS_OBJECT) {
+ if (GC_TYPE(p) == IS_OBJECT) {
zend_object *obj = (zend_object*)p;
if (IS_OBJ_VALID(EG(objects_store).object_buckets[obj->handle]) &&
@@ -1139,7 +1146,7 @@ ZEND_API int zend_gc_collect_cycles(void)
p = current->ref;
GC_G(next_to_free) = current->next;
GC_TRACE_REF(p, "destroying");
- if ((GC_TYPE(p) & GC_TYPE_MASK) == IS_OBJECT) {
+ if (GC_TYPE(p) == IS_OBJECT) {
zend_object *obj = (zend_object*)p;
if (EG(objects_store).object_buckets &&
@@ -1158,7 +1165,7 @@ ZEND_API int zend_gc_collect_cycles(void)
EG(objects_store).free_list_head = obj->handle;
p = current->ref = (zend_refcounted*)(((char*)obj) - obj->handlers->offset);
}
- } else if ((GC_TYPE(p) & GC_TYPE_MASK) == IS_ARRAY) {
+ } else if (GC_TYPE(p) == IS_ARRAY) {
zend_array *arr = (zend_array*)p;
GC_TYPE(arr) = IS_NULL;
@@ -1180,13 +1187,14 @@ ZEND_API int zend_gc_collect_cycles(void)
current = next;
}
- while (additional_buffer != NULL) {
- gc_additional_buffer *next = additional_buffer->next;
- efree(additional_buffer);
- additional_buffer = next;
+ while (GC_G(additional_buf) != additional_buf_snapshot) {
+ gc_additional_buffer *next = GC_G(additional_buf)->next;
+ efree(GC_G(additional_buf));
+ GC_G(additional_buf) = next;
}
GC_TRACE("Collection finished");
+ GC_G(additional_buf) = additional_buf_snapshot;
GC_G(collected) += count;
GC_G(next_to_free) = orig_next_to_free;
#if ZEND_GC_DEBUG
diff --git a/Zend/zend_gc.h b/Zend/zend_gc.h
index 8e3bc46..1c6aab9 100644
--- a/Zend/zend_gc.h
+++ b/Zend/zend_gc.h
@@ -67,6 +67,15 @@ typedef struct _gc_root_buffer {
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_buffer {
+ uint32_t used;
+ struct _gc_additional_buffer *next;
+ gc_root_buffer buf[GC_NUM_ADDITIONAL_ENTRIES];
+} gc_additional_buffer;
+
typedef struct _zend_gc_globals {
zend_bool gc_enabled;
zend_bool gc_active;
@@ -92,7 +101,7 @@ typedef struct _zend_gc_globals {
uint32_t zval_remove_from_buffer;
uint32_t zval_marked_grey;
#endif
-
+ gc_additional_buffer *additional_buf;
} zend_gc_globals;
#ifdef ZTS
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment