PHP  
 PHP: Test and Code Coverage Analysis
downloads | QA | documentation | faq | getting help | mailing lists | reporting bugs | php.net sites | links | my php.net 
 

LCOV - code coverage report
Current view: top level - Zend - zend_gc.c (source / functions) Hit Total Coverage
Test: PHP Code Coverage Lines: 531 581 91.4 %
Date: 2016-04-27 Functions: 18 18 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*
       2             :    +----------------------------------------------------------------------+
       3             :    | Zend Engine                                                          |
       4             :    +----------------------------------------------------------------------+
       5             :    | Copyright (c) 1998-2016 Zend Technologies Ltd. (http://www.zend.com) |
       6             :    +----------------------------------------------------------------------+
       7             :    | This source file is subject to version 2.00 of the Zend license,     |
       8             :    | that is bundled with this package in the file LICENSE, and is        |
       9             :    | available through the world-wide-web at the following url:           |
      10             :    | http://www.zend.com/license/2_00.txt.                                |
      11             :    | If you did not receive a copy of the Zend license and are unable to  |
      12             :    | obtain it through the world-wide-web, please send a note to          |
      13             :    | license@zend.com so we can mail you a copy immediately.              |
      14             :    +----------------------------------------------------------------------+
      15             :    | Authors: David Wang <planetbeing@gmail.com>                          |
      16             :    |          Dmitry Stogov <dmitry@zend.com>                             |
      17             :    +----------------------------------------------------------------------+
      18             : */
      19             : 
      20             : /* $Id$ */
      21             : 
      22             : #include "zend.h"
      23             : #include "zend_API.h"
      24             : 
      25             : /* one (0) is reserved */
      26             : #define GC_ROOT_BUFFER_MAX_ENTRIES 10001
      27             : 
      28             : #define GC_FAKE_BUFFER_FLAG 0x80
      29             : #define GC_TYPE_MASK        0x7f
      30             : 
      31             : #define GC_HAS_DESTRUCTORS  (1<<0)
      32             : 
      33             : #ifndef ZEND_GC_DEBUG
      34             : # define ZEND_GC_DEBUG 0
      35             : #endif
      36             : 
      37             : #define GC_NUM_ADDITIONAL_ENTRIES \
      38             :         ((4096 - ZEND_MM_OVERHEAD - sizeof(void*) * 2) / sizeof(gc_root_buffer))
      39             : 
      40             : typedef struct _gc_addtional_bufer gc_additional_buffer;
      41             : 
      42             : struct _gc_addtional_bufer {
      43             :         uint32_t              used;
      44             :         gc_additional_buffer *next;
      45             :         gc_root_buffer        buf[GC_NUM_ADDITIONAL_ENTRIES];
      46             : };
      47             : 
      48             : #ifdef ZTS
      49             : ZEND_API int gc_globals_id;
      50             : #else
      51             : ZEND_API zend_gc_globals gc_globals;
      52             : #endif
      53             : 
      54             : ZEND_API int (*gc_collect_cycles)(void);
      55             : 
      56             : #define GC_REMOVE_FROM_ROOTS(current) \
      57             :         gc_remove_from_roots((current))
      58             : 
      59             : #if ZEND_GC_DEBUG > 1
      60             : # define GC_TRACE(format, ...) fprintf(stderr, format "\n", ##__VA_ARGS__);
      61             : # define GC_TRACE_REF(ref, format, ...) \
      62             :         do { \
      63             :                 gc_trace_ref((zend_refcounted *) ref); \
      64             :                 fprintf(stderr, format "\n", ##__VA_ARGS__); \
      65             :         } while (0)
      66             : # define GC_TRACE_SET_COLOR(ref, color) \
      67             :         GC_TRACE_REF(ref, "->%s", gc_color_name(color))
      68             : #else
      69             : # define GC_TRACE_REF(ref, format, ...)
      70             : # define GC_TRACE_SET_COLOR(ref, new_color)
      71             : # define GC_TRACE(str)
      72             : #endif
      73             : 
      74             : #define GC_REF_SET_ADDRESS(ref, a) \
      75             :         GC_INFO_SET_ADDRESS(GC_INFO(ref), a)
      76             : #define GC_REF_GET_COLOR(ref) \
      77             :         GC_INFO_GET_COLOR(GC_INFO(ref))
      78             : #define GC_REF_SET_COLOR(ref, c) \
      79             :         do { GC_TRACE_SET_COLOR(ref, c); GC_INFO_SET_COLOR(GC_INFO(ref), c); } while (0)
      80             : #define GC_REF_SET_BLACK(ref) \
      81             :         do { GC_TRACE_SET_COLOR(ref, GC_BLACK); GC_INFO_SET_BLACK(GC_INFO(ref)); } while (0)
      82             : #define GC_REF_SET_PURPLE(ref) \
      83             :         do { GC_TRACE_SET_COLOR(ref, GC_PURPLE); GC_INFO_SET_PURPLE(GC_INFO(ref)); } while (0)
      84             : 
      85             : #if ZEND_GC_DEBUG > 1
      86             : static const char *gc_color_name(uint32_t color) {
      87             :         switch (color) {
      88             :                 case GC_BLACK: return "black";
      89             :                 case GC_WHITE: return "white";
      90             :                 case GC_GREY: return "grey";
      91             :                 case GC_PURPLE: return "purple";
      92             :                 default: return "unknown";
      93             :         }
      94             : }
      95             : static void gc_trace_ref(zend_refcounted *ref) {
      96             :         if (GC_TYPE(ref) == IS_OBJECT) {
      97             :                 zend_object *obj = (zend_object *) ref;
      98             :                 fprintf(stderr, "[%p] rc=%d addr=%d %s object(%s)#%d ",
      99             :                         ref, GC_REFCOUNT(ref), GC_ADDRESS(GC_INFO(ref)),
     100             :                         gc_color_name(GC_REF_GET_COLOR(ref)),
     101             :                         obj->ce->name->val, obj->handle);
     102             :         } else if (GC_TYPE(ref) == IS_ARRAY) {
     103             :                 zend_array *arr = (zend_array *) ref;
     104             :                 fprintf(stderr, "[%p] rc=%d addr=%d %s array(%d) ",
     105             :                         ref, GC_REFCOUNT(ref), GC_ADDRESS(GC_INFO(ref)),
     106             :                         gc_color_name(GC_REF_GET_COLOR(ref)),
     107             :                         zend_hash_num_elements(arr));
     108             :         } else {
     109             :                 fprintf(stderr, "[%p] rc=%d addr=%d %s %s ",
     110             :                         ref, GC_REFCOUNT(ref), GC_ADDRESS(GC_INFO(ref)),
     111             :                         gc_color_name(GC_REF_GET_COLOR(ref)),
     112             :                         zend_get_type_by_const(GC_TYPE(ref)));
     113             :         }
     114             : }
     115             : #endif
     116             : 
     117             : static zend_always_inline void gc_remove_from_roots(gc_root_buffer *root)
     118             : {
     119     1583110 :         root->next->prev = root->prev;
     120     1583110 :         root->prev->next = root->next;
     121     1583110 :         root->prev = GC_G(unused);
     122     1583110 :         GC_G(unused) = root;
     123             :         GC_BENCH_DEC(root_buf_length);
     124             : }
     125             : 
     126       22623 : static void root_buffer_dtor(zend_gc_globals *gc_globals)
     127             : {
     128       22623 :         if (gc_globals->buf) {
     129       22622 :                 free(gc_globals->buf);
     130       22622 :                 gc_globals->buf = NULL;
     131             :         }
     132       22623 : }
     133             : 
     134       22587 : static void gc_globals_ctor_ex(zend_gc_globals *gc_globals)
     135             : {
     136       22587 :         gc_globals->gc_enabled = 0;
     137       22587 :         gc_globals->gc_active = 0;
     138             : 
     139       22587 :         gc_globals->buf = NULL;
     140             : 
     141       22587 :         gc_globals->roots.next = &gc_globals->roots;
     142       22587 :         gc_globals->roots.prev = &gc_globals->roots;
     143       22587 :         gc_globals->unused = NULL;
     144       22587 :         gc_globals->next_to_free = NULL;
     145             : 
     146       22587 :         gc_globals->to_free.next = &gc_globals->to_free;
     147       22587 :         gc_globals->to_free.prev = &gc_globals->to_free;
     148             : 
     149       22587 :         gc_globals->gc_runs = 0;
     150       22587 :         gc_globals->collected = 0;
     151             : 
     152             : #if GC_BENCH
     153             :         gc_globals->root_buf_length = 0;
     154             :         gc_globals->root_buf_peak = 0;
     155             :         gc_globals->zval_possible_root = 0;
     156             :         gc_globals->zval_buffered = 0;
     157             :         gc_globals->zval_remove_from_buffer = 0;
     158             :         gc_globals->zval_marked_grey = 0;
     159             : #endif
     160       22587 : }
     161             : 
     162       22587 : ZEND_API void gc_globals_ctor(void)
     163             : {
     164             : #ifdef ZTS
     165             :         ts_allocate_id(&gc_globals_id, sizeof(zend_gc_globals), (ts_allocate_ctor) gc_globals_ctor_ex, (ts_allocate_dtor) root_buffer_dtor);
     166             : #else
     167       22587 :         gc_globals_ctor_ex(&gc_globals);
     168             : #endif
     169       22587 : }
     170             : 
     171       22623 : ZEND_API void gc_globals_dtor(void)
     172             : {
     173             : #ifndef ZTS
     174       22623 :         root_buffer_dtor(&gc_globals);
     175             : #endif
     176       22623 : }
     177             : 
     178       45127 : ZEND_API void gc_reset(void)
     179             : {
     180       45127 :         GC_G(gc_runs) = 0;
     181       45127 :         GC_G(collected) = 0;
     182       45127 :         GC_G(gc_full) = 0;
     183             : 
     184             : #if GC_BENCH
     185             :         GC_G(root_buf_length) = 0;
     186             :         GC_G(root_buf_peak) = 0;
     187             :         GC_G(zval_possible_root) = 0;
     188             :         GC_G(zval_buffered) = 0;
     189             :         GC_G(zval_remove_from_buffer) = 0;
     190             :         GC_G(zval_marked_grey) = 0;
     191             : #endif
     192             : 
     193       45127 :         GC_G(roots).next = &GC_G(roots);
     194       45127 :         GC_G(roots).prev = &GC_G(roots);
     195             : 
     196       45127 :         GC_G(to_free).next = &GC_G(to_free);
     197       45127 :         GC_G(to_free).prev = &GC_G(to_free);
     198             : 
     199       45127 :         if (GC_G(buf)) {
     200       45125 :                 GC_G(unused) = NULL;
     201       45125 :                 GC_G(first_unused) = GC_G(buf) + 1;
     202             :         } else {
     203           2 :                 GC_G(unused) = NULL;
     204           2 :                 GC_G(first_unused) = NULL;
     205           2 :                 GC_G(last_unused) = NULL;
     206             :         }
     207       45127 : }
     208             : 
     209       22601 : ZEND_API void gc_init(void)
     210             : {
     211       22601 :         if (GC_G(buf) == NULL && GC_G(gc_enabled)) {
     212       22586 :                 GC_G(buf) = (gc_root_buffer*) malloc(sizeof(gc_root_buffer) * GC_ROOT_BUFFER_MAX_ENTRIES);
     213       22586 :                 GC_G(last_unused) = &GC_G(buf)[GC_ROOT_BUFFER_MAX_ENTRIES];
     214       22586 :                 gc_reset();
     215             :         }
     216       22601 : }
     217             : 
     218     1759531 : ZEND_API void ZEND_FASTCALL gc_possible_root(zend_refcounted *ref)
     219             : {
     220             :         gc_root_buffer *newRoot;
     221             : 
     222     1759531 :         if (UNEXPECTED(CG(unclean_shutdown)) || UNEXPECTED(GC_G(gc_active))) {
     223       11080 :                 return;
     224             :         }
     225             : 
     226             :         ZEND_ASSERT(GC_TYPE(ref) == IS_ARRAY || GC_TYPE(ref) == IS_OBJECT);
     227             :         ZEND_ASSERT(EXPECTED(GC_REF_GET_COLOR(ref) == GC_BLACK));
     228             :         ZEND_ASSERT(!GC_ADDRESS(GC_INFO(ref)));
     229             : 
     230             :         GC_BENCH_INC(zval_possible_root);
     231             : 
     232     1748451 :         newRoot = GC_G(unused);
     233     1748451 :         if (newRoot) {
     234     1478135 :                 GC_G(unused) = newRoot->prev;
     235      270316 :         } else if (GC_G(first_unused) != GC_G(last_unused)) {
     236      270275 :                 newRoot = GC_G(first_unused);
     237      270275 :                 GC_G(first_unused)++;
     238             :         } else {
     239          41 :                 if (!GC_G(gc_enabled)) {
     240           8 :                         return;
     241             :                 }
     242          33 :                 GC_REFCOUNT(ref)++;
     243          33 :                 gc_collect_cycles();
     244          33 :                 GC_REFCOUNT(ref)--;
     245          33 :                 if (UNEXPECTED(GC_REFCOUNT(ref)) == 0) {
     246           0 :                         zval_dtor_func_for_ptr(ref);
     247           0 :                         return;
     248             :                 }
     249          33 :                 if (UNEXPECTED(GC_INFO(ref))) {
     250           0 :                         return;
     251             :                 }
     252          33 :                 newRoot = GC_G(unused);
     253          33 :                 if (!newRoot) {
     254             : #if ZEND_GC_DEBUG
     255             :                         if (!GC_G(gc_full)) {
     256             :                                 fprintf(stderr, "GC: no space to record new root candidate\n");
     257             :                                 GC_G(gc_full) = 1;
     258             :                         }
     259             : #endif
     260           0 :                         return;
     261             :                 }
     262          33 :                 GC_G(unused) = newRoot->prev;
     263             :         }
     264             : 
     265             :         GC_TRACE_SET_COLOR(ref, GC_PURPLE);
     266     1748443 :         GC_INFO(ref) = (newRoot - GC_G(buf)) | GC_PURPLE;
     267     1748443 :         newRoot->ref = ref;
     268             : 
     269     1748443 :         newRoot->next = GC_G(roots).next;
     270     1748443 :         newRoot->prev = &GC_G(roots);
     271     1748443 :         GC_G(roots).next->prev = newRoot;
     272     1748443 :         GC_G(roots).next = newRoot;
     273             : 
     274             :         GC_BENCH_INC(zval_buffered);
     275             :         GC_BENCH_INC(root_buf_length);
     276             :         GC_BENCH_PEAK(root_buf_peak, root_buf_length);
     277             : }
     278             : 
     279     1386304 : ZEND_API void ZEND_FASTCALL gc_remove_from_buffer(zend_refcounted *ref)
     280             : {
     281             :         gc_root_buffer *root;
     282             : 
     283             :         ZEND_ASSERT(GC_ADDRESS(GC_INFO(ref)));
     284             :         ZEND_ASSERT(GC_ADDRESS(GC_INFO(ref)) < GC_ROOT_BUFFER_MAX_ENTRIES);
     285             : 
     286             :         GC_BENCH_INC(zval_remove_from_buffer);
     287             : 
     288     1386304 :         root = GC_G(buf) + GC_ADDRESS(GC_INFO(ref));
     289     1386304 :         if (GC_REF_GET_COLOR(ref) != GC_BLACK) {
     290             :                 GC_TRACE_SET_COLOR(ref, GC_PURPLE);
     291             :         }
     292     1386304 :         GC_INFO(ref) = 0;
     293             :         GC_REMOVE_FROM_ROOTS(root);
     294             : 
     295             :         /* updete next root that is going to be freed */
     296     1386304 :         if (GC_G(next_to_free) == root) {
     297           2 :                 GC_G(next_to_free) = root->next;
     298             :         }
     299     1386304 : }
     300             : 
     301      321620 : static void gc_scan_black(zend_refcounted *ref)
     302             : {
     303             :         HashTable *ht;
     304             :         Bucket *p, *end;
     305             :         zval *zv;
     306             : 
     307             : tail_call:
     308      321620 :         ht = NULL;
     309      321620 :         GC_REF_SET_BLACK(ref);
     310             : 
     311      501630 :         if (GC_TYPE(ref) == IS_OBJECT && !(GC_FLAGS(ref) & IS_OBJ_FREE_CALLED)) {
     312             :                 zend_object_get_gc_t get_gc;
     313      206749 :                 zend_object *obj = (zend_object*)ref;
     314             : 
     315      386759 :                 if (EXPECTED(IS_OBJ_VALID(EG(objects_store).object_buckets[obj->handle]) &&
     316             :                              (get_gc = obj->handlers->get_gc) != NULL)) {
     317             :                         int n;
     318             :                         zval *zv, *end;
     319             :                         zval tmp;
     320             : 
     321      206749 :                         ZVAL_OBJ(&tmp, obj);
     322      206749 :                         ht = get_gc(&tmp, &zv, &n);
     323      206749 :                         end = zv + n;
     324      206749 :                         if (EXPECTED(!ht)) {
     325       26739 :                                 if (!n) return;
     326       29477 :                                 while (!Z_REFCOUNTED_P(--end)) {
     327           1 :                                         if (zv == end) return;
     328             :                                 }
     329             :                         }
     330      484238 :                         while (zv != end) {
     331       94742 :                                 if (Z_REFCOUNTED_P(zv)) {
     332       40003 :                                         ref = Z_COUNTED_P(zv);
     333       40003 :                                         GC_REFCOUNT(ref)++;
     334       40003 :                                         if (GC_REF_GET_COLOR(ref) != GC_BLACK) {
     335       10006 :                                                 gc_scan_black(ref);
     336             :                                         }
     337             :                                 }
     338       94742 :                                 zv++;
     339             :                         }
     340      194748 :                         if (EXPECTED(!ht)) {
     341       14738 :                                 ref = Z_COUNTED_P(zv);
     342       14738 :                                 GC_REFCOUNT(ref)++;
     343       14738 :                                 if (GC_REF_GET_COLOR(ref) != GC_BLACK) {
     344       14738 :                                         goto tail_call;
     345             :                                 }
     346           0 :                                 return;
     347             :                         }
     348             :                 } else {
     349           0 :                         return;
     350             :                 }
     351      114871 :         } else if (GC_TYPE(ref) == IS_ARRAY) {
     352       14809 :                 if ((zend_array*)ref != &EG(symbol_table)) {
     353       14809 :                         ht = (zend_array*)ref;
     354             :                 } else {
     355           0 :                         return;
     356             :                 }
     357      100062 :         } else if (GC_TYPE(ref) == IS_REFERENCE) {
     358           4 :                 if (Z_REFCOUNTED(((zend_reference*)ref)->val)) {
     359           3 :                         ref = Z_COUNTED(((zend_reference*)ref)->val);
     360           3 :                         GC_REFCOUNT(ref)++;
     361           3 :                         if (GC_REF_GET_COLOR(ref) != GC_BLACK) {
     362           3 :                                 goto tail_call;
     363             :                         }
     364             :                 }
     365           1 :                 return;
     366             :         } else {
     367      100058 :                 return;
     368             :         }
     369             : 
     370      194819 :         if (!ht->nNumUsed) return;
     371       74811 :         p = ht->arData;
     372       74811 :         end = p + ht->nNumUsed;
     373             :         while (1) {
     374       84815 :                 end--;
     375       84815 :                 zv = &end->val;
     376       84815 :                 if (Z_TYPE_P(zv) == IS_INDIRECT) {
     377       10004 :                         zv = Z_INDIRECT_P(zv);
     378             :                 }
     379       84815 :                 if (Z_REFCOUNTED_P(zv)) {
     380       64806 :                         break;
     381             :                 }
     382       20009 :                 if (p == end) return;
     383       10004 :         }
     384      224343 :         while (p != end) {
     385       94731 :                 zv = &p->val;
     386       94731 :                 if (Z_TYPE_P(zv) == IS_INDIRECT) {
     387           0 :                         zv = Z_INDIRECT_P(zv);
     388             :                 }
     389       94731 :                 if (Z_REFCOUNTED_P(zv)) {
     390       84734 :                         ref = Z_COUNTED_P(zv);
     391       84734 :                         GC_REFCOUNT(ref)++;
     392       84734 :                         if (GC_REF_GET_COLOR(ref) != GC_BLACK) {
     393       83793 :                                 gc_scan_black(ref);
     394             :                         }
     395             :                 }
     396       94731 :                 p++;
     397             :         }
     398       64806 :         zv = &p->val;
     399       64806 :         if (Z_TYPE_P(zv) == IS_INDIRECT) {
     400           2 :                 zv = Z_INDIRECT_P(zv);
     401             :         }
     402       64806 :         ref = Z_COUNTED_P(zv);
     403       64806 :         GC_REFCOUNT(ref)++;
     404       64806 :         if (GC_REF_GET_COLOR(ref) != GC_BLACK) {
     405       50977 :                 goto tail_call;
     406             :         }
     407             : }
     408             : 
     409      974266 : static void gc_mark_grey(zend_refcounted *ref)
     410             : {
     411             :     HashTable *ht;
     412             :         Bucket *p, *end;
     413             :         zval *zv;
     414             : 
     415             : tail_call:
     416      974266 :         if (GC_REF_GET_COLOR(ref) != GC_GREY) {
     417      650971 :                 ht = NULL;
     418             :                 GC_BENCH_INC(zval_marked_grey);
     419      650971 :                 GC_REF_SET_COLOR(ref, GC_GREY);
     420             : 
     421      841007 :                 if (GC_TYPE(ref) == IS_OBJECT && !(GC_FLAGS(ref) & IS_OBJ_FREE_CALLED)) {
     422             :                         zend_object_get_gc_t get_gc;
     423      295438 :                         zend_object *obj = (zend_object*)ref;
     424             : 
     425      485474 :                         if (EXPECTED(IS_OBJ_VALID(EG(objects_store).object_buckets[obj->handle]) &&
     426             :                              (get_gc = obj->handlers->get_gc) != NULL)) {
     427             :                                 int n;
     428             :                                 zval *zv, *end;
     429             :                                 zval tmp;
     430             : 
     431      295438 :                                 ZVAL_OBJ(&tmp, obj);
     432      295438 :                                 ht = get_gc(&tmp, &zv, &n);
     433      295438 :                                 end = zv + n;
     434      295438 :                                 if (EXPECTED(!ht)) {
     435      105402 :                                         if (!n) return;
     436      186796 :                                         while (!Z_REFCOUNTED_P(--end)) {
     437           2 :                                                 if (zv == end) return;
     438             :                                         }
     439             :                                 }
     440      740264 :                                 while (zv != end) {
     441      173398 :                                         if (Z_REFCOUNTED_P(zv)) {
     442       40013 :                                                 ref = Z_COUNTED_P(zv);
     443       40013 :                                                 GC_REFCOUNT(ref)--;
     444       40013 :                                                 gc_mark_grey(ref);
     445             :                                         }
     446      173398 :                                         zv++;
     447             :                                 }
     448      283433 :                                 if (EXPECTED(!ht)) {
     449       93397 :                                         ref = Z_COUNTED_P(zv);
     450       93397 :                                         GC_REFCOUNT(ref)--;
     451       93397 :                                         goto tail_call;
     452             :                                 }
     453             :                         } else {
     454           0 :                                 return;
     455             :                         }
     456      355533 :                 } else if (GC_TYPE(ref) == IS_ARRAY) {
     457      174464 :                         if (((zend_array*)ref) == &EG(symbol_table)) {
     458           0 :                                 GC_REF_SET_BLACK(ref);
     459           0 :                                 return;
     460             :                         } else {
     461      174464 :                                 ht = (zend_array*)ref;
     462             :                         }
     463      181069 :                 } else if (GC_TYPE(ref) == IS_REFERENCE) {
     464       81009 :                         if (Z_REFCOUNTED(((zend_reference*)ref)->val)) {
     465       81008 :                                 if (UNEXPECTED(!EG(objects_store).object_buckets) &&
     466           0 :                                         Z_TYPE(((zend_reference*)ref)->val) == IS_OBJECT) {
     467           0 :                                         Z_TYPE_INFO(((zend_reference*)ref)->val) = IS_NULL;
     468           0 :                                         return;
     469             :                                 }
     470       81008 :                                 ref = Z_COUNTED(((zend_reference*)ref)->val);
     471       81008 :                                 GC_REFCOUNT(ref)--;
     472       81008 :                                 goto tail_call;
     473             :                         }
     474           1 :                         return;
     475             :                 } else {
     476      100060 :                         return;
     477             :                 }
     478             : 
     479      364500 :                 if (!ht->nNumUsed) return;
     480      244486 :                 p = ht->arData;
     481      244486 :                 end = p + ht->nNumUsed;
     482             :                 while (1) {
     483      274489 :                         end--;
     484      274489 :                         zv = &end->val;
     485      274489 :                         if (Z_TYPE_P(zv) == IS_INDIRECT) {
     486       20006 :                                 zv = Z_INDIRECT_P(zv);
     487             :                         }
     488      274489 :                         if (Z_REFCOUNTED_P(zv)) {
     489      224483 :                                 break;
     490             :                         }
     491       50006 :                         if (p == end) return;
     492       30003 :                 }
     493      642333 :                 while (p != end) {
     494      193367 :                         zv = &p->val;
     495      193367 :                         if (Z_TYPE_P(zv) == IS_INDIRECT) {
     496           0 :                                 zv = Z_INDIRECT_P(zv);
     497             :                         }
     498      193367 :                         if (Z_REFCOUNTED_P(zv)) {
     499      306734 :                                 if (Z_TYPE_P(zv) == IS_OBJECT &&
     500      123364 :                                     UNEXPECTED(!EG(objects_store).object_buckets)) {
     501           0 :                                         Z_TYPE_INFO_P(zv) = IS_NULL;
     502             :                                 } else {
     503      183370 :                                         ref = Z_COUNTED_P(zv);
     504      183370 :                                         GC_REFCOUNT(ref)--;
     505      183370 :                                         gc_mark_grey(ref);
     506             :                                 }
     507             :                         }
     508      193367 :                         p++;
     509             :                 }
     510      224483 :                 zv = &p->val;
     511      224483 :                 if (Z_TYPE_P(zv) == IS_INDIRECT) {
     512           7 :                         zv = Z_INDIRECT_P(zv);
     513             :                 }
     514      317895 :                 if (Z_TYPE_P(zv) == IS_OBJECT &&
     515       93412 :                     UNEXPECTED(!EG(objects_store).object_buckets)) {
     516           0 :                         Z_TYPE_INFO_P(zv) = IS_NULL;
     517             :                 } else {
     518      224483 :                         ref = Z_COUNTED_P(zv);
     519      224483 :                         GC_REFCOUNT(ref)--;
     520      224483 :                         goto tail_call;
     521             :                 }
     522             :         }
     523             : }
     524             : 
     525          91 : static void gc_mark_roots(void)
     526             : {
     527          91 :         gc_root_buffer *current = GC_G(roots).next;
     528             : 
     529      353294 :         while (current != &GC_G(roots)) {
     530      353112 :                 if (GC_REF_GET_COLOR(current->ref) == GC_PURPLE) {
     531      351995 :                         gc_mark_grey(current->ref);
     532             :                 }
     533      353112 :                 current = current->next;
     534             :         }
     535          91 : }
     536             : 
     537      815027 : static void gc_scan(zend_refcounted *ref)
     538             : {
     539             :     HashTable *ht;
     540             :         Bucket *p, *end;
     541             :         zval *zv;
     542             : 
     543             : tail_call:
     544      815027 :         if (GC_REF_GET_COLOR(ref) == GC_GREY) {
     545      540852 :                 if (GC_REFCOUNT(ref) > 0) {
     546      162103 :                         gc_scan_black(ref);
     547             :                 } else {
     548      378749 :                         GC_REF_SET_COLOR(ref, GC_WHITE);
     549      408774 :                         if (GC_TYPE(ref) == IS_OBJECT && !(GC_FLAGS(ref) & IS_OBJ_FREE_CALLED)) {
     550             :                                 zend_object_get_gc_t get_gc;
     551      123385 :                                 zend_object *obj = (zend_object*)ref;
     552             : 
     553      153410 :                                 if (EXPECTED(IS_OBJ_VALID(EG(objects_store).object_buckets[obj->handle]) &&
     554             :                                              (get_gc = obj->handlers->get_gc) != NULL)) {
     555             :                                         int n;
     556             :                                         zval *zv, *end;
     557             :                                         zval tmp;
     558             : 
     559      123385 :                                         ZVAL_OBJ(&tmp, obj);
     560      123385 :                                         ht = get_gc(&tmp, &zv, &n);
     561      123385 :                                         end = zv + n;
     562      123385 :                                         if (EXPECTED(!ht)) {
     563       93360 :                                                 if (!n) return;
     564      186713 :                                                 while (!Z_REFCOUNTED_P(--end)) {
     565           1 :                                                         if (zv == end) return;
     566             :                                                 }
     567             :                                         }
     568      340115 :                                         while (zv != end) {
     569       93353 :                                                 if (Z_REFCOUNTED_P(zv)) {
     570          10 :                                                         ref = Z_COUNTED_P(zv);
     571          10 :                                                         gc_scan(ref);
     572             :                                                 }
     573       93353 :                                                 zv++;
     574             :                                         }
     575      123381 :                                         if (EXPECTED(!ht)) {
     576       93356 :                                                 ref = Z_COUNTED_P(zv);
     577       93356 :                                                 goto tail_call;
     578             :                                         }
     579             :                                 } else {
     580           0 :                                         return;
     581             :                                 }
     582      255364 :                         } else if (GC_TYPE(ref) == IS_ARRAY) {
     583      174355 :                                 if ((zend_array*)ref == &EG(symbol_table)) {
     584           0 :                                         GC_REF_SET_BLACK(ref);
     585           0 :                                         return;
     586             :                                 } else {
     587      174355 :                                         ht = (zend_array*)ref;
     588             :                                 }
     589       81009 :                         } else if (GC_TYPE(ref) == IS_REFERENCE) {
     590       81005 :                                 if (Z_REFCOUNTED(((zend_reference*)ref)->val)) {
     591       81005 :                                         ref = Z_COUNTED(((zend_reference*)ref)->val);
     592       81005 :                                         goto tail_call;
     593             :                                 }
     594           0 :                                 return;
     595             :                         } else {
     596           4 :                                 return;
     597             :                         }
     598             : 
     599      204380 :                         if (!ht->nNumUsed) return;
     600      194375 :                         p = ht->arData;
     601      194375 :                         end = p + ht->nNumUsed;
     602             :                         while (1) {
     603      224373 :                                 end--;
     604      224373 :                                 zv = &end->val;
     605      224373 :                                 if (Z_TYPE_P(zv) == IS_INDIRECT) {
     606       20001 :                                         zv = Z_INDIRECT_P(zv);
     607             :                                 }
     608      224373 :                                 if (Z_REFCOUNTED_P(zv)) {
     609      174378 :                                         break;
     610             :                                 }
     611       49995 :                                 if (p == end) return;
     612       29998 :                         }
     613      461922 :                         while (p != end) {
     614      113166 :                                 zv = &p->val;
     615      113166 :                                 if (Z_TYPE_P(zv) == IS_INDIRECT) {
     616           0 :                                         zv = Z_INDIRECT_P(zv);
     617             :                                 }
     618      113166 :                                 if (Z_REFCOUNTED_P(zv)) {
     619      113166 :                                         ref = Z_COUNTED_P(zv);
     620      113166 :                                         gc_scan(ref);
     621             :                                 }
     622      113166 :                                 p++;
     623             :                         }
     624      174378 :                         zv = &p->val;
     625      174378 :                         if (Z_TYPE_P(zv) == IS_INDIRECT) {
     626           5 :                                 zv = Z_INDIRECT_P(zv);
     627             :                         }
     628      174378 :                         ref = Z_COUNTED_P(zv);
     629      174378 :                         goto tail_call;
     630             :                 }
     631             :         }
     632             : }
     633             : 
     634          91 : static void gc_scan_roots(void)
     635             : {
     636          91 :         gc_root_buffer *current = GC_G(roots).next;
     637             : 
     638      353294 :         while (current != &GC_G(roots)) {
     639      353112 :                 gc_scan(current->ref);
     640      353112 :                 current = current->next;
     641             :         }
     642          91 : }
     643             : 
     644       92035 : static void gc_add_garbage(zend_refcounted *ref, gc_additional_buffer **additional_buffer)
     645             : {
     646       92035 :         gc_root_buffer *buf = GC_G(unused);
     647             : 
     648       92035 :         if (buf) {
     649       14743 :                 GC_G(unused) = buf->prev;
     650             : #if 1
     651             :                 /* optimization: color is already GC_BLACK (0) */
     652       14743 :                 GC_INFO(ref) = buf - GC_G(buf);
     653             : #else
     654             :                 GC_REF_SET_ADDRESS(ref, buf - GC_G(buf));
     655             : #endif
     656       77292 :         } else if (GC_G(first_unused) != GC_G(last_unused)) {
     657           6 :                 buf = GC_G(first_unused);
     658           6 :                 GC_G(first_unused)++;
     659             : #if 1
     660             :                 /* optimization: color is already GC_BLACK (0) */
     661           6 :                 GC_INFO(ref) = buf - GC_G(buf);
     662             : #else
     663             :                 GC_REF_SET_ADDRESS(ref, buf - GC_G(buf));
     664             : #endif
     665             :         } else {
     666             :                 /* If we don't have free slots in the buffer, allocate a new one and
     667             :                  * set it's address to GC_ROOT_BUFFER_MAX_ENTRIES that have special
     668             :                  * meaning.
     669             :                  */
     670       77286 :                 if (!*additional_buffer || (*additional_buffer)->used == GC_NUM_ADDITIONAL_ENTRIES) {
     671         611 :                         gc_additional_buffer *new_buffer = emalloc(sizeof(gc_additional_buffer));
     672         611 :                         new_buffer->used = 0;
     673         611 :                         new_buffer->next = *additional_buffer;
     674         611 :                         *additional_buffer = new_buffer;
     675             :                 }
     676       77286 :                 buf = (*additional_buffer)->buf + (*additional_buffer)->used;
     677       77286 :                 (*additional_buffer)->used++;
     678             : #if 1
     679             :                 /* optimization: color is already GC_BLACK (0) */
     680       77286 :                 GC_INFO(ref) = GC_ROOT_BUFFER_MAX_ENTRIES;
     681             : #else
     682             :                 GC_REF_SET_ADDRESS(ref, GC_ROOT_BUFFER_MAX_ENTRIES);
     683             : #endif
     684             :                 /* modify type to prevent indirect destruction */
     685       77286 :                 GC_TYPE(ref) |= GC_FAKE_BUFFER_FLAG;
     686             :         }
     687       92035 :         if (buf) {
     688       92035 :                 GC_REFCOUNT(ref)++;
     689       92035 :                 buf->ref = ref;
     690       92035 :                 buf->next = GC_G(roots).next;
     691       92035 :                 buf->prev = &GC_G(roots);
     692       92035 :                 GC_G(roots).next->prev = buf;
     693       92035 :                 GC_G(roots).next = buf;
     694             :         }
     695       92035 : }
     696             : 
     697      253845 : static int gc_collect_white(zend_refcounted *ref, uint32_t *flags, gc_additional_buffer **additional_buffer)
     698             : {
     699      253845 :         int count = 0;
     700             :         HashTable *ht;
     701             :         Bucket *p, *end;
     702             :         zval *zv;
     703             : 
     704             : tail_call:
     705      573186 :         if (GC_REF_GET_COLOR(ref) == GC_WHITE) {
     706      329351 :                 ht = NULL;
     707      329351 :                 GC_REF_SET_BLACK(ref);
     708             : 
     709             :                 /* don't count references for compatibility ??? */
     710      329351 :                 if (GC_TYPE(ref) != IS_REFERENCE) {
     711      248346 :                         count++;
     712             :                 }
     713             : 
     714      339377 :                 if (GC_TYPE(ref) == IS_OBJECT && !(GC_FLAGS(ref) & IS_OBJ_FREE_CALLED)) {
     715             :                         zend_object_get_gc_t get_gc;
     716       88689 :                         zend_object *obj = (zend_object*)ref;
     717             : 
     718       98715 :                         if (EXPECTED(IS_OBJ_VALID(EG(objects_store).object_buckets[obj->handle]) &&
     719             :                                      (get_gc = obj->handlers->get_gc) != NULL)) {
     720             :                                 int n;
     721             :                                 zval *zv, *end;
     722             :                                 zval tmp;
     723             : 
     724             : #if 1
     725             :                                 /* optimization: color is GC_BLACK (0) */
     726       88689 :                                 if (!GC_INFO(ref)) {
     727             : #else
     728             :                                 if (!GC_ADDRESS(GC_INFO(ref))) {
     729             : #endif
     730       13382 :                                         gc_add_garbage(ref, additional_buffer);
     731             :                                 }
     732      266067 :                                 if (obj->handlers->dtor_obj &&
     733       88689 :                                     ((obj->handlers->dtor_obj != zend_objects_destroy_object) ||
     734       88689 :                                      (obj->ce->destructor != NULL))) {
     735       88661 :                                         *flags |= GC_HAS_DESTRUCTORS;
     736             :                                 }
     737       88689 :                                 ZVAL_OBJ(&tmp, obj);
     738       88689 :                                 ht = get_gc(&tmp, &zv, &n);
     739       88689 :                                 end = zv + n;
     740       88689 :                                 if (EXPECTED(!ht)) {
     741       78663 :                                         if (!n) return count;
     742      157319 :                                         while (!Z_REFCOUNTED_P(--end)) {
     743             :                                                 /* count non-refcounted for compatibility ??? */
     744           2 :                                                 if (Z_TYPE_P(zv) != IS_UNDEF) {
     745           1 :                                                         count++;
     746             :                                                 }
     747           1 :                                                 if (zv == end) return count;
     748             :                                         }
     749             :                                 }
     750      256026 :                                 while (zv != end) {
     751       78656 :                                         if (Z_REFCOUNTED_P(zv)) {
     752          10 :                                                 ref = Z_COUNTED_P(zv);
     753          10 :                                                 GC_REFCOUNT(ref)++;
     754          10 :                                                 count += gc_collect_white(ref, flags, additional_buffer);
     755             :                                         /* count non-refcounted for compatibility ??? */
     756      157292 :                                         } else if (Z_TYPE_P(zv) != IS_UNDEF) {
     757       78646 :                                                 count++;
     758             :                                         }
     759       78656 :                                         zv++;
     760             :                                 }
     761       88685 :                                 if (EXPECTED(!ht)) {
     762       78659 :                                         ref = Z_COUNTED_P(zv);
     763       78659 :                                         GC_REFCOUNT(ref)++;
     764       78659 :                                         goto tail_call;
     765             :                                 }
     766             :                         } else {
     767           0 :                                 return count;
     768             :                         }
     769      240662 :                 } else if (GC_TYPE(ref) == IS_ARRAY) {
     770             : #if 1
     771             :                                 /* optimization: color is GC_BLACK (0) */
     772      159655 :                                 if (!GC_INFO(ref)) {
     773             : #else
     774             :                                 if (!GC_ADDRESS(GC_INFO(ref))) {
     775             : #endif
     776       78653 :                                 gc_add_garbage(ref, additional_buffer);
     777             :                         }
     778      159655 :                         ht = (zend_array*)ref;
     779       81007 :                 } else if (GC_TYPE(ref) == IS_REFERENCE) {
     780       81005 :                         if (Z_REFCOUNTED(((zend_reference*)ref)->val)) {
     781       81005 :                                 ref = Z_COUNTED(((zend_reference*)ref)->val);
     782       81005 :                                 GC_REFCOUNT(ref)++;
     783       81005 :                                 goto tail_call;
     784             :                         }
     785           0 :                         return count;
     786             :                 } else {
     787           2 :                         return count;
     788             :                 }
     789             : 
     790      169681 :                 if (!ht->nNumUsed) return count;
     791      169675 :                 p = ht->arData;
     792      169675 :                 end = p + ht->nNumUsed;
     793             :                 while (1) {
     794      189674 :                         end--;
     795      189674 :                         zv = &end->val;
     796      189674 :                         if (Z_TYPE_P(zv) == IS_INDIRECT) {
     797       10002 :                                 zv = Z_INDIRECT_P(zv);
     798             :                         }
     799      189674 :                         if (Z_REFCOUNTED_P(zv)) {
     800      159677 :                                 break;
     801             :                         }
     802             :                         /* count non-refcounted for compatibility ??? */
     803       29997 :                         if (Z_TYPE_P(zv) != IS_UNDEF) {
     804       29997 :                                 count++;
     805             :                         }
     806       29997 :                         if (p == end) return count;
     807       19999 :                 }
     808      417990 :                 while (p != end) {
     809       98636 :                         zv = &p->val;
     810       98636 :                         if (Z_TYPE_P(zv) == IS_INDIRECT) {
     811           0 :                                 zv = Z_INDIRECT_P(zv);
     812             :                         }
     813       98636 :                         if (Z_REFCOUNTED_P(zv)) {
     814       98636 :                                 ref = Z_COUNTED_P(zv);
     815       98636 :                                 GC_REFCOUNT(ref)++;
     816       98636 :                                 count += gc_collect_white(ref, flags, additional_buffer);
     817             :                                 /* count non-refcounted for compatibility ??? */
     818           0 :                         } else if (Z_TYPE_P(zv) != IS_UNDEF) {
     819           0 :                                 count++;
     820             :                         }
     821       98636 :                         p++;
     822             :                 }
     823      159677 :                 zv = &p->val;
     824      159677 :                 if (Z_TYPE_P(zv) == IS_INDIRECT) {
     825           5 :                         zv = Z_INDIRECT_P(zv);
     826             :                 }
     827      159677 :                 ref = Z_COUNTED_P(zv);
     828      159677 :                 GC_REFCOUNT(ref)++;
     829      159677 :                 goto tail_call;
     830             :         }
     831      243835 :         return count;
     832             : }
     833             : 
     834          91 : static int gc_collect_roots(uint32_t *flags, gc_additional_buffer **additional_buffer)
     835             : {
     836          91 :         int count = 0;
     837          91 :         gc_root_buffer *current = GC_G(roots).next;
     838             : 
     839             :         /* remove non-garbage from the list */
     840      353294 :         while (current != &GC_G(roots)) {
     841      353112 :                 gc_root_buffer *next = current->next;
     842      353112 :                 if (GC_REF_GET_COLOR(current->ref) == GC_BLACK) {
     843      196803 :                         GC_INFO(current->ref) = 0; /* reset GC_ADDRESS() and keep GC_BLACK */
     844             :                         GC_REMOVE_FROM_ROOTS(current);
     845             :                 }
     846      353112 :                 current = next;
     847             :         }
     848             : 
     849          91 :         current = GC_G(roots).next;
     850      156491 :         while (current != &GC_G(roots)) {
     851      156309 :                 GC_REFCOUNT(current->ref)++;
     852      156309 :                 if (GC_REF_GET_COLOR(current->ref) == GC_WHITE) {
     853      155199 :                         count += gc_collect_white(current->ref, flags, additional_buffer);
     854             :                 }
     855      156309 :                 current = current->next;
     856             :         }
     857             : 
     858             :         /* relink remaining roots into list to free */
     859          91 :         if (GC_G(roots).next != &GC_G(roots)) {
     860          55 :                 if (GC_G(to_free).next == &GC_G(to_free)) {
     861             :                         /* move roots into list to free */
     862          55 :                         GC_G(to_free).next = GC_G(roots).next;
     863          55 :                         GC_G(to_free).prev = GC_G(roots).prev;
     864          55 :                         GC_G(to_free).next->prev = &GC_G(to_free);
     865          55 :                         GC_G(to_free).prev->next = &GC_G(to_free);
     866             :                 } else {
     867             :                         /* add roots into list to free */
     868           0 :                         GC_G(to_free).prev->next = GC_G(roots).next;
     869           0 :                         GC_G(roots).next->prev = GC_G(to_free).prev;
     870           0 :                         GC_G(roots).prev->next = &GC_G(to_free);
     871           0 :                         GC_G(to_free).prev = GC_G(roots).prev;
     872             :                 }
     873             : 
     874          55 :                 GC_G(roots).next = &GC_G(roots);
     875          55 :                 GC_G(roots).prev = &GC_G(roots);
     876             :         }
     877          91 :         return count;
     878             : }
     879             : 
     880           4 : static void gc_remove_nested_data_from_buffer(zend_refcounted *ref, gc_root_buffer *root)
     881             : {
     882           4 :         HashTable *ht = NULL;
     883             :         Bucket *p, *end;
     884             :         zval *zv;
     885             : 
     886             : tail_call:
     887          21 :         if (root ||
     888           6 :             (GC_ADDRESS(GC_INFO(ref)) != 0 &&
     889           3 :              GC_REF_GET_COLOR(ref) == GC_BLACK &&
     890           3 :              GC_ADDRESS(GC_INFO(ref)) != GC_ROOT_BUFFER_MAX_ENTRIES)) {
     891             :                 GC_TRACE_REF(ref, "removing from buffer");
     892           6 :                 GC_REFCOUNT(ref)--;
     893           6 :                 if (root) {
     894           3 :                         GC_INFO(ref) = 0;
     895             :                         GC_REMOVE_FROM_ROOTS(root);
     896           3 :                         root = NULL;
     897             :                 } else {
     898           3 :                         GC_REMOVE_FROM_BUFFER(ref);
     899             :                 }
     900             : 
     901           9 :                 if (GC_TYPE(ref) == IS_OBJECT && !(GC_FLAGS(ref) & IS_OBJ_FREE_CALLED)) {
     902             :                         zend_object_get_gc_t get_gc;
     903           4 :                         zend_object *obj = (zend_object*)ref;
     904             : 
     905           7 :                         if (EXPECTED(IS_OBJ_VALID(EG(objects_store).object_buckets[obj->handle]) &&
     906             :                              (get_gc = obj->handlers->get_gc) != NULL)) {
     907             :                                 int n;
     908             :                                 zval *zv, *end;
     909             :                                 zval tmp;
     910             : 
     911           4 :                                 ZVAL_OBJ(&tmp, obj);
     912           4 :                                 ht = get_gc(&tmp, &zv, &n);
     913           4 :                                 end = zv + n;
     914           4 :                                 if (EXPECTED(!ht)) {
     915           1 :                                         if (!n) return;
     916           0 :                                         while (!Z_REFCOUNTED_P(--end)) {
     917           0 :                                                 if (zv == end) return;
     918             :                                         }
     919             :                                 }
     920           6 :                                 while (zv != end) {
     921           0 :                                         if (Z_REFCOUNTED_P(zv)) {
     922           0 :                                                 ref = Z_COUNTED_P(zv);
     923           0 :                                                 gc_remove_nested_data_from_buffer(ref, NULL);
     924             :                                         }
     925           0 :                                         zv++;
     926             :                                 }
     927           3 :                                 if (EXPECTED(!ht)) {
     928           0 :                                         ref = Z_COUNTED_P(zv);
     929           0 :                                         goto tail_call;
     930             :                                 }
     931             :                         } else {
     932           0 :                                 return;
     933             :                         }
     934           2 :                 } else if (GC_TYPE(ref) == IS_ARRAY) {
     935           2 :                         ht = (zend_array*)ref;
     936           0 :                 } else if (GC_TYPE(ref) == IS_REFERENCE) {
     937           0 :                         if (Z_REFCOUNTED(((zend_reference*)ref)->val)) {
     938           0 :                                 ref = Z_COUNTED(((zend_reference*)ref)->val);
     939           0 :                                 goto tail_call;
     940             :                         }
     941           0 :                         return;
     942             :                 } else {
     943           0 :                         return;
     944             :                 }
     945             : 
     946           5 :                 if (!ht->nNumUsed) return;
     947           5 :                 p = ht->arData;
     948           5 :                 end = p + ht->nNumUsed;
     949             :                 while (1) {
     950           5 :                         end--;
     951           5 :                         zv = &end->val;
     952           5 :                         if (Z_TYPE_P(zv) == IS_INDIRECT) {
     953           2 :                                 zv = Z_INDIRECT_P(zv);
     954             :                         }
     955           5 :                         if (Z_REFCOUNTED_P(zv)) {
     956           5 :                                 break;
     957             :                         }
     958           0 :                         if (p == end) return;
     959           0 :                 }
     960          11 :                 while (p != end) {
     961           1 :                         zv = &p->val;
     962           1 :                         if (Z_TYPE_P(zv) == IS_INDIRECT) {
     963           0 :                                 zv = Z_INDIRECT_P(zv);
     964             :                         }
     965           1 :                         if (Z_REFCOUNTED_P(zv)) {
     966           1 :                                 ref = Z_COUNTED_P(zv);
     967           1 :                                 gc_remove_nested_data_from_buffer(ref, NULL);
     968             :                         }
     969           1 :                         p++;
     970             :                 }
     971           5 :                 zv = &p->val;
     972           5 :                 if (Z_TYPE_P(zv) == IS_INDIRECT) {
     973           2 :                         zv = Z_INDIRECT_P(zv);
     974             :                 }
     975           5 :                 ref = Z_COUNTED_P(zv);
     976           5 :                 goto tail_call;
     977             :         }
     978             : }
     979             : 
     980          96 : ZEND_API int zend_gc_collect_cycles(void)
     981             : {
     982          96 :         int count = 0;
     983             : 
     984          96 :         if (GC_G(roots).next != &GC_G(roots)) {
     985             :                 gc_root_buffer *current, *next, *orig_next_to_free;
     986             :                 zend_refcounted *p;
     987             :                 gc_root_buffer to_free;
     988          91 :                 uint32_t gc_flags = 0;
     989             :                 gc_additional_buffer *additional_buffer;
     990             : #if ZEND_GC_DEBUG
     991             :                 zend_bool orig_gc_full;
     992             : #endif
     993             : 
     994          91 :                 if (GC_G(gc_active)) {
     995           0 :                         return 0;
     996             :                 }
     997             : 
     998             :                 GC_TRACE("Collecting cycles");
     999          91 :                 GC_G(gc_runs)++;
    1000          91 :                 GC_G(gc_active) = 1;
    1001             : 
    1002             :                 GC_TRACE("Marking roots");
    1003          91 :                 gc_mark_roots();
    1004             :                 GC_TRACE("Scanning roots");
    1005          91 :                 gc_scan_roots();
    1006             : 
    1007             : #if ZEND_GC_DEBUG
    1008             :                 orig_gc_full = GC_G(gc_full);
    1009             :                 GC_G(gc_full) = 0;
    1010             : #endif
    1011             : 
    1012             :                 GC_TRACE("Collecting roots");
    1013          91 :                 additional_buffer = NULL;
    1014          91 :                 count = gc_collect_roots(&gc_flags, &additional_buffer);
    1015             : #if ZEND_GC_DEBUG
    1016             :                 GC_G(gc_full) = orig_gc_full;
    1017             : #endif
    1018          91 :                 GC_G(gc_active) = 0;
    1019             : 
    1020          91 :                 if (GC_G(to_free).next == &GC_G(to_free)) {
    1021             :                         /* nothing to free */
    1022             :                         GC_TRACE("Nothing to free");
    1023          36 :                         return 0;
    1024             :                 }
    1025             : 
    1026             :                 /* Copy global to_free list into local list */
    1027          55 :                 to_free.next = GC_G(to_free).next;
    1028          55 :                 to_free.prev = GC_G(to_free).prev;
    1029          55 :                 to_free.next->prev = &to_free;
    1030          55 :                 to_free.prev->next = &to_free;
    1031             : 
    1032             :                 /* Free global list */
    1033          55 :                 GC_G(to_free).next = &GC_G(to_free);
    1034          55 :                 GC_G(to_free).prev = &GC_G(to_free);
    1035             : 
    1036          55 :                 orig_next_to_free = GC_G(next_to_free);
    1037             : 
    1038             : #if ZEND_GC_DEBUG
    1039             :                 orig_gc_full = GC_G(gc_full);
    1040             :                 GC_G(gc_full) = 0;
    1041             : #endif
    1042             : 
    1043          55 :                 if (gc_flags & GC_HAS_DESTRUCTORS) {
    1044             :                         GC_TRACE("Calling destructors");
    1045          24 :                         if (EG(objects_store).object_buckets) {
    1046             :                                 /* Remember reference counters before calling destructors */
    1047          24 :                                 current = to_free.next;
    1048      197358 :                                 while (current != &to_free) {
    1049      197310 :                                         current->refcount = GC_REFCOUNT(current->ref);
    1050      197310 :                                         current = current->next;
    1051             :                                 }
    1052             : 
    1053             :                                 /* Call destructors */
    1054          24 :                                 current = to_free.next;
    1055      197358 :                                 while (current != &to_free) {
    1056      197310 :                                         p = current->ref;
    1057      197310 :                                         GC_G(next_to_free) = current->next;
    1058      197310 :                                         if ((GC_TYPE(p) & GC_TYPE_MASK) == IS_OBJECT) {
    1059       88665 :                                                 zend_object *obj = (zend_object*)p;
    1060             : 
    1061      177330 :                                                 if (IS_OBJ_VALID(EG(objects_store).object_buckets[obj->handle]) &&
    1062       88665 :                                                         !(GC_FLAGS(obj) & IS_OBJ_DESTRUCTOR_CALLED)) {
    1063             :                                                         GC_TRACE_REF(obj, "calling destructor");
    1064       78668 :                                                         GC_FLAGS(obj) |= IS_OBJ_DESTRUCTOR_CALLED;
    1065       78668 :                                                         if (obj->handlers->dtor_obj) {
    1066       78668 :                                                                 GC_REFCOUNT(obj)++;
    1067       78668 :                                                                 obj->handlers->dtor_obj(obj);
    1068       78668 :                                                                 GC_REFCOUNT(obj)--;
    1069             :                                                         }
    1070             :                                                 }
    1071             :                                         }
    1072      197310 :                                         current = GC_G(next_to_free);
    1073             :                                 }
    1074             : 
    1075             :                                 /* Remove values captured in destructors */
    1076          24 :                                 current = to_free.next;
    1077      197356 :                                 while (current != &to_free) {
    1078      197308 :                                         GC_G(next_to_free) = current->next;
    1079      197308 :                                         if (GC_REFCOUNT(current->ref) > current->refcount) {
    1080           3 :                                                 gc_remove_nested_data_from_buffer(current->ref, current);
    1081             :                                         }
    1082      197308 :                                         current = GC_G(next_to_free);
    1083             :                                 }
    1084             :                         }
    1085             :                 }
    1086             : 
    1087             :                 /* Destroy zvals */
    1088             :                 GC_TRACE("Destroying zvals");
    1089          55 :                 GC_G(gc_active) = 1;
    1090          55 :                 current = to_free.next;
    1091      248448 :                 while (current != &to_free) {
    1092      248338 :                         p = current->ref;
    1093      248338 :                         GC_G(next_to_free) = current->next;
    1094             :                         GC_TRACE_REF(p, "destroying");
    1095      248338 :                         if ((GC_TYPE(p) & GC_TYPE_MASK) == IS_OBJECT) {
    1096       88685 :                                 zend_object *obj = (zend_object*)p;
    1097             : 
    1098      177370 :                                 if (EG(objects_store).object_buckets &&
    1099       88685 :                                     IS_OBJ_VALID(EG(objects_store).object_buckets[obj->handle])) {
    1100       88685 :                                         GC_TYPE(obj) = IS_NULL;
    1101       88685 :                                         if (!(GC_FLAGS(obj) & IS_OBJ_FREE_CALLED)) {
    1102       88685 :                                                 GC_FLAGS(obj) |= IS_OBJ_FREE_CALLED;
    1103       88685 :                                                 if (obj->handlers->free_obj) {
    1104       88685 :                                                         GC_REFCOUNT(obj)++;
    1105       88685 :                                                         obj->handlers->free_obj(obj);
    1106       88685 :                                                         GC_REFCOUNT(obj)--;
    1107             :                                                 }
    1108             :                                         }
    1109       88685 :                                         SET_OBJ_BUCKET_NUMBER(EG(objects_store).object_buckets[obj->handle], EG(objects_store).free_list_head);
    1110       88685 :                                         EG(objects_store).free_list_head = obj->handle;
    1111       88685 :                                         p = current->ref = (zend_refcounted*)(((char*)obj) - obj->handlers->offset);
    1112             :                                 }
    1113      159653 :                         } else if ((GC_TYPE(p) & GC_TYPE_MASK) == IS_ARRAY) {
    1114      159653 :                                 zend_array *arr = (zend_array*)p;
    1115             : 
    1116      159653 :                                 GC_TYPE(arr) = IS_NULL;
    1117      159653 :                                 zend_hash_destroy(arr);
    1118             :                         }
    1119      248338 :                         current = GC_G(next_to_free);
    1120             :                 }
    1121             : 
    1122             :                 /* Free objects */
    1123          55 :                 current = to_free.next;
    1124      248448 :                 while (current != &to_free) {
    1125      248338 :                         next = current->next;
    1126      248338 :                         p = current->ref;
    1127      248338 :                         if (EXPECTED(current >= GC_G(buf) && current < GC_G(buf) + GC_ROOT_BUFFER_MAX_ENTRIES)) {
    1128      171052 :                                 current->prev = GC_G(unused);
    1129      171052 :                                 GC_G(unused) = current;
    1130             :                         }
    1131      248338 :                         efree(p);
    1132      248338 :                         current = next;
    1133             :                 }
    1134             : 
    1135         721 :                 while (additional_buffer != NULL) {
    1136         611 :                         gc_additional_buffer *next = additional_buffer->next;
    1137         611 :                         efree(additional_buffer);
    1138         611 :                         additional_buffer = next;
    1139             :                 }
    1140             : 
    1141             :                 GC_TRACE("Collection finished");
    1142          55 :                 GC_G(collected) += count;
    1143          55 :                 GC_G(next_to_free) = orig_next_to_free;
    1144             : #if ZEND_GC_DEBUG
    1145             :                 GC_G(gc_full) = orig_gc_full;
    1146             : #endif
    1147          55 :                 GC_G(gc_active) = 0;
    1148             :         }
    1149             : 
    1150          60 :         return count;
    1151             : }
    1152             : 
    1153             : /*
    1154             :  * Local variables:
    1155             :  * tab-width: 4
    1156             :  * c-basic-offset: 4
    1157             :  * indent-tabs-mode: t
    1158             :  * End:
    1159             :  */

Generated by: LCOV version 1.10

Generated at Wed, 27 Apr 2016 15:51:36 +0000 (5 days ago)

Copyright © 2005-2016 The PHP Group
All rights reserved.