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: 529 576 91.8 %
Date: 2015-06-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-2015 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     1129141 :         root->next->prev = root->prev;
     120     1129141 :         root->prev->next = root->next;
     121     1129141 :         root->prev = GC_G(unused);
     122     1129141 :         GC_G(unused) = root;
     123             :         GC_BENCH_DEC(root_buf_length);
     124             : }
     125             : 
     126       21085 : static void root_buffer_dtor(zend_gc_globals *gc_globals)
     127             : {
     128       21085 :         if (gc_globals->buf) {
     129       21084 :                 free(gc_globals->buf);
     130       21084 :                 gc_globals->buf = NULL;
     131             :         }
     132       21085 : }
     133             : 
     134       21049 : static void gc_globals_ctor_ex(zend_gc_globals *gc_globals)
     135             : {
     136       21049 :         gc_globals->gc_enabled = 0;
     137       21049 :         gc_globals->gc_active = 0;
     138             : 
     139       21049 :         gc_globals->buf = NULL;
     140             : 
     141       21049 :         gc_globals->roots.next = &gc_globals->roots;
     142       21049 :         gc_globals->roots.prev = &gc_globals->roots;
     143       21049 :         gc_globals->unused = NULL;
     144       21049 :         gc_globals->next_to_free = NULL;
     145             : 
     146       21049 :         gc_globals->to_free.next = &gc_globals->to_free;
     147       21049 :         gc_globals->to_free.prev = &gc_globals->to_free;
     148             : 
     149       21049 :         gc_globals->gc_runs = 0;
     150       21049 :         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       21049 : }
     161             : 
     162       21049 : 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       21049 :         gc_globals_ctor_ex(&gc_globals);
     168             : #endif
     169       21049 : }
     170             : 
     171       21085 : ZEND_API void gc_globals_dtor(void)
     172             : {
     173             : #ifndef ZTS
     174       21085 :         root_buffer_dtor(&gc_globals);
     175             : #endif
     176       21085 : }
     177             : 
     178       42054 : ZEND_API void gc_reset(void)
     179             : {
     180       42054 :         GC_G(gc_runs) = 0;
     181       42054 :         GC_G(collected) = 0;
     182       42054 :         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       42054 :         GC_G(roots).next = &GC_G(roots);
     194       42054 :         GC_G(roots).prev = &GC_G(roots);
     195             : 
     196       42054 :         GC_G(to_free).next = &GC_G(to_free);
     197       42054 :         GC_G(to_free).prev = &GC_G(to_free);
     198             : 
     199       42054 :         if (GC_G(buf)) {
     200       42052 :                 GC_G(unused) = NULL;
     201       42052 :                 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       42054 : }
     208             : 
     209       21062 : ZEND_API void gc_init(void)
     210             : {
     211       21062 :         if (GC_G(buf) == NULL && GC_G(gc_enabled)) {
     212       21048 :                 GC_G(buf) = (gc_root_buffer*) malloc(sizeof(gc_root_buffer) * GC_ROOT_BUFFER_MAX_ENTRIES);
     213       21048 :                 GC_G(last_unused) = &GC_G(buf)[GC_ROOT_BUFFER_MAX_ENTRIES];
     214       21048 :                 gc_reset();
     215             :         }
     216       21062 : }
     217             : 
     218     1265815 : ZEND_API void ZEND_FASTCALL gc_possible_root(zend_refcounted *ref)
     219             : {
     220             :         gc_root_buffer *newRoot;
     221             : 
     222     1265815 :         if (UNEXPECTED(CG(unclean_shutdown)) || UNEXPECTED(GC_G(gc_active))) {
     223       10143 :                 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     1255672 :         newRoot = GC_G(unused);
     233     1255672 :         if (newRoot) {
     234     1035226 :                 GC_G(unused) = newRoot->prev;
     235      220446 :         } else if (GC_G(first_unused) != GC_G(last_unused)) {
     236      220413 :                 newRoot = GC_G(first_unused);
     237      220413 :                 GC_G(first_unused)++;
     238             :         } else {
     239          33 :                 if (!GC_G(gc_enabled)) {
     240           8 :                         return;
     241             :                 }
     242          25 :                 GC_REFCOUNT(ref)++;
     243          25 :                 gc_collect_cycles();
     244          25 :                 GC_REFCOUNT(ref)--;
     245          25 :                 newRoot = GC_G(unused);
     246          25 :                 if (!newRoot) {
     247             : #if ZEND_GC_DEBUG
     248             :                         if (!GC_G(gc_full)) {
     249             :                                 fprintf(stderr, "GC: no space to record new root candidate\n");
     250             :                                 GC_G(gc_full) = 1;
     251             :                         }
     252             : #endif
     253           0 :                         return;
     254             :                 }
     255          25 :                 GC_G(unused) = newRoot->prev;
     256             :         }
     257             : 
     258             :         GC_TRACE_SET_COLOR(ref, GC_PURPLE);
     259     1255664 :         GC_INFO(ref) = (newRoot - GC_G(buf)) | GC_PURPLE;
     260     1255664 :         newRoot->ref = ref;
     261             : 
     262     1255664 :         newRoot->next = GC_G(roots).next;
     263     1255664 :         newRoot->prev = &GC_G(roots);
     264     1255664 :         GC_G(roots).next->prev = newRoot;
     265     1255664 :         GC_G(roots).next = newRoot;
     266             : 
     267             :         GC_BENCH_INC(zval_buffered);
     268             :         GC_BENCH_INC(root_buf_length);
     269             :         GC_BENCH_PEAK(root_buf_peak, root_buf_length);
     270             : }
     271             : 
     272      982341 : ZEND_API void ZEND_FASTCALL gc_remove_from_buffer(zend_refcounted *ref)
     273             : {
     274             :         gc_root_buffer *root;
     275             : 
     276             :         ZEND_ASSERT(GC_ADDRESS(GC_INFO(ref)));
     277             :         ZEND_ASSERT(GC_ADDRESS(GC_INFO(ref)) < GC_ROOT_BUFFER_MAX_ENTRIES);
     278             : 
     279             :         GC_BENCH_INC(zval_remove_from_buffer);
     280             : 
     281      982341 :         root = GC_G(buf) + GC_ADDRESS(GC_INFO(ref));
     282      982341 :         if (GC_REF_GET_COLOR(ref) != GC_BLACK) {
     283             :                 GC_TRACE_SET_COLOR(ref, GC_PURPLE);
     284             :         }
     285      982341 :         GC_INFO(ref) = 0;
     286             :         GC_REMOVE_FROM_ROOTS(root);
     287             : 
     288             :         /* updete next root that is going to be freed */
     289      982341 :         if (GC_G(next_to_free) == root) {
     290           2 :                 GC_G(next_to_free) = root->next;
     291             :         }
     292      982341 : }
     293             : 
     294      171609 : static void gc_scan_black(zend_refcounted *ref)
     295             : {
     296             :         HashTable *ht;
     297             :         Bucket *p, *end;
     298             :         zval *zv;
     299             : 
     300             : tail_call:
     301      171609 :         ht = NULL;
     302      171609 :         GC_REF_SET_BLACK(ref);
     303             : 
     304      301620 :         if (GC_TYPE(ref) == IS_OBJECT && !(GC_FLAGS(ref) & IS_OBJ_FREE_CALLED)) {
     305             :                 zend_object_get_gc_t get_gc;
     306      156748 :                 zend_object *obj = (zend_object*)ref;
     307             : 
     308      286759 :                 if (EXPECTED(IS_OBJ_VALID(EG(objects_store).object_buckets[obj->handle]) &&
     309             :                              (get_gc = obj->handlers->get_gc) != NULL)) {
     310             :                         int n;
     311             :                         zval *zv, *end;
     312             :                         zval tmp;
     313             : 
     314      156748 :                         ZVAL_OBJ(&tmp, obj);
     315      156748 :                         ht = get_gc(&tmp, &zv, &n);
     316      156748 :                         end = zv + n;
     317      156748 :                         if (EXPECTED(!ht)) {
     318       26737 :                                 if (!n) return;
     319       29477 :                                 while (!Z_REFCOUNTED_P(--end)) {
     320           1 :                                         if (zv == end) return;
     321             :                                 }
     322             :                         }
     323      384240 :                         while (zv != end) {
     324       94742 :                                 if (Z_REFCOUNTED_P(zv)) {
     325       40003 :                                         ref = Z_COUNTED_P(zv);
     326       40003 :                                         GC_REFCOUNT(ref)++;
     327       40003 :                                         if (GC_REF_GET_COLOR(ref) != GC_BLACK) {
     328       10006 :                                                 gc_scan_black(ref);
     329             :                                         }
     330             :                                 }
     331       94742 :                                 zv++;
     332             :                         }
     333      144749 :                         if (EXPECTED(!ht)) {
     334       14738 :                                 ref = Z_COUNTED_P(zv);
     335       14738 :                                 GC_REFCOUNT(ref)++;
     336       14738 :                                 if (GC_REF_GET_COLOR(ref) != GC_BLACK) {
     337       14738 :                                         goto tail_call;
     338             :                                 }
     339           0 :                                 return;
     340             :                         }
     341             :                 } else {
     342           0 :                         return;
     343             :                 }
     344       14861 :         } else if (GC_TYPE(ref) == IS_ARRAY) {
     345       14803 :                 if ((zend_array*)ref != &EG(symbol_table)) {
     346       14803 :                         ht = (zend_array*)ref;
     347             :                 } else {
     348           0 :                         return;
     349             :                 }
     350          58 :         } else if (GC_TYPE(ref) == IS_REFERENCE) {
     351           4 :                 if (Z_REFCOUNTED(((zend_reference*)ref)->val)) {
     352           3 :                         ref = Z_COUNTED(((zend_reference*)ref)->val);
     353           3 :                         GC_REFCOUNT(ref)++;
     354           3 :                         if (GC_REF_GET_COLOR(ref) != GC_BLACK) {
     355           3 :                                 goto tail_call;
     356             :                         }
     357             :                 }
     358           1 :                 return;
     359             :         } else {
     360          54 :                 return;
     361             :         }
     362             : 
     363      144814 :         if (!ht->nNumUsed) return;
     364       24806 :         p = ht->arData;
     365       24806 :         end = p + ht->nNumUsed;
     366             :         while (1) {
     367       34810 :                 end--;
     368       34810 :                 zv = &end->val;
     369       34810 :                 if (Z_TYPE_P(zv) == IS_INDIRECT) {
     370       10004 :                         zv = Z_INDIRECT_P(zv);
     371             :                 }
     372       34810 :                 if (Z_REFCOUNTED_P(zv)) {
     373       14801 :                         break;
     374             :                 }
     375       20009 :                 if (p == end) return;
     376       10004 :         }
     377       74332 :         while (p != end) {
     378       44730 :                 zv = &p->val;
     379       44730 :                 if (Z_TYPE_P(zv) == IS_INDIRECT) {
     380           0 :                         zv = Z_INDIRECT_P(zv);
     381             :                 }
     382       44730 :                 if (Z_REFCOUNTED_P(zv)) {
     383       34733 :                         ref = Z_COUNTED_P(zv);
     384       34733 :                         GC_REFCOUNT(ref)++;
     385       34733 :                         if (GC_REF_GET_COLOR(ref) != GC_BLACK) {
     386       33792 :                                 gc_scan_black(ref);
     387             :                         }
     388             :                 }
     389       44730 :                 p++;
     390             :         }
     391       14801 :         zv = &p->val;
     392       14801 :         if (Z_TYPE_P(zv) == IS_INDIRECT) {
     393           2 :                 zv = Z_INDIRECT_P(zv);
     394             :         }
     395       14801 :         ref = Z_COUNTED_P(zv);
     396       14801 :         GC_REFCOUNT(ref)++;
     397       14801 :         if (GC_REF_GET_COLOR(ref) != GC_BLACK) {
     398         973 :                 goto tail_call;
     399             :         }
     400             : }
     401             : 
     402      734260 : static void gc_mark_grey(zend_refcounted *ref)
     403             : {
     404             :     HashTable *ht;
     405             :         Bucket *p, *end;
     406             :         zval *zv;
     407             : 
     408             : tail_call:
     409      734260 :         if (GC_REF_GET_COLOR(ref) != GC_GREY) {
     410      440963 :                 ht = NULL;
     411             :                 GC_BENCH_INC(zval_marked_grey);
     412      440963 :                 GC_REF_SET_COLOR(ref, GC_GREY);
     413             : 
     414      580996 :                 if (GC_TYPE(ref) == IS_OBJECT && !(GC_FLAGS(ref) & IS_OBJ_FREE_CALLED)) {
     415             :                         zend_object_get_gc_t get_gc;
     416      245430 :                         zend_object *obj = (zend_object*)ref;
     417             : 
     418      385463 :                         if (EXPECTED(IS_OBJ_VALID(EG(objects_store).object_buckets[obj->handle]) &&
     419             :                              (get_gc = obj->handlers->get_gc) != NULL)) {
     420             :                                 int n;
     421             :                                 zval *zv, *end;
     422             :                                 zval tmp;
     423             : 
     424      245430 :                                 ZVAL_OBJ(&tmp, obj);
     425      245430 :                                 ht = get_gc(&tmp, &zv, &n);
     426      245430 :                                 end = zv + n;
     427      245430 :                                 if (EXPECTED(!ht)) {
     428      105397 :                                         if (!n) return;
     429      186796 :                                         while (!Z_REFCOUNTED_P(--end)) {
     430           2 :                                                 if (zv == end) return;
     431             :                                         }
     432             :                                 }
     433      640258 :                                 while (zv != end) {
     434      173398 :                                         if (Z_REFCOUNTED_P(zv)) {
     435       40013 :                                                 ref = Z_COUNTED_P(zv);
     436       40013 :                                                 GC_REFCOUNT(ref)--;
     437       40013 :                                                 gc_mark_grey(ref);
     438             :                                         }
     439      173398 :                                         zv++;
     440             :                                 }
     441      233430 :                                 if (EXPECTED(!ht)) {
     442       93397 :                                         ref = Z_COUNTED_P(zv);
     443       93397 :                                         GC_REFCOUNT(ref)--;
     444       93397 :                                         goto tail_call;
     445             :                                 }
     446             :                         } else {
     447           0 :                                 return;
     448             :                         }
     449      195533 :                 } else if (GC_TYPE(ref) == IS_ARRAY) {
     450      144463 :                         if (((zend_array*)ref) == &EG(symbol_table)) {
     451           0 :                                 GC_REF_SET_BLACK(ref);
     452           0 :                                 return;
     453             :                         } else {
     454      144463 :                                 ht = (zend_array*)ref;
     455             :                         }
     456       51070 :                 } else if (GC_TYPE(ref) == IS_REFERENCE) {
     457       51014 :                         if (Z_REFCOUNTED(((zend_reference*)ref)->val)) {
     458       51013 :                                 if (UNEXPECTED(!EG(objects_store).object_buckets) &&
     459           0 :                                         Z_TYPE(((zend_reference*)ref)->val) == IS_OBJECT) {
     460           0 :                                         Z_TYPE_INFO(((zend_reference*)ref)->val) = IS_NULL;
     461           0 :                                         return;
     462             :                                 }
     463       51013 :                                 ref = Z_COUNTED(((zend_reference*)ref)->val);
     464       51013 :                                 GC_REFCOUNT(ref)--;
     465       51013 :                                 goto tail_call;
     466             :                         }
     467           1 :                         return;
     468             :                 } else {
     469          56 :                         return;
     470             :                 }
     471             : 
     472      284496 :                 if (!ht->nNumUsed) return;
     473      164482 :                 p = ht->arData;
     474      164482 :                 end = p + ht->nNumUsed;
     475             :                 while (1) {
     476      194485 :                         end--;
     477      194485 :                         zv = &end->val;
     478      194485 :                         if (Z_TYPE_P(zv) == IS_INDIRECT) {
     479       20006 :                                 zv = Z_INDIRECT_P(zv);
     480             :                         }
     481      194485 :                         if (Z_REFCOUNTED_P(zv)) {
     482      144479 :                                 break;
     483             :                         }
     484       50006 :                         if (p == end) return;
     485       30003 :                 }
     486      432321 :                 while (p != end) {
     487      143363 :                         zv = &p->val;
     488      143363 :                         if (Z_TYPE_P(zv) == IS_INDIRECT) {
     489           0 :                                 zv = Z_INDIRECT_P(zv);
     490             :                         }
     491      143363 :                         if (Z_REFCOUNTED_P(zv)) {
     492      256727 :                                 if (Z_TYPE_P(zv) == IS_OBJECT &&
     493      123361 :                                     UNEXPECTED(!EG(objects_store).object_buckets)) {
     494           0 :                                         Z_TYPE_INFO_P(zv) = IS_NULL;
     495             :                                 } else {
     496      133366 :                                         ref = Z_COUNTED_P(zv);
     497      133366 :                                         GC_REFCOUNT(ref)--;
     498      133366 :                                         gc_mark_grey(ref);
     499             :                                 }
     500             :                         }
     501      143363 :                         p++;
     502             :                 }
     503      144479 :                 zv = &p->val;
     504      144479 :                 if (Z_TYPE_P(zv) == IS_INDIRECT) {
     505           7 :                         zv = Z_INDIRECT_P(zv);
     506             :                 }
     507      237885 :                 if (Z_TYPE_P(zv) == IS_OBJECT &&
     508       93406 :                     UNEXPECTED(!EG(objects_store).object_buckets)) {
     509           0 :                         Z_TYPE_INFO_P(zv) = IS_NULL;
     510             :                 } else {
     511      144479 :                         ref = Z_COUNTED_P(zv);
     512      144479 :                         GC_REFCOUNT(ref)--;
     513      144479 :                         goto tail_call;
     514             :                 }
     515             :         }
     516             : }
     517             : 
     518          80 : static void gc_mark_roots(void)
     519             : {
     520          80 :         gc_root_buffer *current = GC_G(roots).next;
     521             : 
     522      273269 :         while (current != &GC_G(roots)) {
     523      273109 :                 if (GC_REF_GET_COLOR(current->ref) == GC_PURPLE) {
     524      271992 :                         gc_mark_grey(current->ref);
     525             :                 }
     526      273109 :                 current = current->next;
     527             :         }
     528          80 : }
     529             : 
     530      675027 : static void gc_scan(zend_refcounted *ref)
     531             : {
     532             :     HashTable *ht;
     533             :         Bucket *p, *end;
     534             :         zval *zv;
     535             : 
     536             : tail_call:
     537      675027 :         if (GC_REF_GET_COLOR(ref) == GC_GREY) {
     538      430849 :                 if (GC_REFCOUNT(ref) > 0) {
     539      112097 :                         gc_scan_black(ref);
     540             :                 } else {
     541      318752 :                         GC_REF_SET_COLOR(ref, GC_WHITE);
     542      348773 :                         if (GC_TYPE(ref) == IS_OBJECT && !(GC_FLAGS(ref) & IS_OBJ_FREE_CALLED)) {
     543             :                                 zend_object_get_gc_t get_gc;
     544      123378 :                                 zend_object *obj = (zend_object*)ref;
     545             : 
     546      153399 :                                 if (EXPECTED(IS_OBJ_VALID(EG(objects_store).object_buckets[obj->handle]) &&
     547             :                                              (get_gc = obj->handlers->get_gc) != NULL)) {
     548             :                                         int n;
     549             :                                         zval *zv, *end;
     550             :                                         zval tmp;
     551             : 
     552      123378 :                                         ZVAL_OBJ(&tmp, obj);
     553      123378 :                                         ht = get_gc(&tmp, &zv, &n);
     554      123378 :                                         end = zv + n;
     555      123378 :                                         if (EXPECTED(!ht)) {
     556       93357 :                                                 if (!n) return;
     557      186713 :                                                 while (!Z_REFCOUNTED_P(--end)) {
     558           1 :                                                         if (zv == end) return;
     559             :                                                 }
     560             :                                         }
     561      340107 :                                         while (zv != end) {
     562       93353 :                                                 if (Z_REFCOUNTED_P(zv)) {
     563          10 :                                                         ref = Z_COUNTED_P(zv);
     564          10 :                                                         gc_scan(ref);
     565             :                                                 }
     566       93353 :                                                 zv++;
     567             :                                         }
     568      123377 :                                         if (EXPECTED(!ht)) {
     569       93356 :                                                 ref = Z_COUNTED_P(zv);
     570       93356 :                                                 goto tail_call;
     571             :                                         }
     572             :                                 } else {
     573           0 :                                         return;
     574             :                                 }
     575      195374 :                         } else if (GC_TYPE(ref) == IS_ARRAY) {
     576      144360 :                                 if ((zend_array*)ref == &EG(symbol_table)) {
     577           0 :                                         GC_REF_SET_BLACK(ref);
     578           0 :                                         return;
     579             :                                 } else {
     580      144360 :                                         ht = (zend_array*)ref;
     581             :                                 }
     582       51014 :                         } else if (GC_TYPE(ref) == IS_REFERENCE) {
     583       51010 :                                 if (Z_REFCOUNTED(((zend_reference*)ref)->val)) {
     584       51010 :                                         ref = Z_COUNTED(((zend_reference*)ref)->val);
     585       51010 :                                         goto tail_call;
     586             :                                 }
     587           0 :                                 return;
     588             :                         } else {
     589           4 :                                 return;
     590             :                         }
     591             : 
     592      174381 :                         if (!ht->nNumUsed) return;
     593      164376 :                         p = ht->arData;
     594      164376 :                         end = p + ht->nNumUsed;
     595             :                         while (1) {
     596      194374 :                                 end--;
     597      194374 :                                 zv = &end->val;
     598      194374 :                                 if (Z_TYPE_P(zv) == IS_INDIRECT) {
     599       20001 :                                         zv = Z_INDIRECT_P(zv);
     600             :                                 }
     601      194374 :                                 if (Z_REFCOUNTED_P(zv)) {
     602      144379 :                                         break;
     603             :                                 }
     604       49995 :                                 if (p == end) return;
     605       29998 :                         }
     606      401921 :                         while (p != end) {
     607      113163 :                                 zv = &p->val;
     608      113163 :                                 if (Z_TYPE_P(zv) == IS_INDIRECT) {
     609           0 :                                         zv = Z_INDIRECT_P(zv);
     610             :                                 }
     611      113163 :                                 if (Z_REFCOUNTED_P(zv)) {
     612      113163 :                                         ref = Z_COUNTED_P(zv);
     613      113163 :                                         gc_scan(ref);
     614             :                                 }
     615      113163 :                                 p++;
     616             :                         }
     617      144379 :                         zv = &p->val;
     618      144379 :                         if (Z_TYPE_P(zv) == IS_INDIRECT) {
     619           5 :                                 zv = Z_INDIRECT_P(zv);
     620             :                         }
     621      144379 :                         ref = Z_COUNTED_P(zv);
     622      144379 :                         goto tail_call;
     623             :                 }
     624             :         }
     625             : }
     626             : 
     627          80 : static void gc_scan_roots(void)
     628             : {
     629          80 :         gc_root_buffer *current = GC_G(roots).next;
     630             : 
     631      273269 :         while (current != &GC_G(roots)) {
     632      273109 :                 gc_scan(current->ref);
     633      273109 :                 current = current->next;
     634             :         }
     635          80 : }
     636             : 
     637       92030 : static void gc_add_garbage(zend_refcounted *ref, gc_additional_buffer **additional_buffer)
     638             : {
     639       92030 :         gc_root_buffer *buf = GC_G(unused);
     640             : 
     641       92030 :         if (buf) {
     642       14738 :                 GC_G(unused) = buf->prev;
     643             : #if 1
     644             :                 /* optimization: color is already GC_BLACK (0) */
     645       14738 :                 GC_INFO(ref) = buf - GC_G(buf);
     646             : #else
     647             :                 GC_REF_SET_ADDRESS(ref, buf - GC_G(buf));
     648             : #endif
     649       77292 :         } else if (GC_G(first_unused) != GC_G(last_unused)) {
     650           6 :                 buf = GC_G(first_unused);
     651           6 :                 GC_G(first_unused)++;
     652             : #if 1
     653             :                 /* optimization: color is already GC_BLACK (0) */
     654           6 :                 GC_INFO(ref) = buf - GC_G(buf);
     655             : #else
     656             :                 GC_REF_SET_ADDRESS(ref, buf - GC_G(buf));
     657             : #endif
     658             :         } else {
     659             :                 /* If we don't have free slots in the buffer, allocate a new one and
     660             :                  * set it's address to GC_ROOT_BUFFER_MAX_ENTRIES that have special
     661             :                  * meaning.
     662             :                  */
     663       77286 :                 if (!*additional_buffer || (*additional_buffer)->used == GC_NUM_ADDITIONAL_ENTRIES) {
     664         611 :                         gc_additional_buffer *new_buffer = emalloc(sizeof(gc_additional_buffer));
     665         611 :                         new_buffer->used = 0;
     666         611 :                         new_buffer->next = *additional_buffer;
     667         611 :                         *additional_buffer = new_buffer;
     668             :                 }
     669       77286 :                 buf = (*additional_buffer)->buf + (*additional_buffer)->used;
     670       77286 :                 (*additional_buffer)->used++;
     671             : #if 1
     672             :                 /* optimization: color is already GC_BLACK (0) */
     673       77286 :                 GC_INFO(ref) = GC_ROOT_BUFFER_MAX_ENTRIES;
     674             : #else
     675             :                 GC_REF_SET_ADDRESS(ref, GC_ROOT_BUFFER_MAX_ENTRIES);
     676             : #endif
     677             :                 /* modify type to prevent indirect destruction */
     678       77286 :                 GC_TYPE(ref) |= GC_FAKE_BUFFER_FLAG;
     679             :         }
     680       92030 :         if (buf) {
     681       92030 :                 GC_REFCOUNT(ref)++;
     682       92030 :                 buf->ref = ref;
     683       92030 :                 buf->next = GC_G(roots).next;
     684       92030 :                 buf->prev = &GC_G(roots);
     685       92030 :                 GC_G(roots).next->prev = buf;
     686       92030 :                 GC_G(roots).next = buf;
     687             :         }
     688       92030 : }
     689             : 
     690      223845 : static int gc_collect_white(zend_refcounted *ref, uint32_t *flags, gc_additional_buffer **additional_buffer)
     691             : {
     692      223845 :         int count = 0;
     693             :         HashTable *ht;
     694             :         Bucket *p, *end;
     695             :         zval *zv;
     696             : 
     697             : tail_call:
     698      483192 :         if (GC_REF_GET_COLOR(ref) == GC_WHITE) {
     699      269354 :                 ht = NULL;
     700      269354 :                 GC_REF_SET_BLACK(ref);
     701             : 
     702             :                 /* don't count references for compatibility ??? */
     703      269354 :                 if (GC_TYPE(ref) != IS_REFERENCE) {
     704      218344 :                         count++;
     705             :                 }
     706             : 
     707      279376 :                 if (GC_TYPE(ref) == IS_OBJECT && !(GC_FLAGS(ref) & IS_OBJ_FREE_CALLED)) {
     708             :                         zend_object_get_gc_t get_gc;
     709       88682 :                         zend_object *obj = (zend_object*)ref;
     710             : 
     711       98704 :                         if (EXPECTED(IS_OBJ_VALID(EG(objects_store).object_buckets[obj->handle]) &&
     712             :                                      (get_gc = obj->handlers->get_gc) != NULL)) {
     713             :                                 int n;
     714             :                                 zval *zv, *end;
     715             :                                 zval tmp;
     716             : 
     717             : #if 1
     718             :                                 /* optimization: color is GC_BLACK (0) */
     719       88682 :                                 if (!GC_INFO(ref)) {
     720             : #else
     721             :                                 if (!GC_ADDRESS(GC_INFO(ref))) {
     722             : #endif
     723       13377 :                                         gc_add_garbage(ref, additional_buffer);
     724             :                                 }
     725      266046 :                                 if (obj->handlers->dtor_obj &&
     726       88682 :                                     ((obj->handlers->dtor_obj != zend_objects_destroy_object) ||
     727       88682 :                                      (obj->ce->destructor != NULL))) {
     728       88658 :                                         *flags |= GC_HAS_DESTRUCTORS;
     729             :                                 }
     730       88682 :                                 ZVAL_OBJ(&tmp, obj);
     731       88682 :                                 ht = get_gc(&tmp, &zv, &n);
     732       88682 :                                 end = zv + n;
     733       88682 :                                 if (EXPECTED(!ht)) {
     734       78660 :                                         if (!n) return count;
     735      157319 :                                         while (!Z_REFCOUNTED_P(--end)) {
     736             :                                                 /* count non-refcounted for compatibility ??? */
     737           2 :                                                 if (Z_TYPE_P(zv) != IS_UNDEF) {
     738           1 :                                                         count++;
     739             :                                                 }
     740           1 :                                                 if (zv == end) return count;
     741             :                                         }
     742             :                                 }
     743      256018 :                                 while (zv != end) {
     744       78656 :                                         if (Z_REFCOUNTED_P(zv)) {
     745          10 :                                                 ref = Z_COUNTED_P(zv);
     746          10 :                                                 GC_REFCOUNT(ref)++;
     747          10 :                                                 count += gc_collect_white(ref, flags, additional_buffer);
     748             :                                         /* count non-refcounted for compatibility ??? */
     749      157292 :                                         } else if (Z_TYPE_P(zv) != IS_UNDEF) {
     750       78646 :                                                 count++;
     751             :                                         }
     752       78656 :                                         zv++;
     753             :                                 }
     754       88681 :                                 if (EXPECTED(!ht)) {
     755       78659 :                                         ref = Z_COUNTED_P(zv);
     756       78659 :                                         GC_REFCOUNT(ref)++;
     757       78659 :                                         goto tail_call;
     758             :                                 }
     759             :                         } else {
     760           0 :                                 return count;
     761             :                         }
     762      180672 :                 } else if (GC_TYPE(ref) == IS_ARRAY) {
     763             : #if 1
     764             :                                 /* optimization: color is GC_BLACK (0) */
     765      129660 :                                 if (!GC_INFO(ref)) {
     766             : #else
     767             :                                 if (!GC_ADDRESS(GC_INFO(ref))) {
     768             : #endif
     769       78653 :                                 gc_add_garbage(ref, additional_buffer);
     770             :                         }
     771      129660 :                         ht = (zend_array*)ref;
     772       51012 :                 } else if (GC_TYPE(ref) == IS_REFERENCE) {
     773       51010 :                         if (Z_REFCOUNTED(((zend_reference*)ref)->val)) {
     774       51010 :                                 ref = Z_COUNTED(((zend_reference*)ref)->val);
     775       51010 :                                 GC_REFCOUNT(ref)++;
     776       51010 :                                 goto tail_call;
     777             :                         }
     778           0 :                         return count;
     779             :                 } else {
     780           2 :                         return count;
     781             :                 }
     782             : 
     783      139682 :                 if (!ht->nNumUsed) return count;
     784      139676 :                 p = ht->arData;
     785      139676 :                 end = p + ht->nNumUsed;
     786             :                 while (1) {
     787      159675 :                         end--;
     788      159675 :                         zv = &end->val;
     789      159675 :                         if (Z_TYPE_P(zv) == IS_INDIRECT) {
     790       10002 :                                 zv = Z_INDIRECT_P(zv);
     791             :                         }
     792      159675 :                         if (Z_REFCOUNTED_P(zv)) {
     793      129678 :                                 break;
     794             :                         }
     795             :                         /* count non-refcounted for compatibility ??? */
     796       29997 :                         if (Z_TYPE_P(zv) != IS_UNDEF) {
     797       29997 :                                 count++;
     798             :                         }
     799       29997 :                         if (p == end) return count;
     800       19999 :                 }
     801      357989 :                 while (p != end) {
     802       98633 :                         zv = &p->val;
     803       98633 :                         if (Z_TYPE_P(zv) == IS_INDIRECT) {
     804           0 :                                 zv = Z_INDIRECT_P(zv);
     805             :                         }
     806       98633 :                         if (Z_REFCOUNTED_P(zv)) {
     807       98633 :                                 ref = Z_COUNTED_P(zv);
     808       98633 :                                 GC_REFCOUNT(ref)++;
     809       98633 :                                 count += gc_collect_white(ref, flags, additional_buffer);
     810             :                                 /* count non-refcounted for compatibility ??? */
     811           0 :                         } else if (Z_TYPE_P(zv) != IS_UNDEF) {
     812           0 :                                 count++;
     813             :                         }
     814       98633 :                         p++;
     815             :                 }
     816      129678 :                 zv = &p->val;
     817      129678 :                 if (Z_TYPE_P(zv) == IS_INDIRECT) {
     818           5 :                         zv = Z_INDIRECT_P(zv);
     819             :                 }
     820      129678 :                 ref = Z_COUNTED_P(zv);
     821      129678 :                 GC_REFCOUNT(ref)++;
     822      129678 :                 goto tail_call;
     823             :         }
     824      213838 :         return count;
     825             : }
     826             : 
     827          80 : static int gc_collect_roots(uint32_t *flags, gc_additional_buffer **additional_buffer)
     828             : {
     829          80 :         int count = 0;
     830          80 :         gc_root_buffer *current = GC_G(roots).next;
     831             : 
     832             :         /* remove non-garbage from the list */
     833      273269 :         while (current != &GC_G(roots)) {
     834      273109 :                 gc_root_buffer *next = current->next;
     835      273109 :                 if (GC_REF_GET_COLOR(current->ref) == GC_BLACK) {
     836      146797 :                         GC_INFO(current->ref) = 0; /* reset GC_ADDRESS() and keep GC_BLACK */
     837             :                         GC_REMOVE_FROM_ROOTS(current);
     838             :                 }
     839      273109 :                 current = next;
     840             :         }
     841             : 
     842          80 :         current = GC_G(roots).next;
     843      126472 :         while (current != &GC_G(roots)) {
     844      126312 :                 GC_REFCOUNT(current->ref)++;
     845      126312 :                 if (GC_REF_GET_COLOR(current->ref) == GC_WHITE) {
     846      125202 :                         count += gc_collect_white(current->ref, flags, additional_buffer);
     847             :                 }
     848      126312 :                 current = current->next;
     849             :         }
     850             : 
     851             :         /* relink remaining roots into list to free */
     852          80 :         if (GC_G(roots).next != &GC_G(roots)) {
     853          50 :                 if (GC_G(to_free).next == &GC_G(to_free)) {
     854             :                         /* move roots into list to free */
     855          50 :                         GC_G(to_free).next = GC_G(roots).next;
     856          50 :                         GC_G(to_free).prev = GC_G(roots).prev;
     857          50 :                         GC_G(to_free).next->prev = &GC_G(to_free);
     858          50 :                         GC_G(to_free).prev->next = &GC_G(to_free);
     859             :                 } else {
     860             :                         /* add roots into list to free */
     861           0 :                         GC_G(to_free).prev->next = GC_G(roots).next;
     862           0 :                         GC_G(roots).next->prev = GC_G(to_free).prev;
     863           0 :                         GC_G(roots).prev->next = &GC_G(to_free);
     864           0 :                         GC_G(to_free).prev = GC_G(roots).prev;
     865             :                 }
     866             : 
     867          50 :                 GC_G(roots).next = &GC_G(roots);
     868          50 :                 GC_G(roots).prev = &GC_G(roots);
     869             :         }
     870          80 :         return count;
     871             : }
     872             : 
     873           4 : static void gc_remove_nested_data_from_buffer(zend_refcounted *ref, gc_root_buffer *root)
     874             : {
     875           4 :         HashTable *ht = NULL;
     876             :         Bucket *p, *end;
     877             :         zval *zv;
     878             : 
     879             : tail_call:
     880          21 :         if (root ||
     881           6 :             (GC_ADDRESS(GC_INFO(ref)) != 0 &&
     882           3 :              GC_REF_GET_COLOR(ref) == GC_BLACK &&
     883           3 :              GC_ADDRESS(GC_INFO(ref)) != GC_ROOT_BUFFER_MAX_ENTRIES)) {
     884             :                 GC_TRACE_REF(ref, "removing from buffer");
     885           6 :                 GC_REFCOUNT(ref)--;
     886           6 :                 if (root) {
     887           3 :                         GC_INFO(ref) = 0;
     888             :                         GC_REMOVE_FROM_ROOTS(root);
     889           3 :                         root = NULL;
     890             :                 } else {
     891           3 :                         GC_REMOVE_FROM_BUFFER(ref);
     892             :                 }
     893             : 
     894           9 :                 if (GC_TYPE(ref) == IS_OBJECT && !(GC_FLAGS(ref) & IS_OBJ_FREE_CALLED)) {
     895             :                         zend_object_get_gc_t get_gc;
     896           4 :                         zend_object *obj = (zend_object*)ref;
     897             : 
     898           7 :                         if (EXPECTED(IS_OBJ_VALID(EG(objects_store).object_buckets[obj->handle]) &&
     899             :                              (get_gc = obj->handlers->get_gc) != NULL)) {
     900             :                                 int n;
     901             :                                 zval *zv, *end;
     902             :                                 zval tmp;
     903             : 
     904           4 :                                 ZVAL_OBJ(&tmp, obj);
     905           4 :                                 ht = get_gc(&tmp, &zv, &n);
     906           4 :                                 end = zv + n;
     907           4 :                                 if (EXPECTED(!ht)) {
     908           1 :                                         if (!n) return;
     909           0 :                                         while (!Z_REFCOUNTED_P(--end)) {
     910           0 :                                                 if (zv == end) return;
     911             :                                         }
     912             :                                 }
     913           6 :                                 while (zv != end) {
     914           0 :                                         if (Z_REFCOUNTED_P(zv)) {
     915           0 :                                                 ref = Z_COUNTED_P(zv);
     916           0 :                                                 gc_remove_nested_data_from_buffer(ref, NULL);
     917             :                                         }
     918           0 :                                         zv++;
     919             :                                 }
     920           3 :                                 if (EXPECTED(!ht)) {
     921           0 :                                         ref = Z_COUNTED_P(zv);
     922           0 :                                         goto tail_call;
     923             :                                 }
     924             :                         } else {
     925           0 :                                 return;
     926             :                         }
     927           2 :                 } else if (GC_TYPE(ref) == IS_ARRAY) {
     928           2 :                         ht = (zend_array*)ref;
     929           0 :                 } else if (GC_TYPE(ref) == IS_REFERENCE) {
     930           0 :                         if (Z_REFCOUNTED(((zend_reference*)ref)->val)) {
     931           0 :                                 ref = Z_COUNTED(((zend_reference*)ref)->val);
     932           0 :                                 goto tail_call;
     933             :                         }
     934           0 :                         return;
     935             :                 } else {
     936           0 :                         return;
     937             :                 }
     938             : 
     939           5 :                 if (!ht->nNumUsed) return;
     940           5 :                 p = ht->arData;
     941           5 :                 end = p + ht->nNumUsed;
     942             :                 while (1) {
     943           5 :                         end--;
     944           5 :                         zv = &end->val;
     945           5 :                         if (Z_TYPE_P(zv) == IS_INDIRECT) {
     946           2 :                                 zv = Z_INDIRECT_P(zv);
     947             :                         }
     948           5 :                         if (Z_REFCOUNTED_P(zv)) {
     949           5 :                                 break;
     950             :                         }
     951           0 :                         if (p == end) return;
     952           0 :                 }
     953          11 :                 while (p != end) {
     954           1 :                         zv = &p->val;
     955           1 :                         if (Z_TYPE_P(zv) == IS_INDIRECT) {
     956           0 :                                 zv = Z_INDIRECT_P(zv);
     957             :                         }
     958           1 :                         if (Z_REFCOUNTED_P(zv)) {
     959           1 :                                 ref = Z_COUNTED_P(zv);
     960           1 :                                 gc_remove_nested_data_from_buffer(ref, NULL);
     961             :                         }
     962           1 :                         p++;
     963             :                 }
     964           5 :                 zv = &p->val;
     965           5 :                 if (Z_TYPE_P(zv) == IS_INDIRECT) {
     966           2 :                         zv = Z_INDIRECT_P(zv);
     967             :                 }
     968           5 :                 ref = Z_COUNTED_P(zv);
     969           5 :                 goto tail_call;
     970             :         }
     971             : }
     972             : 
     973          85 : ZEND_API int zend_gc_collect_cycles(void)
     974             : {
     975          85 :         int count = 0;
     976             : 
     977          85 :         if (GC_G(roots).next != &GC_G(roots)) {
     978             :                 gc_root_buffer *current, *next, *orig_next_to_free;
     979             :                 zend_refcounted *p;
     980             :                 gc_root_buffer to_free;
     981          80 :                 uint32_t gc_flags = 0;
     982             :                 gc_additional_buffer *additional_buffer;
     983             : #if ZEND_GC_DEBUG
     984             :                 zend_bool orig_gc_full;
     985             : #endif
     986             : 
     987          80 :                 if (GC_G(gc_active)) {
     988           0 :                         return 0;
     989             :                 }
     990             : 
     991             :                 GC_TRACE("Collecting cycles");
     992          80 :                 GC_G(gc_runs)++;
     993          80 :                 GC_G(gc_active) = 1;
     994             : 
     995             :                 GC_TRACE("Marking roots");
     996          80 :                 gc_mark_roots();
     997             :                 GC_TRACE("Scanning roots");
     998          80 :                 gc_scan_roots();
     999             : 
    1000             : #if ZEND_GC_DEBUG
    1001             :                 orig_gc_full = GC_G(gc_full);
    1002             :                 GC_G(gc_full) = 0;
    1003             : #endif
    1004             : 
    1005             :                 GC_TRACE("Collecting roots");
    1006          80 :                 additional_buffer = NULL;
    1007          80 :                 count = gc_collect_roots(&gc_flags, &additional_buffer);
    1008             : #if ZEND_GC_DEBUG
    1009             :                 GC_G(gc_full) = orig_gc_full;
    1010             : #endif
    1011          80 :                 GC_G(gc_active) = 0;
    1012             : 
    1013          80 :                 if (GC_G(to_free).next == &GC_G(to_free)) {
    1014             :                         /* nothing to free */
    1015             :                         GC_TRACE("Nothing to free");
    1016          30 :                         return 0;
    1017             :                 }
    1018             : 
    1019             :                 /* Copy global to_free list into local list */
    1020          50 :                 to_free.next = GC_G(to_free).next;
    1021          50 :                 to_free.prev = GC_G(to_free).prev;
    1022          50 :                 to_free.next->prev = &to_free;
    1023          50 :                 to_free.prev->next = &to_free;
    1024             : 
    1025             :                 /* Free global list */
    1026          50 :                 GC_G(to_free).next = &GC_G(to_free);
    1027          50 :                 GC_G(to_free).prev = &GC_G(to_free);
    1028             : 
    1029          50 :                 orig_next_to_free = GC_G(next_to_free);
    1030             : 
    1031             : #if ZEND_GC_DEBUG
    1032             :                 orig_gc_full = GC_G(gc_full);
    1033             :                 GC_G(gc_full) = 0;
    1034             : #endif
    1035             : 
    1036          50 :                 if (gc_flags & GC_HAS_DESTRUCTORS) {
    1037             :                         GC_TRACE("Calling destructors");
    1038          21 :                         if (EG(objects_store).object_buckets) {
    1039             :                                 /* Remember reference counters before calling destructors */
    1040          21 :                                 current = to_free.next;
    1041      167354 :                                 while (current != &to_free) {
    1042      167312 :                                         current->refcount = GC_REFCOUNT(current->ref);
    1043      167312 :                                         current = current->next;
    1044             :                                 }
    1045             : 
    1046             :                                 /* Call destructors */
    1047          21 :                                 current = to_free.next;
    1048      167354 :                                 while (current != &to_free) {
    1049      167312 :                                         p = current->ref;
    1050      167312 :                                         GC_G(next_to_free) = current->next;
    1051      167312 :                                         if ((GC_TYPE(p) & GC_TYPE_MASK) == IS_OBJECT) {
    1052       88662 :                                                 zend_object *obj = (zend_object*)p;
    1053             : 
    1054      177324 :                                                 if (IS_OBJ_VALID(EG(objects_store).object_buckets[obj->handle]) &&
    1055       88662 :                                                         !(GC_FLAGS(obj) & IS_OBJ_DESTRUCTOR_CALLED)) {
    1056             :                                                         GC_TRACE_REF(obj, "calling destructor");
    1057       78665 :                                                         GC_FLAGS(obj) |= IS_OBJ_DESTRUCTOR_CALLED;
    1058       78665 :                                                         if (obj->handlers->dtor_obj) {
    1059       78665 :                                                                 GC_REFCOUNT(obj)++;
    1060       78665 :                                                                 obj->handlers->dtor_obj(obj);
    1061       78665 :                                                                 GC_REFCOUNT(obj)--;
    1062             :                                                         }
    1063             :                                                 }
    1064             :                                         }
    1065      167312 :                                         current = GC_G(next_to_free);
    1066             :                                 }
    1067             : 
    1068             :                                 /* Remove values captured in destructors */
    1069          21 :                                 current = to_free.next;
    1070      167352 :                                 while (current != &to_free) {
    1071      167310 :                                         GC_G(next_to_free) = current->next;
    1072      167310 :                                         if (GC_REFCOUNT(current->ref) > current->refcount) {
    1073           3 :                                                 gc_remove_nested_data_from_buffer(current->ref, current);
    1074             :                                         }
    1075      167310 :                                         current = GC_G(next_to_free);
    1076             :                                 }
    1077             :                         }
    1078             :                 }
    1079             : 
    1080             :                 /* Destroy zvals */
    1081             :                 GC_TRACE("Destroying zvals");
    1082          50 :                 GC_G(gc_active) = 1;
    1083          50 :                 current = to_free.next;
    1084      218436 :                 while (current != &to_free) {
    1085      218336 :                         p = current->ref;
    1086      218336 :                         GC_G(next_to_free) = current->next;
    1087             :                         GC_TRACE_REF(p, "destroying");
    1088      218336 :                         if ((GC_TYPE(p) & GC_TYPE_MASK) == IS_OBJECT) {
    1089       88678 :                                 zend_object *obj = (zend_object*)p;
    1090             : 
    1091      177356 :                                 if (EG(objects_store).object_buckets &&
    1092       88678 :                                     IS_OBJ_VALID(EG(objects_store).object_buckets[obj->handle])) {
    1093       88678 :                                         GC_TYPE(obj) = IS_NULL;
    1094       88678 :                                         if (!(GC_FLAGS(obj) & IS_OBJ_FREE_CALLED)) {
    1095       88678 :                                                 GC_FLAGS(obj) |= IS_OBJ_FREE_CALLED;
    1096       88678 :                                                 if (obj->handlers->free_obj) {
    1097       88678 :                                                         GC_REFCOUNT(obj)++;
    1098       88678 :                                                         obj->handlers->free_obj(obj);
    1099       88678 :                                                         GC_REFCOUNT(obj)--;
    1100             :                                                 }
    1101             :                                         }
    1102       88678 :                                         SET_OBJ_BUCKET_NUMBER(EG(objects_store).object_buckets[obj->handle], EG(objects_store).free_list_head);
    1103       88678 :                                         EG(objects_store).free_list_head = obj->handle;
    1104       88678 :                                         p = current->ref = (zend_refcounted*)(((char*)obj) - obj->handlers->offset);
    1105             :                                 }
    1106      129658 :                         } else if ((GC_TYPE(p) & GC_TYPE_MASK) == IS_ARRAY) {
    1107      129658 :                                 zend_array *arr = (zend_array*)p;
    1108             : 
    1109      129658 :                                 GC_TYPE(arr) = IS_NULL;
    1110      129658 :                                 zend_hash_destroy(arr);
    1111             :                         }
    1112      218336 :                         current = GC_G(next_to_free);
    1113             :                 }
    1114             : 
    1115             :                 /* Free objects */
    1116          50 :                 current = to_free.next;
    1117      218436 :                 while (current != &to_free) {
    1118      218336 :                         next = current->next;
    1119      218336 :                         p = current->ref;
    1120      218336 :                         if (EXPECTED(current >= GC_G(buf) && current < GC_G(buf) + GC_ROOT_BUFFER_MAX_ENTRIES)) {
    1121      141050 :                                 current->prev = GC_G(unused);
    1122      141050 :                                 GC_G(unused) = current;
    1123             :                         }
    1124      218336 :                         efree(p);
    1125      218336 :                         current = next;
    1126             :                 }
    1127             : 
    1128         711 :                 while (additional_buffer != NULL) {
    1129         611 :                         gc_additional_buffer *next = additional_buffer->next;
    1130         611 :                         efree(additional_buffer);
    1131         611 :                         additional_buffer = next;
    1132             :                 }
    1133             : 
    1134             :                 GC_TRACE("Collection finished");
    1135          50 :                 GC_G(collected) += count;
    1136          50 :                 GC_G(next_to_free) = orig_next_to_free;
    1137             : #if ZEND_GC_DEBUG
    1138             :                 GC_G(gc_full) = orig_gc_full;
    1139             : #endif
    1140          50 :                 GC_G(gc_active) = 0;
    1141             :         }
    1142             : 
    1143          55 :         return count;
    1144             : }
    1145             : 
    1146             : /*
    1147             :  * Local variables:
    1148             :  * tab-width: 4
    1149             :  * c-basic-offset: 4
    1150             :  * indent-tabs-mode: t
    1151             :  * End:
    1152             :  */

Generated by: LCOV version 1.10

Generated at Sat, 27 Jun 2015 09:41:07 +0000 (6 days ago)

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