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 579 91.4 %
Date: 2016-08-28 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             : 
      23             : /**
      24             :  * zend_gc_collect_cycles
      25             :  * ======================
      26             :  *
      27             :  * Colors and its meaning
      28             :  * ----------------------
      29             :  *
      30             :  * BLACK  (GC_BLACK)   - In use or free.
      31             :  * GREY   (GC_GREY)    - Possible member of cycle.
      32             :  * WHITE  (GC_WHITE)   - Member of garbage cycle.
      33             :  * PURPLE (GC_PURPLE)  - Possible root of cycle.
      34             :  *
      35             :  * Colors described in the paper but not used
      36             :  * ------------------------------------------
      37             :  *
      38             :  * GREEN - Acyclic
      39             :  * RED   - Candidate cycle underogin
      40             :  * ORANGE - Candidate cycle awaiting epoch boundary.
      41             :  *
      42             :  *
      43             :  * Flow
      44             :  * =====
      45             :  *
      46             :  * The garbage collect cycle starts from 'gc_mark_roots', which traverses the
      47             :  * possible roots, and calls mark_grey for roots are marked purple with
      48             :  * depth-first traverse.
      49             :  *
      50             :  * After all possible roots are traversed and marked,
      51             :  * gc_scan_roots will be called, and each root will be called with
      52             :  * gc_scan(root->ref)
      53             :  *
      54             :  * gc_scan checkes the colors of possible members.
      55             :  *
      56             :  * If the node is marked as grey and the refcount > 0
      57             :  *    gc_scan_black will be called on that node to scan it's subgraph.
      58             :  * otherwise (refcount == 0), it marks the node white.
      59             :  *
      60             :  * A node MAY be added to possbile roots when ZEND_UNSET_VAR happens or
      61             :  * zend_assign_to_variable is called only when possible garbage node is
      62             :  * produced.
      63             :  * gc_possible_root() will be called to add the nodes to possible roots.
      64             :  *
      65             :  *
      66             :  * For objects, we call their get_gc handler (by default 'zend_std_get_gc') to
      67             :  * get the object properties to scan.
      68             :  *
      69             :  *
      70             :  * @see http://researcher.watson.ibm.com/researcher/files/us-bacon/Bacon01Concurrent.pdf
      71             :  */
      72             : #include "zend.h"
      73             : #include "zend_API.h"
      74             : 
      75             : /* one (0) is reserved */
      76             : #define GC_ROOT_BUFFER_MAX_ENTRIES 10001
      77             : 
      78             : #define GC_FAKE_BUFFER_FLAG 0x80
      79             : #define GC_TYPE_MASK        0x7f
      80             : 
      81             : #define GC_HAS_DESTRUCTORS  (1<<0)
      82             : 
      83             : #ifndef ZEND_GC_DEBUG
      84             : # define ZEND_GC_DEBUG 0
      85             : #endif
      86             : 
      87             : #define GC_NUM_ADDITIONAL_ENTRIES \
      88             :         ((4096 - ZEND_MM_OVERHEAD - sizeof(void*) * 2) / sizeof(gc_root_buffer))
      89             : 
      90             : typedef struct _gc_addtional_bufer gc_additional_buffer;
      91             : 
      92             : struct _gc_addtional_bufer {
      93             :         uint32_t              used;
      94             :         gc_additional_buffer *next;
      95             :         gc_root_buffer        buf[GC_NUM_ADDITIONAL_ENTRIES];
      96             : };
      97             : 
      98             : #ifdef ZTS
      99             : ZEND_API int gc_globals_id;
     100             : #else
     101             : ZEND_API zend_gc_globals gc_globals;
     102             : #endif
     103             : 
     104             : ZEND_API int (*gc_collect_cycles)(void);
     105             : 
     106             : #define GC_REMOVE_FROM_ROOTS(current) \
     107             :         gc_remove_from_roots((current))
     108             : 
     109             : #if ZEND_GC_DEBUG > 1
     110             : # define GC_TRACE(format, ...) fprintf(stderr, format "\n", ##__VA_ARGS__);
     111             : # define GC_TRACE_REF(ref, format, ...) \
     112             :         do { \
     113             :                 gc_trace_ref((zend_refcounted *) ref); \
     114             :                 fprintf(stderr, format "\n", ##__VA_ARGS__); \
     115             :         } while (0)
     116             : # define GC_TRACE_SET_COLOR(ref, color) \
     117             :         GC_TRACE_REF(ref, "->%s", gc_color_name(color))
     118             : #else
     119             : # define GC_TRACE_REF(ref, format, ...)
     120             : # define GC_TRACE_SET_COLOR(ref, new_color)
     121             : # define GC_TRACE(str)
     122             : #endif
     123             : 
     124             : #define GC_REF_SET_ADDRESS(ref, a) \
     125             :         GC_INFO_SET_ADDRESS(GC_INFO(ref), a)
     126             : #define GC_REF_GET_COLOR(ref) \
     127             :         GC_INFO_GET_COLOR(GC_INFO(ref))
     128             : #define GC_REF_SET_COLOR(ref, c) \
     129             :         do { GC_TRACE_SET_COLOR(ref, c); GC_INFO_SET_COLOR(GC_INFO(ref), c); } while (0)
     130             : #define GC_REF_SET_BLACK(ref) \
     131             :         do { GC_TRACE_SET_COLOR(ref, GC_BLACK); GC_INFO_SET_BLACK(GC_INFO(ref)); } while (0)
     132             : #define GC_REF_SET_PURPLE(ref) \
     133             :         do { GC_TRACE_SET_COLOR(ref, GC_PURPLE); GC_INFO_SET_PURPLE(GC_INFO(ref)); } while (0)
     134             : 
     135             : #if ZEND_GC_DEBUG > 1
     136             : static const char *gc_color_name(uint32_t color) {
     137             :         switch (color) {
     138             :                 case GC_BLACK: return "black";
     139             :                 case GC_WHITE: return "white";
     140             :                 case GC_GREY: return "grey";
     141             :                 case GC_PURPLE: return "purple";
     142             :                 default: return "unknown";
     143             :         }
     144             : }
     145             : static void gc_trace_ref(zend_refcounted *ref) {
     146             :         if (GC_TYPE(ref) == IS_OBJECT) {
     147             :                 zend_object *obj = (zend_object *) ref;
     148             :                 fprintf(stderr, "[%p] rc=%d addr=%d %s object(%s)#%d ",
     149             :                         ref, GC_REFCOUNT(ref), GC_ADDRESS(GC_INFO(ref)),
     150             :                         gc_color_name(GC_REF_GET_COLOR(ref)),
     151             :                         obj->ce->name->val, obj->handle);
     152             :         } else if (GC_TYPE(ref) == IS_ARRAY) {
     153             :                 zend_array *arr = (zend_array *) ref;
     154             :                 fprintf(stderr, "[%p] rc=%d addr=%d %s array(%d) ",
     155             :                         ref, GC_REFCOUNT(ref), GC_ADDRESS(GC_INFO(ref)),
     156             :                         gc_color_name(GC_REF_GET_COLOR(ref)),
     157             :                         zend_hash_num_elements(arr));
     158             :         } else {
     159             :                 fprintf(stderr, "[%p] rc=%d addr=%d %s %s ",
     160             :                         ref, GC_REFCOUNT(ref), GC_ADDRESS(GC_INFO(ref)),
     161             :                         gc_color_name(GC_REF_GET_COLOR(ref)),
     162             :                         zend_get_type_by_const(GC_TYPE(ref)));
     163             :         }
     164             : }
     165             : #endif
     166             : 
     167             : static zend_always_inline void gc_remove_from_roots(gc_root_buffer *root)
     168             : {
     169     1856301 :         root->next->prev = root->prev;
     170     1856301 :         root->prev->next = root->next;
     171     1856301 :         root->prev = GC_G(unused);
     172     1856301 :         GC_G(unused) = root;
     173             :         GC_BENCH_DEC(root_buf_length);
     174             : }
     175             : 
     176       23544 : static void root_buffer_dtor(zend_gc_globals *gc_globals)
     177             : {
     178       23544 :         if (gc_globals->buf) {
     179       23543 :                 free(gc_globals->buf);
     180       23543 :                 gc_globals->buf = NULL;
     181             :         }
     182       23544 : }
     183             : 
     184       23504 : static void gc_globals_ctor_ex(zend_gc_globals *gc_globals)
     185             : {
     186       23504 :         gc_globals->gc_enabled = 0;
     187       23504 :         gc_globals->gc_active = 0;
     188             : 
     189       23504 :         gc_globals->buf = NULL;
     190             : 
     191       23504 :         gc_globals->roots.next = &gc_globals->roots;
     192       23504 :         gc_globals->roots.prev = &gc_globals->roots;
     193       23504 :         gc_globals->unused = NULL;
     194       23504 :         gc_globals->next_to_free = NULL;
     195             : 
     196       23504 :         gc_globals->to_free.next = &gc_globals->to_free;
     197       23504 :         gc_globals->to_free.prev = &gc_globals->to_free;
     198             : 
     199       23504 :         gc_globals->gc_runs = 0;
     200       23504 :         gc_globals->collected = 0;
     201             : 
     202             : #if GC_BENCH
     203             :         gc_globals->root_buf_length = 0;
     204             :         gc_globals->root_buf_peak = 0;
     205             :         gc_globals->zval_possible_root = 0;
     206             :         gc_globals->zval_buffered = 0;
     207             :         gc_globals->zval_remove_from_buffer = 0;
     208             :         gc_globals->zval_marked_grey = 0;
     209             : #endif
     210       23504 : }
     211             : 
     212       23504 : ZEND_API void gc_globals_ctor(void)
     213             : {
     214             : #ifdef ZTS
     215             :         ts_allocate_id(&gc_globals_id, sizeof(zend_gc_globals), (ts_allocate_ctor) gc_globals_ctor_ex, (ts_allocate_dtor) root_buffer_dtor);
     216             : #else
     217       23504 :         gc_globals_ctor_ex(&gc_globals);
     218             : #endif
     219       23504 : }
     220             : 
     221       23544 : ZEND_API void gc_globals_dtor(void)
     222             : {
     223             : #ifndef ZTS
     224       23544 :         root_buffer_dtor(&gc_globals);
     225             : #endif
     226       23544 : }
     227             : 
     228       46961 : ZEND_API void gc_reset(void)
     229             : {
     230       46961 :         GC_G(gc_runs) = 0;
     231       46961 :         GC_G(collected) = 0;
     232       46961 :         GC_G(gc_full) = 0;
     233             : 
     234             : #if GC_BENCH
     235             :         GC_G(root_buf_length) = 0;
     236             :         GC_G(root_buf_peak) = 0;
     237             :         GC_G(zval_possible_root) = 0;
     238             :         GC_G(zval_buffered) = 0;
     239             :         GC_G(zval_remove_from_buffer) = 0;
     240             :         GC_G(zval_marked_grey) = 0;
     241             : #endif
     242             : 
     243       46961 :         GC_G(roots).next = &GC_G(roots);
     244       46961 :         GC_G(roots).prev = &GC_G(roots);
     245             : 
     246       46961 :         GC_G(to_free).next = &GC_G(to_free);
     247       46961 :         GC_G(to_free).prev = &GC_G(to_free);
     248             : 
     249       46961 :         if (GC_G(buf)) {
     250       46959 :                 GC_G(unused) = NULL;
     251       46959 :                 GC_G(first_unused) = GC_G(buf) + 1;
     252             :         } else {
     253           2 :                 GC_G(unused) = NULL;
     254           2 :                 GC_G(first_unused) = NULL;
     255           2 :                 GC_G(last_unused) = NULL;
     256             :         }
     257       46961 : }
     258             : 
     259       23518 : ZEND_API void gc_init(void)
     260             : {
     261       23518 :         if (GC_G(buf) == NULL && GC_G(gc_enabled)) {
     262       23503 :                 GC_G(buf) = (gc_root_buffer*) malloc(sizeof(gc_root_buffer) * GC_ROOT_BUFFER_MAX_ENTRIES);
     263       23503 :                 GC_G(last_unused) = &GC_G(buf)[GC_ROOT_BUFFER_MAX_ENTRIES];
     264       23503 :                 gc_reset();
     265             :         }
     266       23518 : }
     267             : 
     268     1950368 : ZEND_API void ZEND_FASTCALL gc_possible_root(zend_refcounted *ref)
     269             : {
     270             :         gc_root_buffer *newRoot;
     271             : 
     272     1950368 :         if (UNEXPECTED(CG(unclean_shutdown)) || UNEXPECTED(GC_G(gc_active))) {
     273       12321 :                 return;
     274             :         }
     275             : 
     276             :         ZEND_ASSERT(GC_TYPE(ref) == IS_ARRAY || GC_TYPE(ref) == IS_OBJECT);
     277             :         ZEND_ASSERT(EXPECTED(GC_REF_GET_COLOR(ref) == GC_BLACK));
     278             :         ZEND_ASSERT(!GC_ADDRESS(GC_INFO(ref)));
     279             : 
     280             :         GC_BENCH_INC(zval_possible_root);
     281             : 
     282     1938047 :         newRoot = GC_G(unused);
     283     1938047 :         if (newRoot) {
     284     1652552 :                 GC_G(unused) = newRoot->prev;
     285      285495 :         } else if (GC_G(first_unused) != GC_G(last_unused)) {
     286      285444 :                 newRoot = GC_G(first_unused);
     287      285444 :                 GC_G(first_unused)++;
     288             :         } else {
     289          51 :                 if (!GC_G(gc_enabled)) {
     290           8 :                         return;
     291             :                 }
     292          43 :                 GC_REFCOUNT(ref)++;
     293          43 :                 gc_collect_cycles();
     294          43 :                 GC_REFCOUNT(ref)--;
     295          43 :                 if (UNEXPECTED(GC_REFCOUNT(ref)) == 0) {
     296           0 :                         zval_dtor_func(ref);
     297           0 :                         return;
     298             :                 }
     299          43 :                 if (UNEXPECTED(GC_INFO(ref))) {
     300           0 :                         return;
     301             :                 }
     302          43 :                 newRoot = GC_G(unused);
     303          43 :                 if (!newRoot) {
     304             : #if ZEND_GC_DEBUG
     305             :                         if (!GC_G(gc_full)) {
     306             :                                 fprintf(stderr, "GC: no space to record new root candidate\n");
     307             :                                 GC_G(gc_full) = 1;
     308             :                         }
     309             : #endif
     310           0 :                         return;
     311             :                 }
     312          43 :                 GC_G(unused) = newRoot->prev;
     313             :         }
     314             : 
     315             :         GC_TRACE_SET_COLOR(ref, GC_PURPLE);
     316     1938039 :         GC_INFO(ref) = (newRoot - GC_G(buf)) | GC_PURPLE;
     317     1938039 :         newRoot->ref = ref;
     318             : 
     319     1938039 :         newRoot->next = GC_G(roots).next;
     320     1938039 :         newRoot->prev = &GC_G(roots);
     321     1938039 :         GC_G(roots).next->prev = newRoot;
     322     1938039 :         GC_G(roots).next = newRoot;
     323             : 
     324             :         GC_BENCH_INC(zval_buffered);
     325             :         GC_BENCH_INC(root_buf_length);
     326             :         GC_BENCH_PEAK(root_buf_peak, root_buf_length);
     327             : }
     328             : 
     329     1659469 : ZEND_API void ZEND_FASTCALL gc_remove_from_buffer(zend_refcounted *ref)
     330             : {
     331             :         gc_root_buffer *root;
     332             : 
     333             :         ZEND_ASSERT(GC_ADDRESS(GC_INFO(ref)));
     334             :         ZEND_ASSERT(GC_ADDRESS(GC_INFO(ref)) < GC_ROOT_BUFFER_MAX_ENTRIES);
     335             : 
     336             :         GC_BENCH_INC(zval_remove_from_buffer);
     337             : 
     338     1659469 :         root = GC_G(buf) + GC_ADDRESS(GC_INFO(ref));
     339     1659469 :         if (GC_REF_GET_COLOR(ref) != GC_BLACK) {
     340             :                 GC_TRACE_SET_COLOR(ref, GC_PURPLE);
     341             :         }
     342     1659469 :         GC_INFO(ref) = 0;
     343             :         GC_REMOVE_FROM_ROOTS(root);
     344             : 
     345             :         /* updete next root that is going to be freed */
     346     1659469 :         if (GC_G(next_to_free) == root) {
     347          36 :                 GC_G(next_to_free) = root->next;
     348             :         }
     349     1659469 : }
     350             : 
     351      271665 : static void gc_scan_black(zend_refcounted *ref)
     352             : {
     353             :         HashTable *ht;
     354             :         Bucket *p, *end;
     355             :         zval *zv;
     356             : 
     357             : tail_call:
     358      271665 :         ht = NULL;
     359      271665 :         GC_REF_SET_BLACK(ref);
     360             : 
     361      451675 :         if (GC_TYPE(ref) == IS_OBJECT && !(GC_FLAGS(ref) & IS_OBJ_FREE_CALLED)) {
     362             :                 zend_object_get_gc_t get_gc;
     363      206759 :                 zend_object *obj = (zend_object*)ref;
     364             : 
     365      386769 :                 if (EXPECTED(IS_OBJ_VALID(EG(objects_store).object_buckets[obj->handle]) &&
     366             :                              (get_gc = obj->handlers->get_gc) != NULL)) {
     367             :                         int n;
     368             :                         zval *zv, *end;
     369             :                         zval tmp;
     370             : 
     371      206759 :                         ZVAL_OBJ(&tmp, obj);
     372      206759 :                         ht = get_gc(&tmp, &zv, &n);
     373      206759 :                         end = zv + n;
     374      206759 :                         if (EXPECTED(!ht)) {
     375       26749 :                                 if (!n) return;
     376       29497 :                                 while (!Z_REFCOUNTED_P(--end)) {
     377           1 :                                         if (zv == end) return;
     378             :                                 }
     379             :                         }
     380      484259 :                         while (zv != end) {
     381       94743 :                                 if (Z_REFCOUNTED_P(zv)) {
     382       40004 :                                         ref = Z_COUNTED_P(zv);
     383       40004 :                                         GC_REFCOUNT(ref)++;
     384       40004 :                                         if (GC_REF_GET_COLOR(ref) != GC_BLACK) {
     385       10007 :                                                 gc_scan_black(ref);
     386             :                                         }
     387             :                                 }
     388       94743 :                                 zv++;
     389             :                         }
     390      194758 :                         if (EXPECTED(!ht)) {
     391       14748 :                                 ref = Z_COUNTED_P(zv);
     392       14748 :                                 GC_REFCOUNT(ref)++;
     393       14748 :                                 if (GC_REF_GET_COLOR(ref) != GC_BLACK) {
     394       14748 :                                         goto tail_call;
     395             :                                 }
     396           0 :                                 return;
     397             :                         }
     398             :                 } else {
     399           0 :                         return;
     400             :                 }
     401       64906 :         } else if (GC_TYPE(ref) == IS_ARRAY) {
     402       14836 :                 if ((zend_array*)ref != &EG(symbol_table)) {
     403       14836 :                         ht = (zend_array*)ref;
     404             :                 } else {
     405           0 :                         return;
     406             :                 }
     407       50070 :         } else if (GC_TYPE(ref) == IS_REFERENCE) {
     408           6 :                 if (Z_REFCOUNTED(((zend_reference*)ref)->val)) {
     409           4 :                         ref = Z_COUNTED(((zend_reference*)ref)->val);
     410           4 :                         GC_REFCOUNT(ref)++;
     411           4 :                         if (GC_REF_GET_COLOR(ref) != GC_BLACK) {
     412           4 :                                 goto tail_call;
     413             :                         }
     414             :                 }
     415           2 :                 return;
     416             :         } else {
     417       50064 :                 return;
     418             :         }
     419             : 
     420      194846 :         if (!ht->nNumUsed) return;
     421       74840 :         p = ht->arData;
     422       74840 :         end = p + ht->nNumUsed;
     423             :         while (1) {
     424       84856 :                 end--;
     425       84856 :                 zv = &end->val;
     426       84856 :                 if (Z_TYPE_P(zv) == IS_INDIRECT) {
     427       10004 :                         zv = Z_INDIRECT_P(zv);
     428             :                 }
     429       84856 :                 if (Z_REFCOUNTED_P(zv)) {
     430       64835 :                         break;
     431             :                 }
     432       20021 :                 if (p == end) return;
     433       10016 :         }
     434      174419 :         while (p != end) {
     435       44749 :                 zv = &p->val;
     436       44749 :                 if (Z_TYPE_P(zv) == IS_INDIRECT) {
     437           0 :                         zv = Z_INDIRECT_P(zv);
     438             :                 }
     439       44749 :                 if (Z_REFCOUNTED_P(zv)) {
     440       34746 :                         ref = Z_COUNTED_P(zv);
     441       34746 :                         GC_REFCOUNT(ref)++;
     442       34746 :                         if (GC_REF_GET_COLOR(ref) != GC_BLACK) {
     443       33803 :                                 gc_scan_black(ref);
     444             :                         }
     445             :                 }
     446       44749 :                 p++;
     447             :         }
     448       64835 :         zv = &p->val;
     449       64835 :         if (Z_TYPE_P(zv) == IS_INDIRECT) {
     450           2 :                 zv = Z_INDIRECT_P(zv);
     451             :         }
     452       64835 :         ref = Z_COUNTED_P(zv);
     453       64835 :         GC_REFCOUNT(ref)++;
     454       64835 :         if (GC_REF_GET_COLOR(ref) != GC_BLACK) {
     455       50993 :                 goto tail_call;
     456             :         }
     457             : }
     458             : 
     459     1224304 : static void gc_mark_grey(zend_refcounted *ref)
     460             : {
     461             :     HashTable *ht;
     462             :         Bucket *p, *end;
     463             :         zval *zv;
     464             : 
     465             : tail_call:
     466     1224304 :         if (GC_REF_GET_COLOR(ref) != GC_GREY) {
     467      800998 :                 ht = NULL;
     468             :                 GC_BENCH_INC(zval_marked_grey);
     469      800998 :                 GC_REF_SET_COLOR(ref, GC_GREY);
     470             : 
     471      991034 :                 if (GC_TYPE(ref) == IS_OBJECT && !(GC_FLAGS(ref) & IS_OBJ_FREE_CALLED)) {
     472             :                         zend_object_get_gc_t get_gc;
     473      395441 :                         zend_object *obj = (zend_object*)ref;
     474             : 
     475      585477 :                         if (EXPECTED(IS_OBJ_VALID(EG(objects_store).object_buckets[obj->handle]) &&
     476             :                              (get_gc = obj->handlers->get_gc) != NULL)) {
     477             :                                 int n;
     478             :                                 zval *zv, *end;
     479             :                                 zval tmp;
     480             : 
     481      395441 :                                 ZVAL_OBJ(&tmp, obj);
     482      395441 :                                 ht = get_gc(&tmp, &zv, &n);
     483      395441 :                                 end = zv + n;
     484      395441 :                                 if (EXPECTED(!ht)) {
     485      205405 :                                         if (!n) return;
     486      386802 :                                         while (!Z_REFCOUNTED_P(--end)) {
     487           2 :                                                 if (zv == end) return;
     488             :                                         }
     489             :                                 }
     490      940293 :                                 while (zv != end) {
     491      173421 :                                         if (Z_REFCOUNTED_P(zv)) {
     492       40015 :                                                 ref = Z_COUNTED_P(zv);
     493       40015 :                                                 GC_REFCOUNT(ref)--;
     494       40015 :                                                 gc_mark_grey(ref);
     495             :                                         }
     496      173421 :                                         zv++;
     497             :                                 }
     498      383436 :                                 if (EXPECTED(!ht)) {
     499      193400 :                                         ref = Z_COUNTED_P(zv);
     500      193400 :                                         GC_REFCOUNT(ref)--;
     501      193400 :                                         goto tail_call;
     502             :                                 }
     503             :                         } else {
     504           0 :                                 return;
     505             :                         }
     506      405557 :                 } else if (GC_TYPE(ref) == IS_ARRAY) {
     507      274479 :                         if (((zend_array*)ref) == &EG(symbol_table)) {
     508           0 :                                 GC_REF_SET_BLACK(ref);
     509           0 :                                 return;
     510             :                         } else {
     511      274479 :                                 ht = (zend_array*)ref;
     512             :                         }
     513      131078 :                 } else if (GC_TYPE(ref) == IS_REFERENCE) {
     514       81012 :                         if (Z_REFCOUNTED(((zend_reference*)ref)->val)) {
     515       81010 :                                 if (UNEXPECTED(!EG(objects_store).object_buckets) &&
     516           0 :                                         Z_TYPE(((zend_reference*)ref)->val) == IS_OBJECT) {
     517           0 :                                         Z_TYPE_INFO(((zend_reference*)ref)->val) = IS_NULL;
     518           0 :                                         return;
     519             :                                 }
     520       81010 :                                 ref = Z_COUNTED(((zend_reference*)ref)->val);
     521       81010 :                                 GC_REFCOUNT(ref)--;
     522       81010 :                                 goto tail_call;
     523             :                         }
     524           2 :                         return;
     525             :                 } else {
     526       50066 :                         return;
     527             :                 }
     528             : 
     529      464515 :                 if (!ht->nNumUsed) return;
     530      344505 :                 p = ht->arData;
     531      344505 :                 end = p + ht->nNumUsed;
     532             :                 while (1) {
     533      374520 :                         end--;
     534      374520 :                         zv = &end->val;
     535      374520 :                         if (Z_TYPE_P(zv) == IS_INDIRECT) {
     536       20006 :                                 zv = Z_INDIRECT_P(zv);
     537             :                         }
     538      374520 :                         if (Z_REFCOUNTED_P(zv)) {
     539      324502 :                                 break;
     540             :                         }
     541       50018 :                         if (p == end) return;
     542       30015 :                 }
     543      792392 :                 while (p != end) {
     544      143388 :                         zv = &p->val;
     545      143388 :                         if (Z_TYPE_P(zv) == IS_INDIRECT) {
     546           0 :                                 zv = Z_INDIRECT_P(zv);
     547             :                         }
     548      143388 :                         if (Z_REFCOUNTED_P(zv)) {
     549      256758 :                                 if (Z_TYPE_P(zv) == IS_OBJECT &&
     550      123373 :                                     UNEXPECTED(!EG(objects_store).object_buckets)) {
     551           0 :                                         Z_TYPE_INFO_P(zv) = IS_NULL;
     552             :                                 } else {
     553      133385 :                                         ref = Z_COUNTED_P(zv);
     554      133385 :                                         GC_REFCOUNT(ref)--;
     555      133385 :                                         gc_mark_grey(ref);
     556             :                                 }
     557             :                         }
     558      143388 :                         p++;
     559             :                 }
     560      324502 :                 zv = &p->val;
     561      324502 :                 if (Z_TYPE_P(zv) == IS_INDIRECT) {
     562           7 :                         zv = Z_INDIRECT_P(zv);
     563             :                 }
     564      517924 :                 if (Z_TYPE_P(zv) == IS_OBJECT &&
     565      193422 :                     UNEXPECTED(!EG(objects_store).object_buckets)) {
     566           0 :                         Z_TYPE_INFO_P(zv) = IS_NULL;
     567             :                 } else {
     568      324502 :                         ref = Z_COUNTED_P(zv);
     569      324502 :                         GC_REFCOUNT(ref)--;
     570      324502 :                         goto tail_call;
     571             :                 }
     572             :         }
     573             : }
     574             : 
     575         105 : static void gc_mark_roots(void)
     576             : {
     577         105 :         gc_root_buffer *current = GC_G(roots).next;
     578             : 
     579      453339 :         while (current != &GC_G(roots)) {
     580      453129 :                 if (GC_REF_GET_COLOR(current->ref) == GC_PURPLE) {
     581      451992 :                         gc_mark_grey(current->ref);
     582             :                 }
     583      453129 :                 current = current->next;
     584             :         }
     585         105 : }
     586             : 
     587     1115032 : static void gc_scan(zend_refcounted *ref)
     588             : {
     589             :     HashTable *ht;
     590             :         Bucket *p, *end;
     591             :         zval *zv;
     592             : 
     593             : tail_call:
     594     1115032 :         if (GC_REF_GET_COLOR(ref) == GC_GREY) {
     595      740841 :                 if (GC_REFCOUNT(ref) > 0) {
     596      162110 :                         gc_scan_black(ref);
     597             :                 } else {
     598      578731 :                         GC_REF_SET_COLOR(ref, GC_WHITE);
     599      608756 :                         if (GC_TYPE(ref) == IS_OBJECT && !(GC_FLAGS(ref) & IS_OBJ_FREE_CALLED)) {
     600             :                                 zend_object_get_gc_t get_gc;
     601      223378 :                                 zend_object *obj = (zend_object*)ref;
     602             : 
     603      253403 :                                 if (EXPECTED(IS_OBJ_VALID(EG(objects_store).object_buckets[obj->handle]) &&
     604             :                                              (get_gc = obj->handlers->get_gc) != NULL)) {
     605             :                                         int n;
     606             :                                         zval *zv, *end;
     607             :                                         zval tmp;
     608             : 
     609      223378 :                                         ZVAL_OBJ(&tmp, obj);
     610      223378 :                                         ht = get_gc(&tmp, &zv, &n);
     611      223378 :                                         end = zv + n;
     612      223378 :                                         if (EXPECTED(!ht)) {
     613      193353 :                                                 if (!n) return;
     614      386699 :                                                 while (!Z_REFCOUNTED_P(--end)) {
     615           1 :                                                         if (zv == end) return;
     616             :                                                 }
     617             :                                         }
     618      540123 :                                         while (zv != end) {
     619       93375 :                                                 if (Z_REFCOUNTED_P(zv)) {
     620          11 :                                                         ref = Z_COUNTED_P(zv);
     621          11 :                                                         gc_scan(ref);
     622             :                                                 }
     623       93375 :                                                 zv++;
     624             :                                         }
     625      223374 :                                         if (EXPECTED(!ht)) {
     626      193349 :                                                 ref = Z_COUNTED_P(zv);
     627      193349 :                                                 goto tail_call;
     628             :                                         }
     629             :                                 } else {
     630           0 :                                         return;
     631             :                                 }
     632      355353 :                         } else if (GC_TYPE(ref) == IS_ARRAY) {
     633      274343 :                                 if ((zend_array*)ref == &EG(symbol_table)) {
     634           0 :                                         GC_REF_SET_BLACK(ref);
     635           0 :                                         return;
     636             :                                 } else {
     637      274343 :                                         ht = (zend_array*)ref;
     638             :                                 }
     639       81010 :                         } else if (GC_TYPE(ref) == IS_REFERENCE) {
     640       81006 :                                 if (Z_REFCOUNTED(((zend_reference*)ref)->val)) {
     641       81006 :                                         ref = Z_COUNTED(((zend_reference*)ref)->val);
     642       81006 :                                         goto tail_call;
     643             :                                 }
     644           0 :                                 return;
     645             :                         } else {
     646           4 :                                 return;
     647             :                         }
     648             : 
     649      304368 :                         if (!ht->nNumUsed) return;
     650      294365 :                         p = ht->arData;
     651      294365 :                         end = p + ht->nNumUsed;
     652             :                         while (1) {
     653      324363 :                                 end--;
     654      324363 :                                 zv = &end->val;
     655      324363 :                                 if (Z_TYPE_P(zv) == IS_INDIRECT) {
     656       20001 :                                         zv = Z_INDIRECT_P(zv);
     657             :                                 }
     658      324363 :                                 if (Z_REFCOUNTED_P(zv)) {
     659      274368 :                                         break;
     660             :                                 }
     661       49995 :                                 if (p == end) return;
     662       29998 :                         }
     663      661905 :                         while (p != end) {
     664      113169 :                                 zv = &p->val;
     665      113169 :                                 if (Z_TYPE_P(zv) == IS_INDIRECT) {
     666           0 :                                         zv = Z_INDIRECT_P(zv);
     667             :                                 }
     668      113169 :                                 if (Z_REFCOUNTED_P(zv)) {
     669      113169 :                                         ref = Z_COUNTED_P(zv);
     670      113169 :                                         gc_scan(ref);
     671             :                                 }
     672      113169 :                                 p++;
     673             :                         }
     674      274368 :                         zv = &p->val;
     675      274368 :                         if (Z_TYPE_P(zv) == IS_INDIRECT) {
     676           5 :                                 zv = Z_INDIRECT_P(zv);
     677             :                         }
     678      274368 :                         ref = Z_COUNTED_P(zv);
     679      274368 :                         goto tail_call;
     680             :                 }
     681             :         }
     682             : }
     683             : 
     684         105 : static void gc_scan_roots(void)
     685             : {
     686         105 :         gc_root_buffer *current = GC_G(roots).next;
     687             : 
     688      453339 :         while (current != &GC_G(roots)) {
     689      453129 :                 gc_scan(current->ref);
     690      453129 :                 current = current->next;
     691             :         }
     692         105 : }
     693             : 
     694      192025 : static void gc_add_garbage(zend_refcounted *ref, gc_additional_buffer **additional_buffer)
     695             : {
     696      192025 :         gc_root_buffer *buf = GC_G(unused);
     697             : 
     698      192025 :         if (buf) {
     699       14755 :                 GC_G(unused) = buf->prev;
     700             : #if 1
     701             :                 /* optimization: color is already GC_BLACK (0) */
     702       14755 :                 GC_INFO(ref) = buf - GC_G(buf);
     703             : #else
     704             :                 GC_REF_SET_ADDRESS(ref, buf - GC_G(buf));
     705             : #endif
     706      177270 :         } else if (GC_G(first_unused) != GC_G(last_unused)) {
     707           6 :                 buf = GC_G(first_unused);
     708           6 :                 GC_G(first_unused)++;
     709             : #if 1
     710             :                 /* optimization: color is already GC_BLACK (0) */
     711           6 :                 GC_INFO(ref) = buf - GC_G(buf);
     712             : #else
     713             :                 GC_REF_SET_ADDRESS(ref, buf - GC_G(buf));
     714             : #endif
     715             :         } else {
     716             :                 /* If we don't have free slots in the buffer, allocate a new one and
     717             :                  * set it's address to GC_ROOT_BUFFER_MAX_ENTRIES that have special
     718             :                  * meaning.
     719             :                  */
     720      177264 :                 if (!*additional_buffer || (*additional_buffer)->used == GC_NUM_ADDITIONAL_ENTRIES) {
     721        1401 :                         gc_additional_buffer *new_buffer = emalloc(sizeof(gc_additional_buffer));
     722        1401 :                         new_buffer->used = 0;
     723        1401 :                         new_buffer->next = *additional_buffer;
     724        1401 :                         *additional_buffer = new_buffer;
     725             :                 }
     726      177264 :                 buf = (*additional_buffer)->buf + (*additional_buffer)->used;
     727      177264 :                 (*additional_buffer)->used++;
     728             : #if 1
     729             :                 /* optimization: color is already GC_BLACK (0) */
     730      177264 :                 GC_INFO(ref) = GC_ROOT_BUFFER_MAX_ENTRIES;
     731             : #else
     732             :                 GC_REF_SET_ADDRESS(ref, GC_ROOT_BUFFER_MAX_ENTRIES);
     733             : #endif
     734             :                 /* modify type to prevent indirect destruction */
     735      177264 :                 GC_TYPE(ref) |= GC_FAKE_BUFFER_FLAG;
     736             :         }
     737      192025 :         if (buf) {
     738      192025 :                 buf->ref = ref;
     739      192025 :                 buf->next = GC_G(roots).next;
     740      192025 :                 buf->prev = &GC_G(roots);
     741      192025 :                 GC_G(roots).next->prev = buf;
     742      192025 :                 GC_G(roots).next = buf;
     743             :         }
     744      192025 : }
     745             : 
     746      353839 : static int gc_collect_white(zend_refcounted *ref, uint32_t *flags, gc_additional_buffer **additional_buffer)
     747             : {
     748      353839 :         int count = 0;
     749             :         HashTable *ht;
     750             :         Bucket *p, *end;
     751             :         zval *zv;
     752             : 
     753             : tail_call:
     754      873164 :         if (GC_REF_GET_COLOR(ref) == GC_WHITE) {
     755      529333 :                 ht = NULL;
     756      529333 :                 GC_REF_SET_BLACK(ref);
     757             : 
     758             :                 /* don't count references for compatibility ??? */
     759      529333 :                 if (GC_TYPE(ref) != IS_REFERENCE) {
     760      448327 :                         count++;
     761             :                 }
     762             : 
     763      539359 :                 if (GC_TYPE(ref) == IS_OBJECT && !(GC_FLAGS(ref) & IS_OBJ_FREE_CALLED)) {
     764             :                         zend_object_get_gc_t get_gc;
     765      188682 :                         zend_object *obj = (zend_object*)ref;
     766             : 
     767      198708 :                         if (EXPECTED(IS_OBJ_VALID(EG(objects_store).object_buckets[obj->handle]) &&
     768             :                                      (get_gc = obj->handlers->get_gc) != NULL)) {
     769             :                                 int n;
     770             :                                 zval *zv, *end;
     771             :                                 zval tmp;
     772             : 
     773             : #if 1
     774             :                                 /* optimization: color is GC_BLACK (0) */
     775      188682 :                                 if (!GC_INFO(ref)) {
     776             : #else
     777             :                                 if (!GC_ADDRESS(GC_INFO(ref))) {
     778             : #endif
     779       13385 :                                         gc_add_garbage(ref, additional_buffer);
     780             :                                 }
     781      566042 :                                 if (obj->handlers->dtor_obj &&
     782      188682 :                                     ((obj->handlers->dtor_obj != zend_objects_destroy_object) ||
     783      188678 :                                      (obj->ce->destructor != NULL))) {
     784      188654 :                                         *flags |= GC_HAS_DESTRUCTORS;
     785             :                                 }
     786      188682 :                                 ZVAL_OBJ(&tmp, obj);
     787      188682 :                                 ht = get_gc(&tmp, &zv, &n);
     788      188682 :                                 end = zv + n;
     789      188682 :                                 if (EXPECTED(!ht)) {
     790      178656 :                                         if (!n) return count;
     791      357305 :                                         while (!Z_REFCOUNTED_P(--end)) {
     792             :                                                 /* count non-refcounted for compatibility ??? */
     793           2 :                                                 if (Z_TYPE_P(zv) != IS_UNDEF) {
     794           1 :                                                         count++;
     795             :                                                 }
     796           1 :                                                 if (zv == end) return count;
     797             :                                         }
     798             :                                 }
     799      456034 :                                 while (zv != end) {
     800       78678 :                                         if (Z_REFCOUNTED_P(zv)) {
     801          11 :                                                 ref = Z_COUNTED_P(zv);
     802          11 :                                                 GC_REFCOUNT(ref)++;
     803          11 :                                                 count += gc_collect_white(ref, flags, additional_buffer);
     804             :                                         /* count non-refcounted for compatibility ??? */
     805      157334 :                                         } else if (Z_TYPE_P(zv) != IS_UNDEF) {
     806       78653 :                                                 count++;
     807             :                                         }
     808       78678 :                                         zv++;
     809             :                                 }
     810      188678 :                                 if (EXPECTED(!ht)) {
     811      178652 :                                         ref = Z_COUNTED_P(zv);
     812      178652 :                                         GC_REFCOUNT(ref)++;
     813      178652 :                                         goto tail_call;
     814             :                                 }
     815             :                         } else {
     816           0 :                                 return count;
     817             :                         }
     818      340651 :                 } else if (GC_TYPE(ref) == IS_ARRAY) {
     819             : #if 1
     820             :                                 /* optimization: color is GC_BLACK (0) */
     821      259643 :                                 if (!GC_INFO(ref)) {
     822             : #else
     823             :                                 if (!GC_ADDRESS(GC_INFO(ref))) {
     824             : #endif
     825      178640 :                                 gc_add_garbage(ref, additional_buffer);
     826             :                         }
     827      259643 :                         ht = (zend_array*)ref;
     828       81008 :                 } else if (GC_TYPE(ref) == IS_REFERENCE) {
     829       81006 :                         if (Z_REFCOUNTED(((zend_reference*)ref)->val)) {
     830       81006 :                                 ref = Z_COUNTED(((zend_reference*)ref)->val);
     831       81006 :                                 GC_REFCOUNT(ref)++;
     832       81006 :                                 goto tail_call;
     833             :                         }
     834           0 :                         return count;
     835             :                 } else {
     836           2 :                         return count;
     837             :                 }
     838             : 
     839      269669 :                 if (!ht->nNumUsed) return count;
     840      269665 :                 p = ht->arData;
     841      269665 :                 end = p + ht->nNumUsed;
     842             :                 while (1) {
     843      289664 :                         end--;
     844      289664 :                         zv = &end->val;
     845      289664 :                         if (Z_TYPE_P(zv) == IS_INDIRECT) {
     846       10002 :                                 zv = Z_INDIRECT_P(zv);
     847             :                         }
     848      289664 :                         if (Z_REFCOUNTED_P(zv)) {
     849      259667 :                                 break;
     850             :                         }
     851             :                         /* count non-refcounted for compatibility ??? */
     852       29997 :                         if (Z_TYPE_P(zv) != IS_UNDEF) {
     853       29997 :                                 count++;
     854             :                         }
     855       29997 :                         if (p == end) return count;
     856       19999 :                 }
     857      617973 :                 while (p != end) {
     858       98639 :                         zv = &p->val;
     859       98639 :                         if (Z_TYPE_P(zv) == IS_INDIRECT) {
     860           0 :                                 zv = Z_INDIRECT_P(zv);
     861             :                         }
     862       98639 :                         if (Z_REFCOUNTED_P(zv)) {
     863       98639 :                                 ref = Z_COUNTED_P(zv);
     864       98639 :                                 GC_REFCOUNT(ref)++;
     865       98639 :                                 count += gc_collect_white(ref, flags, additional_buffer);
     866             :                                 /* count non-refcounted for compatibility ??? */
     867           0 :                         } else if (Z_TYPE_P(zv) != IS_UNDEF) {
     868           0 :                                 count++;
     869             :                         }
     870       98639 :                         p++;
     871             :                 }
     872      259667 :                 zv = &p->val;
     873      259667 :                 if (Z_TYPE_P(zv) == IS_INDIRECT) {
     874           5 :                         zv = Z_INDIRECT_P(zv);
     875             :                 }
     876      259667 :                 ref = Z_COUNTED_P(zv);
     877      259667 :                 GC_REFCOUNT(ref)++;
     878      259667 :                 goto tail_call;
     879             :         }
     880      343831 :         return count;
     881             : }
     882             : 
     883         105 : static int gc_collect_roots(uint32_t *flags, gc_additional_buffer **additional_buffer)
     884             : {
     885         105 :         int count = 0;
     886         105 :         gc_root_buffer *current = GC_G(roots).next;
     887             : 
     888             :         /* remove non-garbage from the list */
     889      453339 :         while (current != &GC_G(roots)) {
     890      453129 :                 gc_root_buffer *next = current->next;
     891      453129 :                 if (GC_REF_GET_COLOR(current->ref) == GC_BLACK) {
     892      196829 :                         GC_INFO(current->ref) = 0; /* reset GC_ADDRESS() and keep GC_BLACK */
     893             :                         GC_REMOVE_FROM_ROOTS(current);
     894             :                 }
     895      453129 :                 current = next;
     896             :         }
     897             : 
     898         105 :         current = GC_G(roots).next;
     899      256510 :         while (current != &GC_G(roots)) {
     900      256300 :                 if (GC_REF_GET_COLOR(current->ref) == GC_WHITE) {
     901      255189 :                         count += gc_collect_white(current->ref, flags, additional_buffer);
     902             :                 }
     903      256300 :                 current = current->next;
     904             :         }
     905             : 
     906             :         /* relink remaining roots into list to free */
     907         105 :         if (GC_G(roots).next != &GC_G(roots)) {
     908          66 :                 if (GC_G(to_free).next == &GC_G(to_free)) {
     909             :                         /* move roots into list to free */
     910          66 :                         GC_G(to_free).next = GC_G(roots).next;
     911          66 :                         GC_G(to_free).prev = GC_G(roots).prev;
     912          66 :                         GC_G(to_free).next->prev = &GC_G(to_free);
     913          66 :                         GC_G(to_free).prev->next = &GC_G(to_free);
     914             :                 } else {
     915             :                         /* add roots into list to free */
     916           0 :                         GC_G(to_free).prev->next = GC_G(roots).next;
     917           0 :                         GC_G(roots).next->prev = GC_G(to_free).prev;
     918           0 :                         GC_G(roots).prev->next = &GC_G(to_free);
     919           0 :                         GC_G(to_free).prev = GC_G(roots).prev;
     920             :                 }
     921             : 
     922          66 :                 GC_G(roots).next = &GC_G(roots);
     923          66 :                 GC_G(roots).prev = &GC_G(roots);
     924             :         }
     925         105 :         return count;
     926             : }
     927             : 
     928           4 : static void gc_remove_nested_data_from_buffer(zend_refcounted *ref, gc_root_buffer *root)
     929             : {
     930           4 :         HashTable *ht = NULL;
     931             :         Bucket *p, *end;
     932             :         zval *zv;
     933             : 
     934             : tail_call:
     935          21 :         if (root ||
     936           6 :             (GC_ADDRESS(GC_INFO(ref)) != 0 &&
     937           3 :              GC_REF_GET_COLOR(ref) == GC_BLACK &&
     938           3 :              GC_ADDRESS(GC_INFO(ref)) != GC_ROOT_BUFFER_MAX_ENTRIES)) {
     939             :                 GC_TRACE_REF(ref, "removing from buffer");
     940           6 :                 if (root) {
     941           3 :                         GC_INFO(ref) = 0;
     942             :                         GC_REMOVE_FROM_ROOTS(root);
     943           3 :                         root = NULL;
     944             :                 } else {
     945           3 :                         GC_REMOVE_FROM_BUFFER(ref);
     946             :                 }
     947             : 
     948           9 :                 if (GC_TYPE(ref) == IS_OBJECT && !(GC_FLAGS(ref) & IS_OBJ_FREE_CALLED)) {
     949             :                         zend_object_get_gc_t get_gc;
     950           4 :                         zend_object *obj = (zend_object*)ref;
     951             : 
     952           7 :                         if (EXPECTED(IS_OBJ_VALID(EG(objects_store).object_buckets[obj->handle]) &&
     953             :                              (get_gc = obj->handlers->get_gc) != NULL)) {
     954             :                                 int n;
     955             :                                 zval *zv, *end;
     956             :                                 zval tmp;
     957             : 
     958           4 :                                 ZVAL_OBJ(&tmp, obj);
     959           4 :                                 ht = get_gc(&tmp, &zv, &n);
     960           4 :                                 end = zv + n;
     961           4 :                                 if (EXPECTED(!ht)) {
     962           1 :                                         if (!n) return;
     963           0 :                                         while (!Z_REFCOUNTED_P(--end)) {
     964           0 :                                                 if (zv == end) return;
     965             :                                         }
     966             :                                 }
     967           6 :                                 while (zv != end) {
     968           0 :                                         if (Z_REFCOUNTED_P(zv)) {
     969           0 :                                                 ref = Z_COUNTED_P(zv);
     970           0 :                                                 gc_remove_nested_data_from_buffer(ref, NULL);
     971             :                                         }
     972           0 :                                         zv++;
     973             :                                 }
     974           3 :                                 if (EXPECTED(!ht)) {
     975           0 :                                         ref = Z_COUNTED_P(zv);
     976           0 :                                         goto tail_call;
     977             :                                 }
     978             :                         } else {
     979           0 :                                 return;
     980             :                         }
     981           2 :                 } else if (GC_TYPE(ref) == IS_ARRAY) {
     982           2 :                         ht = (zend_array*)ref;
     983           0 :                 } else if (GC_TYPE(ref) == IS_REFERENCE) {
     984           0 :                         if (Z_REFCOUNTED(((zend_reference*)ref)->val)) {
     985           0 :                                 ref = Z_COUNTED(((zend_reference*)ref)->val);
     986           0 :                                 goto tail_call;
     987             :                         }
     988           0 :                         return;
     989             :                 } else {
     990           0 :                         return;
     991             :                 }
     992             : 
     993           5 :                 if (!ht->nNumUsed) return;
     994           5 :                 p = ht->arData;
     995           5 :                 end = p + ht->nNumUsed;
     996             :                 while (1) {
     997           5 :                         end--;
     998           5 :                         zv = &end->val;
     999           5 :                         if (Z_TYPE_P(zv) == IS_INDIRECT) {
    1000           2 :                                 zv = Z_INDIRECT_P(zv);
    1001             :                         }
    1002           5 :                         if (Z_REFCOUNTED_P(zv)) {
    1003           5 :                                 break;
    1004             :                         }
    1005           0 :                         if (p == end) return;
    1006           0 :                 }
    1007          11 :                 while (p != end) {
    1008           1 :                         zv = &p->val;
    1009           1 :                         if (Z_TYPE_P(zv) == IS_INDIRECT) {
    1010           0 :                                 zv = Z_INDIRECT_P(zv);
    1011             :                         }
    1012           1 :                         if (Z_REFCOUNTED_P(zv)) {
    1013           1 :                                 ref = Z_COUNTED_P(zv);
    1014           1 :                                 gc_remove_nested_data_from_buffer(ref, NULL);
    1015             :                         }
    1016           1 :                         p++;
    1017             :                 }
    1018           5 :                 zv = &p->val;
    1019           5 :                 if (Z_TYPE_P(zv) == IS_INDIRECT) {
    1020           2 :                         zv = Z_INDIRECT_P(zv);
    1021             :                 }
    1022           5 :                 ref = Z_COUNTED_P(zv);
    1023           5 :                 goto tail_call;
    1024             :         }
    1025             : }
    1026             : 
    1027         110 : ZEND_API int zend_gc_collect_cycles(void)
    1028             : {
    1029         110 :         int count = 0;
    1030             : 
    1031         110 :         if (GC_G(roots).next != &GC_G(roots)) {
    1032             :                 gc_root_buffer *current, *next, *orig_next_to_free;
    1033             :                 zend_refcounted *p;
    1034             :                 gc_root_buffer to_free;
    1035         105 :                 uint32_t gc_flags = 0;
    1036             :                 gc_additional_buffer *additional_buffer;
    1037             : #if ZEND_GC_DEBUG
    1038             :                 zend_bool orig_gc_full;
    1039             : #endif
    1040             : 
    1041         105 :                 if (GC_G(gc_active)) {
    1042           0 :                         return 0;
    1043             :                 }
    1044             : 
    1045             :                 GC_TRACE("Collecting cycles");
    1046         105 :                 GC_G(gc_runs)++;
    1047         105 :                 GC_G(gc_active) = 1;
    1048             : 
    1049             :                 GC_TRACE("Marking roots");
    1050         105 :                 gc_mark_roots();
    1051             :                 GC_TRACE("Scanning roots");
    1052         105 :                 gc_scan_roots();
    1053             : 
    1054             : #if ZEND_GC_DEBUG
    1055             :                 orig_gc_full = GC_G(gc_full);
    1056             :                 GC_G(gc_full) = 0;
    1057             : #endif
    1058             : 
    1059             :                 GC_TRACE("Collecting roots");
    1060         105 :                 additional_buffer = NULL;
    1061         105 :                 count = gc_collect_roots(&gc_flags, &additional_buffer);
    1062             : #if ZEND_GC_DEBUG
    1063             :                 GC_G(gc_full) = orig_gc_full;
    1064             : #endif
    1065         105 :                 GC_G(gc_active) = 0;
    1066             : 
    1067         105 :                 if (GC_G(to_free).next == &GC_G(to_free)) {
    1068             :                         /* nothing to free */
    1069             :                         GC_TRACE("Nothing to free");
    1070          39 :                         return 0;
    1071             :                 }
    1072             : 
    1073             :                 /* Copy global to_free list into local list */
    1074          66 :                 to_free.next = GC_G(to_free).next;
    1075          66 :                 to_free.prev = GC_G(to_free).prev;
    1076          66 :                 to_free.next->prev = &to_free;
    1077          66 :                 to_free.prev->next = &to_free;
    1078             : 
    1079             :                 /* Free global list */
    1080          66 :                 GC_G(to_free).next = &GC_G(to_free);
    1081          66 :                 GC_G(to_free).prev = &GC_G(to_free);
    1082             : 
    1083          66 :                 orig_next_to_free = GC_G(next_to_free);
    1084             : 
    1085             : #if ZEND_GC_DEBUG
    1086             :                 orig_gc_full = GC_G(gc_full);
    1087             :                 GC_G(gc_full) = 0;
    1088             : #endif
    1089             : 
    1090          66 :                 if (gc_flags & GC_HAS_DESTRUCTORS) {
    1091             :                         GC_TRACE("Calling destructors");
    1092          35 :                         if (EG(objects_store).object_buckets) {
    1093             :                                 /* Remember reference counters before calling destructors */
    1094          35 :                                 current = to_free.next;
    1095      397361 :                                 while (current != &to_free) {
    1096      397291 :                                         current->refcount = GC_REFCOUNT(current->ref);
    1097      397291 :                                         current = current->next;
    1098             :                                 }
    1099             : 
    1100             :                                 /* Call destructors */
    1101          35 :                                 current = to_free.next;
    1102      397358 :                                 while (current != &to_free) {
    1103      397288 :                                         p = current->ref;
    1104      397288 :                                         GC_G(next_to_free) = current->next;
    1105      397288 :                                         if ((GC_TYPE(p) & GC_TYPE_MASK) == IS_OBJECT) {
    1106      188655 :                                                 zend_object *obj = (zend_object*)p;
    1107             : 
    1108      377310 :                                                 if (IS_OBJ_VALID(EG(objects_store).object_buckets[obj->handle]) &&
    1109      188655 :                                                         !(GC_FLAGS(obj) & IS_OBJ_DESTRUCTOR_CALLED)) {
    1110             :                                                         GC_TRACE_REF(obj, "calling destructor");
    1111      178658 :                                                         GC_FLAGS(obj) |= IS_OBJ_DESTRUCTOR_CALLED;
    1112      178658 :                                                         if (obj->handlers->dtor_obj) {
    1113      178658 :                                                                 GC_REFCOUNT(obj)++;
    1114      178658 :                                                                 obj->handlers->dtor_obj(obj);
    1115      178658 :                                                                 GC_REFCOUNT(obj)--;
    1116             :                                                         }
    1117             :                                                 }
    1118             :                                         }
    1119      397288 :                                         current = GC_G(next_to_free);
    1120             :                                 }
    1121             : 
    1122             :                                 /* Remove values captured in destructors */
    1123          35 :                                 current = to_free.next;
    1124      368064 :                                 while (current != &to_free) {
    1125      367994 :                                         GC_G(next_to_free) = current->next;
    1126      367994 :                                         if (GC_REFCOUNT(current->ref) > current->refcount) {
    1127           3 :                                                 gc_remove_nested_data_from_buffer(current->ref, current);
    1128             :                                         }
    1129      367994 :                                         current = GC_G(next_to_free);
    1130             :                                 }
    1131             :                         }
    1132             :                 }
    1133             : 
    1134             :                 /* Destroy zvals */
    1135             :                 GC_TRACE("Destroying zvals");
    1136          66 :                 GC_G(gc_active) = 1;
    1137          66 :                 current = to_free.next;
    1138      268440 :                 while (current != &to_free) {
    1139      268308 :                         p = current->ref;
    1140      268308 :                         GC_G(next_to_free) = current->next;
    1141             :                         GC_TRACE_REF(p, "destroying");
    1142      268308 :                         if ((GC_TYPE(p) & GC_TYPE_MASK) == IS_OBJECT) {
    1143       23398 :                                 zend_object *obj = (zend_object*)p;
    1144             : 
    1145       46796 :                                 if (EG(objects_store).object_buckets &&
    1146       23398 :                                     IS_OBJ_VALID(EG(objects_store).object_buckets[obj->handle])) {
    1147       23398 :                                         EG(objects_store).object_buckets[obj->handle] = SET_OBJ_INVALID(obj);
    1148       23398 :                                         GC_TYPE(obj) = IS_NULL;
    1149       23398 :                                         if (!(GC_FLAGS(obj) & IS_OBJ_FREE_CALLED)) {
    1150       23398 :                                                 GC_FLAGS(obj) |= IS_OBJ_FREE_CALLED;
    1151       23398 :                                                 if (obj->handlers->free_obj) {
    1152       23398 :                                                         GC_REFCOUNT(obj)++;
    1153       23398 :                                                         obj->handlers->free_obj(obj);
    1154       23398 :                                                         GC_REFCOUNT(obj)--;
    1155             :                                                 }
    1156             :                                         }
    1157       23398 :                                         SET_OBJ_BUCKET_NUMBER(EG(objects_store).object_buckets[obj->handle], EG(objects_store).free_list_head);
    1158       23398 :                                         EG(objects_store).free_list_head = obj->handle;
    1159       23398 :                                         p = current->ref = (zend_refcounted*)(((char*)obj) - obj->handlers->offset);
    1160             :                                 }
    1161      244910 :                         } else if ((GC_TYPE(p) & GC_TYPE_MASK) == IS_ARRAY) {
    1162      244910 :                                 zend_array *arr = (zend_array*)p;
    1163             : 
    1164      244910 :                                 GC_TYPE(arr) = IS_NULL;
    1165      244910 :                                 zend_hash_destroy(arr);
    1166             :                         }
    1167      268308 :                         current = GC_G(next_to_free);
    1168             :                 }
    1169             : 
    1170             :                 /* Free objects */
    1171          66 :                 current = to_free.next;
    1172      268440 :                 while (current != &to_free) {
    1173      268308 :                         next = current->next;
    1174      268308 :                         p = current->ref;
    1175      268308 :                         if (EXPECTED(current >= GC_G(buf) && current < GC_G(buf) + GC_ROOT_BUFFER_MAX_ENTRIES)) {
    1176       91044 :                                 current->prev = GC_G(unused);
    1177       91044 :                                 GC_G(unused) = current;
    1178             :                         }
    1179      268308 :                         efree(p);
    1180      268308 :                         current = next;
    1181             :                 }
    1182             : 
    1183        1533 :                 while (additional_buffer != NULL) {
    1184        1401 :                         gc_additional_buffer *next = additional_buffer->next;
    1185        1401 :                         efree(additional_buffer);
    1186        1401 :                         additional_buffer = next;
    1187             :                 }
    1188             : 
    1189             :                 GC_TRACE("Collection finished");
    1190          66 :                 GC_G(collected) += count;
    1191          66 :                 GC_G(next_to_free) = orig_next_to_free;
    1192             : #if ZEND_GC_DEBUG
    1193             :                 GC_G(gc_full) = orig_gc_full;
    1194             : #endif
    1195          66 :                 GC_G(gc_active) = 0;
    1196             :         }
    1197             : 
    1198          71 :         return count;
    1199             : }
    1200             : 
    1201             : /*
    1202             :  * Local variables:
    1203             :  * tab-width: 4
    1204             :  * c-basic-offset: 4
    1205             :  * indent-tabs-mode: t
    1206             :  * End:
    1207             :  *
    1208             :  * vim:noexpandtab:
    1209             :  */

Generated by: LCOV version 1.10

Generated at Sun, 28 Aug 2016 17:09:58 +0000 (41 hours ago)

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