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

Generated by: LCOV version 1.10

Generated at Mon, 26 Jan 2015 14:46:41 +0000 (3 days ago)

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