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: 413 442 93.4 %
Date: 2014-10-22 Functions: 17 17 100.0 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.10

Generated at Wed, 22 Oct 2014 07:24:45 +0000 (3 days ago)

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