Back to index

php5  5.3.10
zend_gc.c
Go to the documentation of this file.
00001 /*
00002    +----------------------------------------------------------------------+
00003    | Zend Engine                                                          |
00004    +----------------------------------------------------------------------+
00005    | Copyright (c) 1998-2012 Zend Technologies Ltd. (http://www.zend.com) |
00006    +----------------------------------------------------------------------+
00007    | This source file is subject to version 2.00 of the Zend license,     |
00008    | that is bundled with this package in the file LICENSE, and is        |
00009    | available through the world-wide-web at the following url:           |
00010    | http://www.zend.com/license/2_00.txt.                                |
00011    | If you did not receive a copy of the Zend license and are unable to  |
00012    | obtain it through the world-wide-web, please send a note to          |
00013    | license@zend.com so we can mail you a copy immediately.              |
00014    +----------------------------------------------------------------------+
00015    | Authors: David Wang <planetbeing@gmail.com>                          |
00016    |          Dmitry Stogov <dmitry@zend.com>                             |
00017    +----------------------------------------------------------------------+
00018 */
00019 
00020 /* $Id: zend_gc.c 321634 2012-01-01 13:15:04Z felipe $ */
00021 
00022 #include "zend.h"
00023 #include "zend_API.h"
00024 
00025 #define GC_ROOT_BUFFER_MAX_ENTRIES 10000
00026 
00027 #ifdef ZTS
00028 ZEND_API int gc_globals_id;
00029 #else
00030 ZEND_API zend_gc_globals gc_globals;
00031 #endif
00032 
00033 static void root_buffer_dtor(zend_gc_globals *gc_globals TSRMLS_DC)
00034 {
00035        if (gc_globals->buf) {
00036               free(gc_globals->buf);
00037               gc_globals->buf = NULL;
00038        }      
00039 }
00040 
00041 static void gc_globals_ctor_ex(zend_gc_globals *gc_globals TSRMLS_DC)
00042 {
00043        gc_globals->gc_enabled = 0;
00044        gc_globals->gc_active = 0;
00045 
00046        gc_globals->buf = NULL;
00047 
00048        gc_globals->roots.next = &gc_globals->roots;
00049        gc_globals->roots.prev = &gc_globals->roots;
00050        gc_globals->unused = NULL;
00051        gc_globals->zval_to_free = NULL;
00052        gc_globals->free_list = NULL;
00053        gc_globals->next_to_free = NULL;
00054 
00055        gc_globals->gc_runs = 0;
00056        gc_globals->collected = 0;
00057 
00058 #if GC_BENCH
00059        gc_globals->root_buf_length = 0;
00060        gc_globals->root_buf_peak = 0;
00061        gc_globals->zval_possible_root = 0;
00062        gc_globals->zobj_possible_root = 0;
00063        gc_globals->zval_buffered = 0;
00064        gc_globals->zobj_buffered = 0;
00065        gc_globals->zval_remove_from_buffer = 0;
00066        gc_globals->zobj_remove_from_buffer = 0;
00067        gc_globals->zval_marked_grey = 0;
00068        gc_globals->zobj_marked_grey = 0;
00069 #endif
00070 }
00071 
00072 ZEND_API void gc_globals_ctor(TSRMLS_D)
00073 {
00074 #ifdef ZTS
00075        ts_allocate_id(&gc_globals_id, sizeof(zend_gc_globals), (ts_allocate_ctor) gc_globals_ctor_ex, (ts_allocate_dtor) root_buffer_dtor);
00076 #else
00077        gc_globals_ctor_ex(&gc_globals);
00078 #endif
00079 }
00080 
00081 ZEND_API void gc_globals_dtor(TSRMLS_D)
00082 {
00083 #ifndef ZTS
00084        root_buffer_dtor(&gc_globals TSRMLS_DC);
00085 #endif
00086 }
00087 
00088 ZEND_API void gc_reset(TSRMLS_D)
00089 {
00090        GC_G(gc_runs) = 0;
00091        GC_G(collected) = 0;
00092 
00093 #if GC_BENCH
00094        GC_G(root_buf_length) = 0;
00095        GC_G(root_buf_peak) = 0;
00096        GC_G(zval_possible_root) = 0;
00097        GC_G(zobj_possible_root) = 0;
00098        GC_G(zval_buffered) = 0;
00099        GC_G(zobj_buffered) = 0;
00100        GC_G(zval_remove_from_buffer) = 0;
00101        GC_G(zobj_remove_from_buffer) = 0;
00102        GC_G(zval_marked_grey) = 0;
00103        GC_G(zobj_marked_grey) = 0;
00104 #endif
00105 
00106        GC_G(roots).next = &GC_G(roots);
00107        GC_G(roots).prev = &GC_G(roots);
00108 
00109        if (GC_G(buf)) {
00110               GC_G(unused) = NULL;
00111               GC_G(first_unused) = GC_G(buf);
00112 
00113               GC_G(zval_to_free) = NULL;
00114        } else {
00115               GC_G(unused) = NULL;
00116               GC_G(first_unused) = NULL;
00117               GC_G(last_unused) = NULL;
00118        }
00119 }
00120 
00121 ZEND_API void gc_init(TSRMLS_D)
00122 {
00123        if (GC_G(buf) == NULL && GC_G(gc_enabled)) {
00124               GC_G(buf) = (gc_root_buffer*) malloc(sizeof(gc_root_buffer) * GC_ROOT_BUFFER_MAX_ENTRIES);
00125               GC_G(last_unused) = &GC_G(buf)[GC_ROOT_BUFFER_MAX_ENTRIES];
00126               gc_reset(TSRMLS_C);
00127        }
00128 }
00129 
00130 ZEND_API void gc_zval_possible_root(zval *zv TSRMLS_DC)
00131 {
00132        if (UNEXPECTED(GC_G(free_list) != NULL &&
00133                       GC_ZVAL_ADDRESS(zv) != NULL &&
00134                          GC_ZVAL_GET_COLOR(zv) == GC_BLACK) &&
00135                          (GC_ZVAL_ADDRESS(zv) < GC_G(buf) ||
00136                           GC_ZVAL_ADDRESS(zv) >= GC_G(last_unused))) {
00137               /* The given zval is a garbage that is going to be deleted by
00138                * currently running GC */
00139               return;
00140        }
00141 
00142        if (zv->type == IS_OBJECT) {
00143               GC_ZOBJ_CHECK_POSSIBLE_ROOT(zv);
00144               return;
00145        }
00146 
00147        GC_BENCH_INC(zval_possible_root);
00148 
00149        if (GC_ZVAL_GET_COLOR(zv) != GC_PURPLE) {
00150               GC_ZVAL_SET_PURPLE(zv);
00151 
00152               if (!GC_ZVAL_ADDRESS(zv)) {
00153                      gc_root_buffer *newRoot = GC_G(unused);
00154 
00155                      if (newRoot) {
00156                             GC_G(unused) = newRoot->prev;
00157                      } else if (GC_G(first_unused) != GC_G(last_unused)) {
00158                             newRoot = GC_G(first_unused);
00159                             GC_G(first_unused)++;
00160                      } else {
00161                             if (!GC_G(gc_enabled)) {
00162                                    GC_ZVAL_SET_BLACK(zv);
00163                                    return;
00164                             }
00165                             zv->refcount__gc++;
00166                             gc_collect_cycles(TSRMLS_C);
00167                             zv->refcount__gc--;
00168                             newRoot = GC_G(unused);
00169                             if (!newRoot) {
00170                                    return;
00171                             }
00172                             GC_ZVAL_SET_PURPLE(zv);
00173                             GC_G(unused) = newRoot->prev;
00174                      }
00175 
00176                      newRoot->next = GC_G(roots).next;
00177                      newRoot->prev = &GC_G(roots);
00178                      GC_G(roots).next->prev = newRoot;
00179                      GC_G(roots).next = newRoot;
00180 
00181                      GC_ZVAL_SET_ADDRESS(zv, newRoot);
00182 
00183                      newRoot->handle = 0;
00184                      newRoot->u.pz = zv;
00185 
00186                      GC_BENCH_INC(zval_buffered);
00187                      GC_BENCH_INC(root_buf_length);
00188                      GC_BENCH_PEAK(root_buf_peak, root_buf_length);
00189               }
00190        }
00191 }
00192 
00193 ZEND_API void gc_zobj_possible_root(zval *zv TSRMLS_DC)
00194 {
00195        struct _store_object *obj;
00196 
00197        if (UNEXPECTED(Z_OBJ_HT_P(zv)->get_properties == NULL ||
00198            EG(objects_store).object_buckets == NULL)) {
00199               return;
00200        }
00201 
00202        GC_BENCH_INC(zobj_possible_root);
00203 
00204        obj = &EG(objects_store).object_buckets[Z_OBJ_HANDLE_P(zv)].bucket.obj;
00205        if (GC_GET_COLOR(obj->buffered) != GC_PURPLE) {
00206               GC_SET_PURPLE(obj->buffered);
00207               if (!GC_ADDRESS(obj->buffered)) {
00208                      gc_root_buffer *newRoot = GC_G(unused);
00209 
00210                      if (newRoot) {
00211                             GC_G(unused) = newRoot->prev;
00212                      } else if (GC_G(first_unused) != GC_G(last_unused)) {
00213                             newRoot = GC_G(first_unused);
00214                             GC_G(first_unused)++;
00215                      } else {
00216                             if (!GC_G(gc_enabled)) {
00217                                    GC_ZVAL_SET_BLACK(zv);
00218                                    return;
00219                             }
00220                             zv->refcount__gc++;
00221                             gc_collect_cycles(TSRMLS_C);
00222                             zv->refcount__gc--;
00223                             newRoot = GC_G(unused);
00224                             if (!newRoot) {
00225                                    return;
00226                             }
00227                             obj = &EG(objects_store).object_buckets[Z_OBJ_HANDLE_P(zv)].bucket.obj;
00228                             GC_SET_PURPLE(obj->buffered);
00229                             GC_G(unused) = newRoot->prev;
00230                      }
00231 
00232                      newRoot->next = GC_G(roots).next;
00233                      newRoot->prev = &GC_G(roots);
00234                      GC_G(roots).next->prev = newRoot;
00235                      GC_G(roots).next = newRoot;
00236 
00237                      GC_SET_ADDRESS(obj->buffered, newRoot);
00238 
00239                      newRoot->handle = Z_OBJ_HANDLE_P(zv);
00240                      newRoot->u.handlers = Z_OBJ_HT_P(zv);
00241 
00242                      GC_BENCH_INC(zobj_buffered);
00243                      GC_BENCH_INC(root_buf_length);
00244                      GC_BENCH_PEAK(root_buf_peak, root_buf_length);
00245               }
00246        }
00247 }
00248 
00249 ZEND_API void gc_remove_zval_from_buffer(zval *zv TSRMLS_DC)
00250 {
00251        gc_root_buffer* root_buffer = GC_ADDRESS(((zval_gc_info*)zv)->u.buffered);
00252 
00253        if (UNEXPECTED(GC_G(free_list) != NULL &&
00254                          GC_ZVAL_GET_COLOR(zv) == GC_BLACK) &&
00255                          (GC_ZVAL_ADDRESS(zv) < GC_G(buf) ||
00256                           GC_ZVAL_ADDRESS(zv) >= GC_G(last_unused))) {
00257               /* The given zval is a garbage that is going to be deleted by
00258                * currently running GC */
00259               if (GC_G(next_to_free) == (zval_gc_info*)zv) {
00260                      GC_G(next_to_free) = ((zval_gc_info*)zv)->u.next;
00261               }
00262               return;
00263        }
00264        GC_BENCH_INC(zval_remove_from_buffer);
00265        GC_REMOVE_FROM_BUFFER(root_buffer);
00266        ((zval_gc_info*)zv)->u.buffered = NULL;
00267 }
00268 
00269 static void zval_scan_black(zval *pz TSRMLS_DC)
00270 {
00271        Bucket *p;
00272 
00273 tail_call:
00274        p = NULL;
00275        GC_ZVAL_SET_BLACK(pz);
00276 
00277        if (Z_TYPE_P(pz) == IS_OBJECT && EG(objects_store).object_buckets) {
00278               struct _store_object *obj = &EG(objects_store).object_buckets[Z_OBJ_HANDLE_P(pz)].bucket.obj;
00279 
00280               obj->refcount++;
00281               if (GC_GET_COLOR(obj->buffered) != GC_BLACK) {
00282                      GC_SET_BLACK(obj->buffered);
00283                      if (EXPECTED(EG(objects_store).object_buckets[Z_OBJ_HANDLE_P(pz)].valid &&
00284                                   Z_OBJ_HANDLER_P(pz, get_properties) != NULL)) {
00285                             HashTable *props = Z_OBJPROP_P(pz);
00286                             if(!props) {
00287                                    return;
00288                             }
00289                             p = props->pListHead;
00290                      }
00291               }
00292        } else if (Z_TYPE_P(pz) == IS_ARRAY) {
00293               if (Z_ARRVAL_P(pz) != &EG(symbol_table)) {
00294                      p = Z_ARRVAL_P(pz)->pListHead;
00295               }
00296        }
00297        while (p != NULL) {
00298               pz = *(zval**)p->pData;
00299               if (Z_TYPE_P(pz) != IS_ARRAY || Z_ARRVAL_P(pz) != &EG(symbol_table)) {
00300                      pz->refcount__gc++;
00301               }
00302               if (GC_ZVAL_GET_COLOR(pz) != GC_BLACK) {
00303                      if (p->pListNext == NULL) {
00304                             goto tail_call;
00305                      } else {
00306                             zval_scan_black(pz TSRMLS_CC);
00307                      }
00308               }
00309               p = p->pListNext;
00310        }
00311 }
00312 
00313 static void zobj_scan_black(struct _store_object *obj, zval *pz TSRMLS_DC)
00314 {
00315        Bucket *p;
00316 
00317        GC_SET_BLACK(obj->buffered);
00318        if (EXPECTED(EG(objects_store).object_buckets[Z_OBJ_HANDLE_P(pz)].valid &&
00319                     Z_OBJ_HANDLER_P(pz, get_properties) != NULL)) {
00320               HashTable *props = Z_OBJPROP_P(pz);
00321               if(!props) {
00322                      return;
00323               }
00324               p = props->pListHead;
00325               while (p != NULL) {
00326                      pz = *(zval**)p->pData;
00327                      if (Z_TYPE_P(pz) != IS_ARRAY || Z_ARRVAL_P(pz) != &EG(symbol_table)) {
00328                             pz->refcount__gc++;
00329                      }
00330                      if (GC_ZVAL_GET_COLOR(pz) != GC_BLACK) {
00331                             zval_scan_black(pz TSRMLS_CC);
00332                      }
00333                      p = p->pListNext;
00334               }
00335        }
00336 }
00337 
00338 static void zval_mark_grey(zval *pz TSRMLS_DC)
00339 {
00340        Bucket *p;
00341 
00342 tail_call:
00343        if (GC_ZVAL_GET_COLOR(pz) != GC_GREY) {
00344               p = NULL;
00345               GC_BENCH_INC(zval_marked_grey);
00346               GC_ZVAL_SET_COLOR(pz, GC_GREY);
00347 
00348               if (Z_TYPE_P(pz) == IS_OBJECT && EG(objects_store).object_buckets) {
00349                      struct _store_object *obj = &EG(objects_store).object_buckets[Z_OBJ_HANDLE_P(pz)].bucket.obj;
00350 
00351                      obj->refcount--;
00352                      if (GC_GET_COLOR(obj->buffered) != GC_GREY) {
00353                             GC_BENCH_INC(zobj_marked_grey);
00354                             GC_SET_COLOR(obj->buffered, GC_GREY);
00355                             if (EXPECTED(EG(objects_store).object_buckets[Z_OBJ_HANDLE_P(pz)].valid &&
00356                                          Z_OBJ_HANDLER_P(pz, get_properties) != NULL)) {
00357                                    HashTable *props = Z_OBJPROP_P(pz);
00358                                    if(!props) {
00359                                           return;
00360                                    }
00361                                    p = props->pListHead;
00362                             }
00363                      }
00364               } else if (Z_TYPE_P(pz) == IS_ARRAY) {
00365                      if (Z_ARRVAL_P(pz) == &EG(symbol_table)) {
00366                             GC_ZVAL_SET_BLACK(pz);
00367                      } else {
00368                             p = Z_ARRVAL_P(pz)->pListHead;
00369                      }
00370               }
00371               while (p != NULL) {
00372                      pz = *(zval**)p->pData;
00373                      if (Z_TYPE_P(pz) != IS_ARRAY || Z_ARRVAL_P(pz) != &EG(symbol_table)) {
00374                             pz->refcount__gc--;
00375                      }
00376                      if (p->pListNext == NULL) {
00377                             goto tail_call;
00378                      } else {
00379                             zval_mark_grey(pz TSRMLS_CC);
00380                      }
00381                      p = p->pListNext;
00382               }
00383        }
00384 }
00385 
00386 static void zobj_mark_grey(struct _store_object *obj, zval *pz TSRMLS_DC)
00387 {
00388        Bucket *p;
00389 
00390        if (GC_GET_COLOR(obj->buffered) != GC_GREY) {
00391               GC_BENCH_INC(zobj_marked_grey);
00392               GC_SET_COLOR(obj->buffered, GC_GREY);
00393               if (EXPECTED(EG(objects_store).object_buckets[Z_OBJ_HANDLE_P(pz)].valid &&
00394                            Z_OBJ_HANDLER_P(pz, get_properties) != NULL)) {
00395                      HashTable *props = Z_OBJPROP_P(pz);
00396                      if(!props) {
00397                             return;
00398                      }
00399                      p = props->pListHead;
00400                      while (p != NULL) {
00401                             pz = *(zval**)p->pData;
00402                             if (Z_TYPE_P(pz) != IS_ARRAY || Z_ARRVAL_P(pz) != &EG(symbol_table)) {
00403                                    pz->refcount__gc--;
00404                             }
00405                             zval_mark_grey(pz TSRMLS_CC);
00406                             p = p->pListNext;
00407                      }
00408               }
00409        }
00410 }
00411 
00412 static void gc_mark_roots(TSRMLS_D)
00413 {
00414        gc_root_buffer *current = GC_G(roots).next;
00415 
00416        while (current != &GC_G(roots)) {
00417               if (current->handle) {
00418                      if (EG(objects_store).object_buckets) {
00419                             struct _store_object *obj = &EG(objects_store).object_buckets[current->handle].bucket.obj;
00420 
00421                             if (GC_GET_COLOR(obj->buffered) == GC_PURPLE) {
00422                                    zval z;
00423 
00424                                    INIT_PZVAL(&z);
00425                                    Z_OBJ_HANDLE(z) = current->handle;
00426                                    Z_OBJ_HT(z) = current->u.handlers;
00427                                    zobj_mark_grey(obj, &z TSRMLS_CC);
00428                             } else {
00429                                    GC_SET_ADDRESS(obj->buffered, NULL);
00430                                    GC_REMOVE_FROM_BUFFER(current);
00431                             }
00432                      }
00433               } else {
00434                      if (GC_ZVAL_GET_COLOR(current->u.pz) == GC_PURPLE) {
00435                             zval_mark_grey(current->u.pz TSRMLS_CC);
00436                      } else {
00437                             GC_ZVAL_SET_ADDRESS(current->u.pz, NULL);
00438                             GC_REMOVE_FROM_BUFFER(current);
00439                      }
00440               }
00441               current = current->next;
00442        }
00443 }
00444 
00445 static int zval_scan(zval *pz TSRMLS_DC)
00446 {
00447        Bucket *p;
00448 
00449 tail_call:    
00450        if (GC_ZVAL_GET_COLOR(pz) == GC_GREY) {
00451               p = NULL;
00452               if (pz->refcount__gc > 0) {
00453                      zval_scan_black(pz TSRMLS_CC);
00454               } else {
00455                      GC_ZVAL_SET_COLOR(pz, GC_WHITE);
00456                      if (Z_TYPE_P(pz) == IS_OBJECT && EG(objects_store).object_buckets) {
00457                             struct _store_object *obj = &EG(objects_store).object_buckets[Z_OBJ_HANDLE_P(pz)].bucket.obj;
00458 
00459                             if (GC_GET_COLOR(obj->buffered) == GC_GREY) {
00460                                    if (obj->refcount > 0) {
00461                                           zobj_scan_black(obj, pz TSRMLS_CC);
00462                                    } else {
00463                                           GC_SET_COLOR(obj->buffered, GC_WHITE);
00464                                           if (EXPECTED(EG(objects_store).object_buckets[Z_OBJ_HANDLE_P(pz)].valid &&
00465                                                        Z_OBJ_HANDLER_P(pz, get_properties) != NULL)) {
00466                                                  HashTable *props = Z_OBJPROP_P(pz);
00467                                                  if(!props) {
00468                                                         return 0;
00469                                                  }
00470                                                  p = props->pListHead;
00471                                           }
00472                                    }
00473                             }
00474                      } else if (Z_TYPE_P(pz) == IS_ARRAY) {
00475                             if (Z_ARRVAL_P(pz) == &EG(symbol_table)) {
00476                                    GC_ZVAL_SET_BLACK(pz);
00477                             } else {
00478                                    p = Z_ARRVAL_P(pz)->pListHead;
00479                             }
00480                      }
00481               }
00482               while (p != NULL) {
00483                      if (p->pListNext == NULL) {
00484                             pz = *(zval**)p->pData;
00485                             goto tail_call;
00486                      } else {
00487                             zval_scan(*(zval**)p->pData TSRMLS_CC);
00488                      }
00489                      p = p->pListNext;
00490               }
00491        }
00492        return 0;
00493 }
00494 
00495 static void zobj_scan(zval *pz TSRMLS_DC)
00496 {
00497        Bucket *p;
00498 
00499        if (EG(objects_store).object_buckets) {
00500               struct _store_object *obj = &EG(objects_store).object_buckets[Z_OBJ_HANDLE_P(pz)].bucket.obj;
00501 
00502               if (GC_GET_COLOR(obj->buffered) == GC_GREY) {
00503                      if (obj->refcount > 0) {
00504                             zobj_scan_black(obj, pz TSRMLS_CC);
00505                      } else {
00506                             GC_SET_COLOR(obj->buffered, GC_WHITE);
00507                             if (EXPECTED(EG(objects_store).object_buckets[Z_OBJ_HANDLE_P(pz)].valid &&
00508                                          Z_OBJ_HANDLER_P(pz, get_properties) != NULL)) {
00509                                    HashTable *props = Z_OBJPROP_P(pz);
00510                                    if(!props) {
00511                                           return;
00512                                    }
00513                                    p = props->pListHead;
00514                                    while (p != NULL) {
00515                                           zval_scan(*(zval**)p->pData TSRMLS_CC);
00516                                           p = p->pListNext;
00517                                    }
00518                             }
00519                      }
00520               }
00521        }
00522 }
00523 
00524 static void gc_scan_roots(TSRMLS_D)
00525 {
00526        gc_root_buffer *current = GC_G(roots).next;
00527 
00528        while (current != &GC_G(roots)) {
00529               if (current->handle) {
00530                      zval z;
00531 
00532                      INIT_PZVAL(&z);
00533                      Z_OBJ_HANDLE(z) = current->handle;
00534                      Z_OBJ_HT(z) = current->u.handlers;
00535                      zobj_scan(&z TSRMLS_CC);
00536               } else {
00537                      zval_scan(current->u.pz TSRMLS_CC);
00538               }
00539               current = current->next;
00540        }
00541 }
00542 
00543 static void zval_collect_white(zval *pz TSRMLS_DC)
00544 {
00545        Bucket *p;
00546 
00547 tail_call:
00548        if (((zval_gc_info*)(pz))->u.buffered == (gc_root_buffer*)GC_WHITE) {
00549               p = NULL;
00550               GC_ZVAL_SET_BLACK(pz);
00551 
00552               if (Z_TYPE_P(pz) == IS_OBJECT && EG(objects_store).object_buckets) {
00553                      struct _store_object *obj = &EG(objects_store).object_buckets[Z_OBJ_HANDLE_P(pz)].bucket.obj;
00554 
00555                      if (obj->buffered == (gc_root_buffer*)GC_WHITE) {
00556                             GC_SET_BLACK(obj->buffered);
00557 
00558                             if (EXPECTED(EG(objects_store).object_buckets[Z_OBJ_HANDLE_P(pz)].valid &&
00559                                          Z_OBJ_HANDLER_P(pz, get_properties) != NULL)) {
00560                                    HashTable *props = Z_OBJPROP_P(pz);
00561                                    if(!props) {
00562                                           return;
00563                                    }
00564                                    p = props->pListHead;
00565                             }
00566                      }
00567               } else {
00568                      if (Z_TYPE_P(pz) == IS_ARRAY) {
00569                             p = Z_ARRVAL_P(pz)->pListHead;
00570                      }
00571               }
00572 
00573               /* restore refcount and put into list to free */
00574               pz->refcount__gc++;
00575               ((zval_gc_info*)pz)->u.next = GC_G(zval_to_free);
00576               GC_G(zval_to_free) = (zval_gc_info*)pz;
00577 
00578               while (p != NULL) {
00579                      pz = *(zval**)p->pData;
00580                      if (Z_TYPE_P(pz) != IS_ARRAY || Z_ARRVAL_P(pz) != &EG(symbol_table)) {
00581                             pz->refcount__gc++;
00582                      }
00583                      if (p->pListNext == NULL) {
00584                             goto tail_call;
00585                      } else {
00586                             zval_collect_white(pz TSRMLS_CC);
00587                      }
00588                      p = p->pListNext;
00589               }
00590        }
00591 }
00592 
00593 static void zobj_collect_white(zval *pz TSRMLS_DC)
00594 {
00595        Bucket *p;
00596 
00597        if (EG(objects_store).object_buckets) {
00598               struct _store_object *obj = &EG(objects_store).object_buckets[Z_OBJ_HANDLE_P(pz)].bucket.obj;
00599 
00600               if (obj->buffered == (gc_root_buffer*)GC_WHITE) {
00601                      GC_SET_BLACK(obj->buffered);
00602 
00603                      if (EXPECTED(EG(objects_store).object_buckets[Z_OBJ_HANDLE_P(pz)].valid &&
00604                                   Z_OBJ_HANDLER_P(pz, get_properties) != NULL)) {
00605                             HashTable *props = Z_OBJPROP_P(pz);
00606                             if(!props) {
00607                                    return;
00608                             }
00609                             p = props->pListHead;
00610                             while (p != NULL) {
00611                                    pz = *(zval**)p->pData;
00612                                    if (Z_TYPE_P(pz) != IS_ARRAY || Z_ARRVAL_P(pz) != &EG(symbol_table)) {
00613                                           pz->refcount__gc++;
00614                                    }
00615                                    zval_collect_white(pz TSRMLS_CC);
00616                                    p = p->pListNext;
00617                             }
00618                      }
00619               }
00620        }
00621 }
00622 
00623 static void gc_collect_roots(TSRMLS_D)
00624 {
00625        gc_root_buffer *current = GC_G(roots).next;
00626 
00627        while (current != &GC_G(roots)) {
00628               if (current->handle) {
00629                      if (EG(objects_store).object_buckets) {
00630                             struct _store_object *obj = &EG(objects_store).object_buckets[current->handle].bucket.obj;
00631                             zval z;
00632 
00633                             GC_SET_ADDRESS(obj->buffered, NULL);
00634                             INIT_PZVAL(&z);
00635                             Z_OBJ_HANDLE(z) = current->handle;
00636                             Z_OBJ_HT(z) = current->u.handlers;
00637                             zobj_collect_white(&z TSRMLS_CC);
00638                      }
00639               } else {
00640                      GC_ZVAL_SET_ADDRESS(current->u.pz, NULL);
00641                      zval_collect_white(current->u.pz TSRMLS_CC);
00642               }
00643 
00644               GC_REMOVE_FROM_BUFFER(current);
00645               current = current->next;
00646        }
00647 }
00648 
00649 #define FREE_LIST_END ((zval_gc_info*)(~(zend_uintptr_t)GC_COLOR))
00650 
00651 ZEND_API int gc_collect_cycles(TSRMLS_D)
00652 {
00653        int count = 0;
00654 
00655        if (GC_G(roots).next != &GC_G(roots)) {
00656               zval_gc_info *p, *q, *orig_free_list, *orig_next_to_free;
00657 
00658               if (GC_G(gc_active)) {
00659                      return 0;
00660               }
00661               GC_G(gc_runs)++;
00662               GC_G(zval_to_free) = FREE_LIST_END;
00663               GC_G(gc_active) = 1;
00664               gc_mark_roots(TSRMLS_C);
00665               gc_scan_roots(TSRMLS_C);
00666               gc_collect_roots(TSRMLS_C);
00667 
00668               orig_free_list = GC_G(free_list);
00669               orig_next_to_free = GC_G(next_to_free);
00670               p = GC_G(free_list) = GC_G(zval_to_free);
00671               GC_G(zval_to_free) = NULL;
00672               GC_G(gc_active) = 0;
00673 
00674               /* First call destructors */
00675               while (p != FREE_LIST_END) {
00676                      if (Z_TYPE(p->z) == IS_OBJECT) {
00677                             if (EG(objects_store).object_buckets &&
00678                                    EG(objects_store).object_buckets[Z_OBJ_HANDLE(p->z)].valid &&
00679                                    EG(objects_store).object_buckets[Z_OBJ_HANDLE(p->z)].bucket.obj.refcount <= 0 &&
00680                                    EG(objects_store).object_buckets[Z_OBJ_HANDLE(p->z)].bucket.obj.dtor &&
00681                                    !EG(objects_store).object_buckets[Z_OBJ_HANDLE(p->z)].destructor_called) {
00682 
00683                                    EG(objects_store).object_buckets[Z_OBJ_HANDLE(p->z)].destructor_called = 1;
00684                                    EG(objects_store).object_buckets[Z_OBJ_HANDLE(p->z)].bucket.obj.refcount++;
00685                                    EG(objects_store).object_buckets[Z_OBJ_HANDLE(p->z)].bucket.obj.dtor(EG(objects_store).object_buckets[Z_OBJ_HANDLE(p->z)].bucket.obj.object, Z_OBJ_HANDLE(p->z) TSRMLS_CC);
00686                                    EG(objects_store).object_buckets[Z_OBJ_HANDLE(p->z)].bucket.obj.refcount--;
00687                             }
00688                      }
00689                      count++;
00690                      p = p->u.next;
00691               }
00692 
00693               /* Destroy zvals */
00694               p = GC_G(free_list);
00695               while (p != FREE_LIST_END) {
00696                      GC_G(next_to_free) = p->u.next;
00697                      if (Z_TYPE(p->z) == IS_OBJECT) {
00698                             if (EG(objects_store).object_buckets &&
00699                                    EG(objects_store).object_buckets[Z_OBJ_HANDLE(p->z)].valid &&
00700                                    EG(objects_store).object_buckets[Z_OBJ_HANDLE(p->z)].bucket.obj.refcount <= 0) {
00701                                    EG(objects_store).object_buckets[Z_OBJ_HANDLE(p->z)].bucket.obj.refcount = 1;
00702                                    Z_TYPE(p->z) = IS_NULL;
00703                                    zend_objects_store_del_ref_by_handle_ex(Z_OBJ_HANDLE(p->z), Z_OBJ_HT(p->z) TSRMLS_CC);
00704                             }
00705                      } else if (Z_TYPE(p->z) == IS_ARRAY) {
00706                             Z_TYPE(p->z) = IS_NULL;
00707                             zend_hash_destroy(Z_ARRVAL(p->z));
00708                             FREE_HASHTABLE(Z_ARRVAL(p->z));
00709                      } else {
00710                             zval_dtor(&p->z);
00711                             Z_TYPE(p->z) = IS_NULL;
00712                      }
00713                      p = GC_G(next_to_free);
00714               }
00715 
00716               /* Free zvals */
00717               p = GC_G(free_list);
00718               while (p != FREE_LIST_END) {
00719                      q = p->u.next;
00720                      FREE_ZVAL_EX(&p->z);
00721                      p = q;
00722               }
00723               GC_G(collected) += count;
00724               GC_G(free_list) = orig_free_list;
00725               GC_G(next_to_free) = orig_next_to_free;
00726        }
00727 
00728        return count;
00729 }
00730 
00731 /*
00732  * Local variables:
00733  * tab-width: 4
00734  * c-basic-offset: 4
00735  * indent-tabs-mode: t
00736  * End:
00737  */