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: 408 430 94.9 %
Date: 2015-04-14 Functions: 17 17 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             : #ifdef ZTS
      29             : ZEND_API int gc_globals_id;
      30             : #else
      31             : ZEND_API zend_gc_globals gc_globals;
      32             : #endif
      33             : 
      34             : ZEND_API int (*gc_collect_cycles)(void);
      35             : 
      36             : #define GC_REMOVE_FROM_ROOTS(current) \
      37             :         gc_remove_from_roots((current))
      38             : 
      39             : static zend_always_inline void gc_remove_from_roots(gc_root_buffer *root)
      40             : {
      41     1276076 :         root->next->prev = root->prev;
      42     1276076 :         root->prev->next = root->next;
      43     1276076 :         root->prev = GC_G(unused);
      44     1276076 :         GC_G(unused) = root;
      45             :         GC_BENCH_DEC(root_buf_length);
      46             : }
      47             : 
      48       20905 : static void root_buffer_dtor(zend_gc_globals *gc_globals)
      49             : {
      50       20905 :         if (gc_globals->buf) {
      51       20904 :                 free(gc_globals->buf);
      52       20904 :                 gc_globals->buf = NULL;
      53             :         }
      54       20905 : }
      55             : 
      56       20871 : static void gc_globals_ctor_ex(zend_gc_globals *gc_globals)
      57             : {
      58       20871 :         gc_globals->gc_enabled = 0;
      59       20871 :         gc_globals->gc_active = 0;
      60             : 
      61       20871 :         gc_globals->buf = NULL;
      62             : 
      63       20871 :         gc_globals->roots.next = &gc_globals->roots;
      64       20871 :         gc_globals->roots.prev = &gc_globals->roots;
      65       20871 :         gc_globals->unused = NULL;
      66       20871 :         gc_globals->next_to_free = NULL;
      67             : 
      68       20871 :         gc_globals->to_free.next = &gc_globals->to_free;
      69       20871 :         gc_globals->to_free.prev = &gc_globals->to_free;
      70             : 
      71       20871 :         gc_globals->gc_runs = 0;
      72       20871 :         gc_globals->collected = 0;
      73             : 
      74             : #if GC_BENCH
      75             :         gc_globals->root_buf_length = 0;
      76             :         gc_globals->root_buf_peak = 0;
      77             :         gc_globals->zval_possible_root = 0;
      78             :         gc_globals->zval_buffered = 0;
      79             :         gc_globals->zval_remove_from_buffer = 0;
      80             :         gc_globals->zval_marked_grey = 0;
      81             : #endif
      82       20871 : }
      83             : 
      84       20871 : ZEND_API void gc_globals_ctor(void)
      85             : {
      86             : #ifdef ZTS
      87             :         ts_allocate_id(&gc_globals_id, sizeof(zend_gc_globals), (ts_allocate_ctor) gc_globals_ctor_ex, (ts_allocate_dtor) root_buffer_dtor);
      88             : #else
      89       20871 :         gc_globals_ctor_ex(&gc_globals);
      90             : #endif
      91       20871 : }
      92             : 
      93       20905 : ZEND_API void gc_globals_dtor(void)
      94             : {
      95             : #ifndef ZTS
      96       20905 :         root_buffer_dtor(&gc_globals);
      97             : #endif
      98       20905 : }
      99             : 
     100       41707 : ZEND_API void gc_reset(void)
     101             : {
     102       41707 :         GC_G(gc_runs) = 0;
     103       41707 :         GC_G(collected) = 0;
     104       41707 :         GC_G(gc_full) = 0;
     105             : 
     106             : #if GC_BENCH
     107             :         GC_G(root_buf_length) = 0;
     108             :         GC_G(root_buf_peak) = 0;
     109             :         GC_G(zval_possible_root) = 0;
     110             :         GC_G(zval_buffered) = 0;
     111             :         GC_G(zval_remove_from_buffer) = 0;
     112             :         GC_G(zval_marked_grey) = 0;
     113             : #endif
     114             : 
     115       41707 :         GC_G(roots).next = &GC_G(roots);
     116       41707 :         GC_G(roots).prev = &GC_G(roots);
     117             : 
     118       41707 :         GC_G(to_free).next = &GC_G(to_free);
     119       41707 :         GC_G(to_free).prev = &GC_G(to_free);
     120             : 
     121       41707 :         if (GC_G(buf)) {
     122       41705 :                 GC_G(unused) = NULL;
     123       41705 :                 GC_G(first_unused) = GC_G(buf) + 1;
     124             :         } else {
     125           2 :                 GC_G(unused) = NULL;
     126           2 :                 GC_G(first_unused) = NULL;
     127           2 :                 GC_G(last_unused) = NULL;
     128             :         }
     129       41707 : }
     130             : 
     131       20883 : ZEND_API void gc_init(void)
     132             : {
     133       20883 :         if (GC_G(buf) == NULL && GC_G(gc_enabled)) {
     134       20870 :                 GC_G(buf) = (gc_root_buffer*) malloc(sizeof(gc_root_buffer) * GC_ROOT_BUFFER_MAX_ENTRIES);
     135       20870 :                 GC_G(last_unused) = &GC_G(buf)[GC_ROOT_BUFFER_MAX_ENTRIES];
     136       20870 :                 gc_reset();
     137             :         }
     138       20883 : }
     139             : 
     140     1354894 : ZEND_API void gc_possible_root(zend_refcounted *ref)
     141             : {
     142     1354894 :         if (UNEXPECTED(GC_TYPE(ref) == IS_NULL) || UNEXPECTED(CG(unclean_shutdown))) {
     143        9859 :                 return;
     144             :         }
     145             : 
     146             :         ZEND_ASSERT(GC_TYPE(ref) == IS_ARRAY || GC_TYPE(ref) == IS_OBJECT);
     147             :         GC_BENCH_INC(zval_possible_root);
     148             : 
     149     1345035 :         if (EXPECTED(GC_GET_COLOR(GC_INFO(ref)) == GC_BLACK)) {
     150     1345035 :                 GC_SET_PURPLE(GC_INFO(ref));
     151             : 
     152     1345035 :                 if (!GC_ADDRESS(GC_INFO(ref))) {
     153     1345035 :                         gc_root_buffer *newRoot = GC_G(unused);
     154             : 
     155     1345035 :                         if (newRoot) {
     156     1054649 :                                 GC_G(unused) = newRoot->prev;
     157      290386 :                         } else if (GC_G(first_unused) != GC_G(last_unused)) {
     158      290350 :                                 newRoot = GC_G(first_unused);
     159      290350 :                                 GC_G(first_unused)++;
     160             :                         } else {
     161          36 :                                 if (!GC_G(gc_enabled)) {
     162           8 :                                         GC_SET_BLACK(GC_INFO(ref));
     163           8 :                                         return;
     164             :                                 }
     165          28 :                                 GC_REFCOUNT(ref)++;
     166          28 :                                 gc_collect_cycles();
     167          28 :                                 GC_REFCOUNT(ref)--;
     168          28 :                                 newRoot = GC_G(unused);
     169          28 :                                 if (!newRoot) {
     170           8 :                                         return;
     171             :                                 }
     172          20 :                                 GC_SET_PURPLE(GC_INFO(ref));
     173          20 :                                 GC_G(unused) = newRoot->prev;
     174             :                         }
     175             : 
     176     1345019 :                         newRoot->next = GC_G(roots).next;
     177     1345019 :                         newRoot->prev = &GC_G(roots);
     178     1345019 :                         GC_G(roots).next->prev = newRoot;
     179     1345019 :                         GC_G(roots).next = newRoot;
     180             : 
     181     1345019 :                         GC_SET_ADDRESS(GC_INFO(ref), newRoot - GC_G(buf));
     182             : 
     183     1345019 :                         newRoot->ref = ref;
     184             : 
     185             :                         GC_BENCH_INC(zval_buffered);
     186             :                         GC_BENCH_INC(root_buf_length);
     187             :                         GC_BENCH_PEAK(root_buf_peak, root_buf_length);
     188             :                 }
     189             :         }
     190             : }
     191             : 
     192     1120553 : ZEND_API void gc_remove_from_buffer(zend_refcounted *ref)
     193             : {
     194             :         gc_root_buffer *root;
     195             : 
     196     1120553 :         root = GC_G(buf) + GC_ADDRESS(GC_INFO(ref));
     197             :         GC_BENCH_INC(zval_remove_from_buffer);
     198             :         GC_REMOVE_FROM_ROOTS(root);
     199     1120553 :         GC_INFO(ref) = 0;
     200             : 
     201             :         /* updete next root that is going to be freed */
     202     1120553 :         if (GC_G(next_to_free) == root) {
     203          11 :                 GC_G(next_to_free) = root->next;
     204             :         }
     205     1120553 : }
     206             : 
     207      212799 : static void gc_scan_black(zend_refcounted *ref)
     208             : {
     209             :         HashTable *ht;
     210             :         uint idx;
     211             :         Bucket *p;
     212             : 
     213             : tail_call:
     214      212799 :         ht = NULL;
     215      212799 :         GC_SET_BLACK(GC_INFO(ref));
     216             : 
     217      212799 :         if (GC_TYPE(ref) == IS_OBJECT) {
     218             :                 zend_object_get_gc_t get_gc;
     219      174444 :                 zend_object *obj = (zend_object*)ref;
     220             : 
     221      174444 :                 if (EXPECTED(IS_OBJ_VALID(EG(objects_store).object_buckets[obj->handle]) &&
     222             :                              (get_gc = obj->handlers->get_gc) != NULL)) {
     223             :                         int i, n;
     224             :                         zval *table;
     225             :                         zval tmp;
     226             :                         HashTable *props;
     227             : 
     228      174444 :                         ZVAL_OBJ(&tmp, obj);
     229      174444 :                         props = get_gc(&tmp, &table, &n);
     230      174444 :                         while (n > 0 && !Z_REFCOUNTED(table[n-1])) n--;
     231      212699 :                         for (i = 0; i < n; i++) {
     232       76505 :                                 if (Z_REFCOUNTED(table[i])) {
     233       38254 :                                         ref = Z_COUNTED(table[i]);
     234       38254 :                                         GC_REFCOUNT(ref)++;
     235       38254 :                                         if (GC_GET_COLOR(GC_INFO(ref)) != GC_BLACK) {
     236       38254 :                                                 if (!props && i == n - 1) {
     237             :                                                         goto tail_call;
     238             :                                                 } else {
     239           4 :                                                         gc_scan_black(ref);
     240             :                                                 }
     241             :                                         }
     242             :                                 }
     243             :                         }
     244      136194 :                         if (!props) {
     245       66183 :                                 return;
     246             :                         }
     247       70011 :                         ht = props;
     248             :                 }
     249       38355 :         } else if (GC_TYPE(ref) == IS_ARRAY) {
     250       38304 :                 if ((zend_array*)ref != &EG(symbol_table)) {
     251       38304 :                         ht = (zend_array*)ref;
     252             :                 }
     253          51 :         } else if (GC_TYPE(ref) == IS_REFERENCE) {
     254           6 :                 if (Z_REFCOUNTED(((zend_reference*)ref)->val)) {
     255           5 :                         ref = Z_COUNTED(((zend_reference*)ref)->val);
     256           5 :                         GC_REFCOUNT(ref)++;
     257           5 :                         if (GC_GET_COLOR(GC_INFO(ref)) != GC_BLACK) {
     258           5 :                                 goto tail_call;
     259             :                         }
     260             :                 }
     261           1 :                 return;
     262             :         }
     263      108360 :         if (!ht) return;
     264      222271 :         for (idx = 0; idx < ht->nNumUsed; idx++) {
     265      116373 :                 p = ht->arData + idx;
     266      116373 :                 if (!Z_REFCOUNTED(p->val)) continue;
     267       86367 :                 ref = Z_COUNTED(p->val);
     268       86367 :                 GC_REFCOUNT(ref)++;
     269       86367 :                 if (GC_GET_COLOR(GC_INFO(ref)) != GC_BLACK) {
     270       48081 :                         if (idx == ht->nNumUsed-1) {
     271        2417 :                                 goto tail_call;
     272             :                         } else {
     273       45664 :                                 gc_scan_black(ref);
     274             :                         }
     275             :                 }
     276             :         }
     277             : }
     278             : 
     279      798683 : static void gc_mark_grey(zend_refcounted *ref)
     280             : {
     281             :     HashTable *ht;
     282             :     uint idx;
     283             :         Bucket *p;
     284             : 
     285             : tail_call:
     286      798683 :         if (GC_GET_COLOR(GC_INFO(ref)) != GC_GREY) {
     287      509785 :                 ht = NULL;
     288             :                 GC_BENCH_INC(zval_marked_grey);
     289      509785 :                 GC_SET_COLOR(GC_INFO(ref), GC_GREY);
     290             : 
     291      509785 :                 if (GC_TYPE(ref) == IS_OBJECT) {
     292             :                         zend_object_get_gc_t get_gc;
     293      286043 :                         zend_object *obj = (zend_object*)ref;
     294             : 
     295      286043 :                         if (EXPECTED(IS_OBJ_VALID(EG(objects_store).object_buckets[obj->handle]) &&
     296             :                              (get_gc = obj->handlers->get_gc) != NULL)) {
     297             :                                 int i, n;
     298             :                                 zval *table;
     299             :                                 zval tmp;
     300             :                                 HashTable *props;
     301             : 
     302      286043 :                                 ZVAL_OBJ(&tmp, obj);
     303      286043 :                                 props = get_gc(&tmp, &table, &n);
     304             : 
     305      286043 :                                 while (n > 0 && !Z_REFCOUNTED(table[n-1])) n--;
     306      407671 :                                 for (i = 0; i < n; i++) {
     307      243255 :                                         if (Z_REFCOUNTED(table[i])) {
     308      121639 :                                                 ref = Z_COUNTED(table[i]);
     309      121639 :                                                 GC_REFCOUNT(ref)--;
     310      121639 :                                                 if (!props && i == n - 1) {
     311             :                                                         goto tail_call;
     312             :                                                 } else {
     313          12 :                                                         gc_mark_grey(ref);
     314             :                                                 }
     315             :                                         }
     316             :                                 }
     317      164416 :                                 if (!props) {
     318       84389 :                                         return;
     319             :                                 }
     320       80027 :                                 ht = props;
     321             :                         }
     322      223742 :                 } else if (GC_TYPE(ref) == IS_ARRAY) {
     323      172681 :                         if (((zend_array*)ref) == &EG(symbol_table)) {
     324           0 :                                 GC_SET_BLACK(GC_INFO(ref));
     325             :                         } else {
     326      172681 :                                 ht = (zend_array*)ref;
     327             :                         }
     328       51061 :                 } else if (GC_TYPE(ref) == IS_REFERENCE) {
     329       51015 :                         if (Z_REFCOUNTED(((zend_reference*)ref)->val)) {
     330       51014 :                                 if (UNEXPECTED(!EG(objects_store).object_buckets) &&
     331           0 :                                         Z_TYPE(((zend_reference*)ref)->val) == IS_OBJECT) {
     332           0 :                                         Z_TYPE_INFO(((zend_reference*)ref)->val) = IS_NULL;
     333           0 :                                         return;
     334             :                                 }
     335       51014 :                                 ref = Z_COUNTED(((zend_reference*)ref)->val);
     336       51014 :                                 GC_REFCOUNT(ref)--;
     337       51014 :                                 goto tail_call;
     338             :                         }
     339           1 :                         return;
     340             :                 }
     341      252754 :                 if (!ht) return;
     342      474112 :                 for (idx = 0; idx < ht->nNumUsed; idx++) {
     343      384096 :                         p = ht->arData + idx;
     344      384096 :                         if (!Z_REFCOUNTED(p->val)) continue;
     345      324091 :                         if (UNEXPECTED(!EG(objects_store).object_buckets) &&
     346           0 :                                 Z_TYPE(p->val) == IS_OBJECT) {
     347           0 :                                 Z_TYPE_INFO(p->val) = IS_NULL;
     348           0 :                                 continue;
     349             :                         }
     350      324091 :                         ref = Z_COUNTED(p->val);
     351      324091 :                         GC_REFCOUNT(ref)--;
     352      324091 :                         if (idx == ht->nNumUsed-1) {
     353      162692 :                                 goto tail_call;
     354             :                         } else {
     355      161399 :                                 gc_mark_grey(ref);
     356             :                         }
     357             :                 }
     358             :         }
     359             : }
     360             : 
     361          74 : static void gc_mark_roots(void)
     362             : {
     363          74 :         gc_root_buffer *current = GC_G(roots).next;
     364             : 
     365      303242 :         while (current != &GC_G(roots)) {
     366      303094 :                 if (GC_GET_COLOR(GC_INFO(current->ref)) == GC_PURPLE) {
     367      301939 :                         gc_mark_grey(current->ref);
     368             :                 }
     369      303094 :                 current = current->next;
     370             :         }
     371          74 : }
     372             : 
     373      732192 : static void gc_scan(zend_refcounted *ref)
     374             : {
     375             :     HashTable *ht;
     376             :     uint idx;
     377             :         Bucket *p;
     378             : 
     379             : tail_call:
     380      732192 :         if (GC_GET_COLOR(GC_INFO(ref)) == GC_GREY) {
     381      471577 :                 ht = NULL;
     382      471577 :                 if (GC_REFCOUNT(ref) > 0) {
     383      126459 :                         gc_scan_black(ref);
     384             :                 } else {
     385      345118 :                         GC_SET_COLOR(GC_INFO(ref), GC_WHITE);
     386      345118 :                         if (GC_TYPE(ref) == IS_OBJECT) {
     387             :                                 zend_object_get_gc_t get_gc;
     388      140662 :                                 zend_object *obj = (zend_object*)ref;
     389             : 
     390      140662 :                                 if (EXPECTED(IS_OBJ_VALID(EG(objects_store).object_buckets[obj->handle]) &&
     391             :                                              (get_gc = obj->handlers->get_gc) != NULL)) {
     392             :                                         int i, n;
     393             :                                         zval *table;
     394             :                                         zval tmp;
     395             :                                         HashTable *props;
     396             : 
     397      140662 :                                         ZVAL_OBJ(&tmp, obj);
     398      140662 :                                         props = get_gc(&tmp, &table, &n);
     399      140662 :                                         while (n > 0 && !Z_REFCOUNTED(table[n-1])) n--;
     400      243098 :                                         for (i = 0; i < n; i++) {
     401      204875 :                                                 if (Z_REFCOUNTED(table[i])) {
     402      102448 :                                                         ref = Z_COUNTED(table[i]);
     403      102448 :                                                         if (!props && i == n - 1) {
     404             :                                                                 goto tail_call;
     405             :                                                         } else {
     406           9 :                                                                 gc_scan(ref);
     407             :                                                         }
     408             :                                                 }
     409             :                                         }
     410       38223 :                                         if (!props) {
     411       18206 :                                                 return;
     412             :                                         }
     413       20017 :                                         ht = props;
     414             :                                 }
     415      204456 :                         } else if (GC_TYPE(ref) == IS_ARRAY) {
     416      153444 :                                 if ((zend_array*)ref == &EG(symbol_table)) {
     417           0 :                                         GC_SET_BLACK(GC_INFO(ref));
     418             :                                 } else {
     419      153444 :                                         ht = (zend_array*)ref;
     420             :                                 }
     421       51012 :                         } else if (GC_TYPE(ref) == IS_REFERENCE) {
     422       51009 :                                 if (Z_REFCOUNTED(((zend_reference*)ref)->val)) {
     423       51009 :                                         ref = Z_COUNTED(((zend_reference*)ref)->val);
     424       51009 :                                         goto tail_call;
     425             :                                 }
     426           0 :                                 return;
     427             :                         }
     428             :                 }
     429      299923 :                 if (!ht) return;
     430      355647 :                 for (idx = 0; idx < ht->nNumUsed; idx++) {
     431      325640 :                         p = ht->arData + idx;
     432      325640 :                         if (!Z_REFCOUNTED(p->val)) continue;
     433      275641 :                         ref = Z_COUNTED(p->val);
     434      275641 :                         if (idx == ht->nNumUsed-1) {
     435      143454 :                                 goto tail_call;
     436             :                         } else {
     437      132187 :                                 gc_scan(ref);
     438             :                         }
     439             :                 }
     440             :         }
     441             : }
     442             : 
     443          74 : static void gc_scan_roots(void)
     444             : {
     445          74 :         gc_root_buffer *current = GC_G(roots).next;
     446             : 
     447      303242 :         while (current != &GC_G(roots)) {
     448      303094 :                 gc_scan(current->ref);
     449      303094 :                 current = current->next;
     450             :         }
     451          74 : }
     452             : 
     453      259765 : static int gc_collect_white(zend_refcounted *ref)
     454             : {
     455      259765 :         int count = 0;
     456             :         HashTable *ht;
     457             :         uint idx;
     458             :         Bucket *p;
     459             : 
     460             : tail_call:
     461      518538 :         if (GC_GET_COLOR(GC_INFO(ref)) == GC_WHITE) {
     462      296986 :                 ht = NULL;
     463      296986 :                 GC_SET_BLACK(GC_INFO(ref));
     464             : 
     465             :                 /* don't count references for compatibility ??? */
     466      296986 :                 if (GC_TYPE(ref) != IS_REFERENCE) {
     467      245977 :                         count++;
     468             :                 }
     469             : 
     470             : #if 1
     471      542962 :                 if ((GC_TYPE(ref) == IS_OBJECT || GC_TYPE(ref) == IS_ARRAY) &&
     472      245976 :                     !GC_ADDRESS(GC_INFO(ref))) {
     473             :                         /* add garbage into list */
     474       98405 :                         gc_root_buffer *buf = GC_G(unused);
     475             : 
     476       98405 :                         if (buf) {
     477       21118 :                                 GC_G(unused) = buf->prev;
     478       77287 :                         } else if (GC_G(first_unused) != GC_G(last_unused)) {
     479           1 :                                 buf = GC_G(first_unused);
     480           1 :                                 GC_G(first_unused)++;
     481             :                         } else {
     482             :                                 /* TODO: find a perfect way to handle such case */
     483       77286 :                                 GC_G(gc_full) = 1;
     484             :                         }
     485             : 
     486       98405 :                         if (buf) {
     487       21119 :                                 buf->ref = ref;
     488       21119 :                                 buf->next = GC_G(roots).next;
     489       21119 :                                 buf->prev = &GC_G(roots);
     490       21119 :                                 GC_G(roots).next->prev = buf;
     491       21119 :                                 GC_G(roots).next = buf;
     492       21119 :                                 GC_SET_ADDRESS(GC_INFO(ref), buf - GC_G(buf));
     493             :                         }
     494             :                 }
     495             : #endif
     496             : 
     497      296986 :                 if (GC_TYPE(ref) == IS_OBJECT) {
     498             :                         zend_object_get_gc_t get_gc;
     499      111599 :                         zend_object *obj = (zend_object*)ref;
     500             : 
     501      111599 :                         if (EXPECTED(IS_OBJ_VALID(EG(objects_store).object_buckets[obj->handle]) &&
     502             :                                      (get_gc = obj->handlers->get_gc) != NULL)) {
     503             :                                 int i, n;
     504             :                                 zval *table;
     505             :                                 zval tmp;
     506             :                                 HashTable *props;
     507             : 
     508      111599 :                                 ZVAL_OBJ(&tmp, obj);
     509      111599 :                                 props = get_gc(&tmp, &table, &n);
     510      269608 :                                 while (n > 0 && !Z_REFCOUNTED(table[n-1])) {
     511             :                                         /* count non-refcounted for compatibility ??? */
     512       92820 :                                         if (Z_TYPE(table[n-1]) != IS_UNDEF) {
     513       46410 :                                                 count++;
     514             :                                         }
     515       46410 :                                         n--;
     516             :                                 }
     517      194972 :                                 for (i = 0; i < n; i++) {
     518      166750 :                                         if (Z_REFCOUNTED(table[i])) {
     519       83385 :                                                 ref = Z_COUNTED(table[i]);
     520       83385 :                                                 GC_REFCOUNT(ref)++;
     521       83385 :                                                 if (!props && i == n - 1) {
     522             :                                                         goto tail_call;
     523             :                                                 } else {
     524           8 :                                                         count += gc_collect_white(ref);
     525             :                                                 }
     526             :                                         /* count non-refcounted for compatibility ??? */
     527      166730 :                                         } else if (Z_TYPE(table[i]) != IS_UNDEF) {
     528       83365 :                                                 count++;
     529             :                                         }
     530             :                                 }
     531       28222 :                                 if (!props) {
     532       18206 :                                         return count;
     533             :                                 }
     534       10016 :                                 ht = props;
     535             :                         }
     536      185387 :                 } else if (GC_TYPE(ref) == IS_ARRAY) {
     537      134377 :                         ht = (zend_array*)ref;
     538       51010 :                 } else if (GC_TYPE(ref) == IS_REFERENCE) {
     539       51009 :                         if (Z_REFCOUNTED(((zend_reference*)ref)->val)) {
     540       51009 :                                 ref = Z_COUNTED(((zend_reference*)ref)->val);
     541       51009 :                                 GC_REFCOUNT(ref)++;
     542       51009 :                                 goto tail_call;
     543             :                         }
     544           0 :                         return count;
     545             :                 }
     546             : 
     547      144394 :                 if (!ht) return count;
     548      287729 :                 for (idx = 0; idx < ht->nNumUsed; idx++) {
     549      267723 :                         p = ht->arData + idx;
     550      267723 :                         if (!Z_REFCOUNTED(p->val)) {
     551             :                                 /* count non-refcounted for compatibility ??? */
     552       89997 :                                 if (Z_TYPE(p->val) != IS_UNDEF && Z_TYPE(p->val) != IS_INDIRECT) {
     553       20000 :                                         count++;
     554             :                                 }
     555       29999 :                                 continue;
     556             :                         }
     557      237724 :                         ref = Z_COUNTED(p->val);
     558      237724 :                         GC_REFCOUNT(ref)++;
     559      237724 :                         if (idx == ht->nNumUsed-1) {
     560      124387 :                                 goto tail_call;
     561             :                         } else {
     562      113337 :                                 count += gc_collect_white(ref);
     563             :                         }
     564             :                 }
     565             :         }
     566      241558 :         return count;
     567             : }
     568             : 
     569          74 : static int gc_collect_roots(void)
     570             : {
     571          74 :         int count = 0;
     572          74 :         gc_root_buffer *current = GC_G(roots).next;
     573             : 
     574             :         /* remove non-garbage from the list */
     575      303242 :         while (current != &GC_G(roots)) {
     576      303094 :                 if (GC_GET_COLOR(GC_INFO(current->ref)) != GC_WHITE) {
     577      155523 :                         GC_SET_ADDRESS(GC_INFO(current->ref), 0);
     578             :                         GC_REMOVE_FROM_ROOTS(current);
     579             :                 }
     580      303094 :                 current = current->next;
     581             :         }
     582             : 
     583          74 :         current = GC_G(roots).next;
     584      147719 :         while (current != &GC_G(roots)) {
     585      147571 :                 if (GC_GET_COLOR(GC_INFO(current->ref)) == GC_WHITE) {
     586      146420 :                         GC_REFCOUNT(current->ref)++;
     587      146420 :                         count += gc_collect_white(current->ref);
     588             :                 }
     589      147571 :                 current = current->next;
     590             :         }
     591             : 
     592          74 :         if (GC_G(gc_full) == 1) {
     593           9 :                 current = GC_G(roots).next;
     594       90018 :                 while (current != &GC_G(roots)) {
     595       90000 :                         GC_SET_ADDRESS(GC_INFO(current->ref), 0);
     596       90000 :                         GC_SET_BLACK(GC_INFO(current->ref));
     597       90000 :                         current = current->next;
     598             :                 }
     599           9 :                 gc_reset();
     600           9 :                 return 0;
     601             :         }
     602             : 
     603             :         /* relink remaining roots into list to free */
     604          65 :         if (GC_G(roots).next != &GC_G(roots)) {
     605          45 :                 if (GC_G(to_free).next == &GC_G(to_free)) {
     606             :                         /* move roots into list to free */
     607          45 :                         GC_G(to_free).next = GC_G(roots).next;
     608          45 :                         GC_G(to_free).prev = GC_G(roots).prev;
     609          45 :                         GC_G(to_free).next->prev = &GC_G(to_free);
     610          45 :                         GC_G(to_free).prev->next = &GC_G(to_free);
     611             :                 } else {
     612             :                         /* add roots into list to free */
     613           0 :                         GC_G(to_free).prev->next = GC_G(roots).next;
     614           0 :                         GC_G(roots).next->prev = GC_G(to_free).prev;
     615           0 :                         GC_G(roots).prev->next = &GC_G(to_free);
     616           0 :                         GC_G(to_free).prev = GC_G(roots).prev;
     617             :                 }
     618             : 
     619          45 :                 GC_G(roots).next = &GC_G(roots);
     620          45 :                 GC_G(roots).prev = &GC_G(roots);
     621             :         }
     622          65 :         return count;
     623             : }
     624             : 
     625           2 : static void gc_remove_nested_data_from_buffer(zend_refcounted *ref)
     626             : {
     627           2 :         HashTable *ht = NULL;
     628             :         uint idx;
     629             :         Bucket *p;
     630             : 
     631             : tail_call:
     632           3 :         if (GC_ADDRESS(GC_INFO(ref)) != 0) {
     633           2 :                 GC_REMOVE_FROM_BUFFER(ref);
     634             : 
     635           2 :                 if (GC_TYPE(ref) == IS_OBJECT) {
     636             :                         zend_object_get_gc_t get_gc;
     637           1 :                         zend_object *obj = (zend_object*)ref;
     638             : 
     639           1 :                         if (EXPECTED(IS_OBJ_VALID(EG(objects_store).object_buckets[obj->handle]) &&
     640             :                              (get_gc = obj->handlers->get_gc) != NULL)) {
     641             :                                 int i, n;
     642             :                                 zval *table;
     643             :                                 zval tmp;
     644             :                                 HashTable *props;
     645             : 
     646           1 :                                 ZVAL_OBJ(&tmp, obj);
     647           1 :                                 props = get_gc(&tmp, &table, &n);
     648             : 
     649           1 :                                 while (n > 0 && !Z_REFCOUNTED(table[n-1])) n--;
     650           2 :                                 for (i = 0; i < n; i++) {
     651           1 :                                         if (Z_REFCOUNTED(table[i])) {
     652           1 :                                                 ref = Z_COUNTED(table[i]);
     653           1 :                                                 if (!props && i == n - 1) {
     654             :                                                         goto tail_call;
     655             :                                                 } else {
     656           1 :                                                         gc_remove_nested_data_from_buffer(ref);
     657             :                                                 }
     658             :                                         }
     659             :                                 }
     660           1 :                                 if (!props) {
     661           0 :                                         return;
     662             :                                 }
     663           1 :                                 ht = props;
     664             :                         }
     665           1 :                 } else if (GC_TYPE(ref) == IS_ARRAY) {
     666           1 :                         ht = (zend_array*)ref;
     667           0 :                 } else if (GC_TYPE(ref) == IS_REFERENCE) {
     668           0 :                         if (Z_REFCOUNTED(((zend_reference*)ref)->val)) {
     669           0 :                                 ref = Z_COUNTED(((zend_reference*)ref)->val);
     670           0 :                                 goto tail_call;
     671             :                         }
     672           0 :                         return;
     673             :                 }
     674           2 :                 if (!ht) return;
     675           3 :                 for (idx = 0; idx < ht->nNumUsed; idx++) {
     676           2 :                         p = ht->arData + idx;
     677           2 :                         if (!Z_REFCOUNTED(p->val)) continue;
     678           1 :                         ref = Z_COUNTED(p->val);
     679           1 :                         if (idx == ht->nNumUsed-1) {
     680           1 :                                 goto tail_call;
     681             :                         } else {
     682           0 :                                 gc_remove_nested_data_from_buffer(ref);
     683             :                         }
     684             :                 }
     685             :         }
     686             : }
     687             : 
     688          78 : ZEND_API int zend_gc_collect_cycles(void)
     689             : {
     690          78 :         int count = 0;
     691             : 
     692          78 :         if (GC_G(roots).next != &GC_G(roots)) {
     693             :                 gc_root_buffer *current, *orig_next_to_free;
     694             :                 zend_refcounted *p;
     695             :                 gc_root_buffer to_free;
     696             : 
     697          74 :                 if (GC_G(gc_active)) {
     698           0 :                         return 0;
     699             :                 }
     700          74 :                 GC_G(gc_runs)++;
     701          74 :                 GC_G(gc_active) = 1;
     702          74 :                 gc_mark_roots();
     703          74 :                 gc_scan_roots();
     704          74 :                 count = gc_collect_roots();
     705          74 :                 GC_G(gc_active) = 0;
     706             : 
     707          74 :                 if (GC_G(to_free).next == &GC_G(to_free)) {
     708             :                         /* nothing to free */
     709          29 :                         return 0;
     710             :                 }
     711             : 
     712             :                 /* Copy global to_free list into local list */
     713          45 :                 to_free.next = GC_G(to_free).next;
     714          45 :                 to_free.prev = GC_G(to_free).prev;
     715          45 :                 to_free.next->prev = &to_free;
     716          45 :                 to_free.prev->next = &to_free;
     717             : 
     718             :                 /* Free global list */
     719          45 :                 GC_G(to_free).next = &GC_G(to_free);
     720          45 :                 GC_G(to_free).prev = &GC_G(to_free);
     721             : 
     722          45 :                 orig_next_to_free = GC_G(next_to_free);
     723             : 
     724             :                 /* Remember reference counters before calling destructors */
     725          45 :                 current = to_free.next;
     726       78780 :                 while (current != &to_free) {
     727       78690 :                         current->refcount = GC_REFCOUNT(current->ref);
     728       78690 :                         current = current->next;
     729             :                 }
     730             : 
     731             :                 /* Call destructors */
     732          45 :                 if (EG(objects_store).object_buckets) {
     733          45 :                         current = to_free.next;
     734       78776 :                         while (current != &to_free) {
     735       78686 :                                 p = current->ref;
     736       78686 :                                 GC_G(next_to_free) = current->next;
     737       78686 :                                 if (GC_TYPE(p) == IS_OBJECT) {
     738       32952 :                                         zend_object *obj = (zend_object*)p;
     739             : 
     740       65904 :                                         if (IS_OBJ_VALID(EG(objects_store).object_buckets[obj->handle]) &&
     741       32952 :                                                 !(GC_FLAGS(obj) & IS_OBJ_DESTRUCTOR_CALLED)) {
     742             : 
     743        4749 :                                                 GC_FLAGS(obj) |= IS_OBJ_DESTRUCTOR_CALLED;
     744        4749 :                                                 if (obj->handlers->dtor_obj) {
     745        4749 :                                                         GC_REFCOUNT(obj)++;
     746        4749 :                                                         obj->handlers->dtor_obj(obj);
     747        4749 :                                                         GC_REFCOUNT(obj)--;
     748             :                                                 }
     749             :                                         }
     750             :                                 }
     751       78686 :                                 current = GC_G(next_to_free);
     752             :                         }
     753             : 
     754             :                         /* Remove values captured in destructors */
     755          45 :                         current = to_free.next;
     756       72357 :                         while (current != &to_free) {
     757       72267 :                                 GC_G(next_to_free) = current->next;
     758       72267 :                                 if (GC_REFCOUNT(current->ref) > current->refcount) {
     759           1 :                                         gc_remove_nested_data_from_buffer(current->ref);
     760             :                                 }
     761       72267 :                                 current = GC_G(next_to_free);
     762             :                         }
     763             :                 }
     764             : 
     765             :                 /* Destroy zvals */
     766          45 :                 current = to_free.next;
     767       72346 :                 while (current != &to_free) {
     768       72256 :                         p = current->ref;
     769       72256 :                         GC_G(next_to_free) = current->next;
     770       72256 :                         if (GC_TYPE(p) == IS_OBJECT) {
     771       31246 :                                 zend_object *obj = (zend_object*)p;
     772             : 
     773       93738 :                                 if (EG(objects_store).object_buckets &&
     774       31246 :                                     IS_OBJ_VALID(EG(objects_store).object_buckets[obj->handle]) &&
     775       31246 :                                         !(GC_FLAGS(obj) & IS_OBJ_FREE_CALLED)) {
     776             : 
     777       31246 :                                         GC_FLAGS(obj) |= IS_OBJ_FREE_CALLED;
     778       31246 :                                         if (obj->handlers->free_obj) {
     779       31246 :                                                 GC_REFCOUNT(obj)++;
     780       31246 :                                                 obj->handlers->free_obj(obj);
     781       31246 :                                                 GC_REFCOUNT(obj)--;
     782             :                                         }
     783             :                                 }
     784       41010 :                         } else if (GC_TYPE(p) == IS_ARRAY) {
     785       41010 :                                 zend_array *arr = (zend_array*)p;
     786             : 
     787       41010 :                                 GC_REFCOUNT(arr) = 0;
     788       41010 :                                 GC_TYPE(arr) = IS_NULL;
     789       41010 :                                 zend_hash_destroy(arr);
     790             :                         }
     791       72256 :                         current = GC_G(next_to_free);
     792             :                 }
     793             : 
     794             :                 /* Free objects */
     795          45 :                 current = to_free.next;
     796       72346 :                 while (current != &to_free) {
     797       72256 :                         p = current->ref;
     798       72256 :                         GC_G(next_to_free) = current->next;
     799       72256 :                         if (GC_TYPE(p) == IS_OBJECT) {
     800       31246 :                                 zend_object *obj = (zend_object*)p;
     801             : 
     802       62492 :                                 if (EG(objects_store).object_buckets &&
     803       31246 :                                     IS_OBJ_VALID(EG(objects_store).object_buckets[obj->handle])) {
     804             : 
     805       31246 :                                         zend_objects_store_free(obj);
     806             :                                 }
     807             :                         } else {
     808       41010 :                                 GC_REMOVE_FROM_BUFFER(p);
     809       41010 :                                 efree(p);
     810             :                         }
     811       72256 :                         current = GC_G(next_to_free);
     812             :                 }
     813             : 
     814          45 :                 GC_G(collected) += count;
     815          45 :                 GC_G(next_to_free) = orig_next_to_free;
     816             :         }
     817             : 
     818          49 :         return count;
     819             : }
     820             : 
     821             : /*
     822             :  * Local variables:
     823             :  * tab-width: 4
     824             :  * c-basic-offset: 4
     825             :  * indent-tabs-mode: t
     826             :  * End:
     827             :  */

Generated by: LCOV version 1.10

Generated at Tue, 14 Apr 2015 11:48:39 +0000 (11 days ago)

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