root/Zend/zend_gc.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. root_buffer_dtor
  2. gc_globals_ctor_ex
  3. gc_globals_ctor
  4. gc_globals_dtor
  5. gc_reset
  6. gc_init
  7. gc_zval_possible_root
  8. gc_zobj_possible_root
  9. gc_remove_zval_from_buffer
  10. zval_scan_black
  11. zobj_scan_black
  12. zval_mark_grey
  13. zobj_mark_grey
  14. gc_mark_roots
  15. zval_scan
  16. zobj_scan
  17. gc_scan_roots
  18. zval_collect_white
  19. zobj_collect_white
  20. gc_collect_roots
  21. gc_collect_cycles

   1 /*
   2    +----------------------------------------------------------------------+
   3    | Zend Engine                                                          |
   4    +----------------------------------------------------------------------+
   5    | Copyright (c) 1998-2016 Zend Technologies Ltd. (http://www.zend.com) |
   6    +----------------------------------------------------------------------+
   7    | This source file is subject to version 2.00 of the Zend license,     |
   8    | that is bundled with this package in the file LICENSE, and is        |
   9    | available through the world-wide-web at the following url:           |
  10    | http://www.zend.com/license/2_00.txt.                                |
  11    | If you did not receive a copy of the Zend license and are unable to  |
  12    | obtain it through the world-wide-web, please send a note to          |
  13    | license@zend.com so we can mail you a copy immediately.              |
  14    +----------------------------------------------------------------------+
  15    | Authors: David Wang <planetbeing@gmail.com>                          |
  16    |          Dmitry Stogov <dmitry@zend.com>                             |
  17    +----------------------------------------------------------------------+
  18 */
  19 
  20 /* $Id$ */
  21 
  22 #include "zend.h"
  23 #include "zend_API.h"
  24 
  25 #define GC_ROOT_BUFFER_MAX_ENTRIES 10000
  26 
  27 #ifdef ZTS
  28 ZEND_API int gc_globals_id;
  29 #else
  30 ZEND_API zend_gc_globals gc_globals;
  31 #endif
  32 
  33 static void root_buffer_dtor(zend_gc_globals *gc_globals TSRMLS_DC)
  34 {
  35         if (gc_globals->buf) {
  36                 free(gc_globals->buf);
  37                 gc_globals->buf = NULL;
  38         }       
  39 }
  40 
  41 static void gc_globals_ctor_ex(zend_gc_globals *gc_globals TSRMLS_DC)
  42 {
  43         gc_globals->gc_enabled = 0;
  44         gc_globals->gc_active = 0;
  45 
  46         gc_globals->buf = NULL;
  47 
  48         gc_globals->roots.next = &gc_globals->roots;
  49         gc_globals->roots.prev = &gc_globals->roots;
  50         gc_globals->unused = NULL;
  51         gc_globals->zval_to_free = NULL;
  52         gc_globals->free_list = NULL;
  53         gc_globals->next_to_free = NULL;
  54 
  55         gc_globals->gc_runs = 0;
  56         gc_globals->collected = 0;
  57 
  58 #if GC_BENCH
  59         gc_globals->root_buf_length = 0;
  60         gc_globals->root_buf_peak = 0;
  61         gc_globals->zval_possible_root = 0;
  62         gc_globals->zobj_possible_root = 0;
  63         gc_globals->zval_buffered = 0;
  64         gc_globals->zobj_buffered = 0;
  65         gc_globals->zval_remove_from_buffer = 0;
  66         gc_globals->zobj_remove_from_buffer = 0;
  67         gc_globals->zval_marked_grey = 0;
  68         gc_globals->zobj_marked_grey = 0;
  69 #endif
  70 }
  71 
  72 ZEND_API void gc_globals_ctor(TSRMLS_D)
  73 {
  74 #ifdef ZTS
  75         ts_allocate_id(&gc_globals_id, sizeof(zend_gc_globals), (ts_allocate_ctor) gc_globals_ctor_ex, (ts_allocate_dtor) root_buffer_dtor);
  76 #else
  77         gc_globals_ctor_ex(&gc_globals);
  78 #endif
  79 }
  80 
  81 ZEND_API void gc_globals_dtor(TSRMLS_D)
  82 {
  83 #ifndef ZTS
  84         root_buffer_dtor(&gc_globals TSRMLS_DC);
  85 #endif
  86 }
  87 
  88 ZEND_API void gc_reset(TSRMLS_D)
  89 {
  90         GC_G(gc_runs) = 0;
  91         GC_G(collected) = 0;
  92 
  93 #if GC_BENCH
  94         GC_G(root_buf_length) = 0;
  95         GC_G(root_buf_peak) = 0;
  96         GC_G(zval_possible_root) = 0;
  97         GC_G(zobj_possible_root) = 0;
  98         GC_G(zval_buffered) = 0;
  99         GC_G(zobj_buffered) = 0;
 100         GC_G(zval_remove_from_buffer) = 0;
 101         GC_G(zobj_remove_from_buffer) = 0;
 102         GC_G(zval_marked_grey) = 0;
 103         GC_G(zobj_marked_grey) = 0;
 104 #endif
 105 
 106         GC_G(roots).next = &GC_G(roots);
 107         GC_G(roots).prev = &GC_G(roots);
 108 
 109         if (GC_G(buf)) {
 110                 GC_G(unused) = NULL;
 111                 GC_G(first_unused) = GC_G(buf);
 112 
 113                 GC_G(zval_to_free) = NULL;
 114         } else {
 115                 GC_G(unused) = NULL;
 116                 GC_G(first_unused) = NULL;
 117                 GC_G(last_unused) = NULL;
 118         }
 119 }
 120 
 121 ZEND_API void gc_init(TSRMLS_D)
 122 {
 123         if (GC_G(buf) == NULL && GC_G(gc_enabled)) {
 124                 GC_G(buf) = (gc_root_buffer*) malloc(sizeof(gc_root_buffer) * GC_ROOT_BUFFER_MAX_ENTRIES);
 125                 GC_G(last_unused) = &GC_G(buf)[GC_ROOT_BUFFER_MAX_ENTRIES];
 126                 gc_reset(TSRMLS_C);
 127         }
 128 }
 129 
 130 ZEND_API void gc_zval_possible_root(zval *zv TSRMLS_DC)
 131 {
 132         if (UNEXPECTED(GC_G(free_list) != NULL &&
 133                        GC_ZVAL_ADDRESS(zv) != NULL &&
 134                            GC_ZVAL_GET_COLOR(zv) == GC_BLACK) &&
 135                            (GC_ZVAL_ADDRESS(zv) < GC_G(buf) ||
 136                             GC_ZVAL_ADDRESS(zv) >= GC_G(last_unused))) {
 137                 /* The given zval is a garbage that is going to be deleted by
 138                  * currently running GC */
 139                 return;
 140         }
 141 
 142         if (zv->type == IS_OBJECT) {
 143                 GC_ZOBJ_CHECK_POSSIBLE_ROOT(zv);
 144                 return;
 145         }
 146 
 147         GC_BENCH_INC(zval_possible_root);
 148 
 149         if (GC_ZVAL_GET_COLOR(zv) != GC_PURPLE) {
 150                 if (!GC_ZVAL_ADDRESS(zv)) {
 151                         gc_root_buffer *newRoot = GC_G(unused);
 152 
 153                         if (newRoot) {
 154                                 GC_G(unused) = newRoot->prev;
 155                         } else if (GC_G(first_unused) != GC_G(last_unused)) {
 156                                 newRoot = GC_G(first_unused);
 157                                 GC_G(first_unused)++;
 158                         } else {
 159                                 if (!GC_G(gc_enabled)) {
 160                                         return;
 161                                 }
 162                                 zv->refcount__gc++;
 163                                 gc_collect_cycles(TSRMLS_C);
 164                                 zv->refcount__gc--;
 165                                 newRoot = GC_G(unused);
 166                                 if (!newRoot) {
 167                                         return;
 168                                 }
 169                                 GC_G(unused) = newRoot->prev;
 170                         }
 171 
 172                         GC_ZVAL_SET_PURPLE(zv);
 173                         newRoot->next = GC_G(roots).next;
 174                         newRoot->prev = &GC_G(roots);
 175                         GC_G(roots).next->prev = newRoot;
 176                         GC_G(roots).next = newRoot;
 177 
 178                         GC_ZVAL_SET_ADDRESS(zv, newRoot);
 179 
 180                         newRoot->handle = 0;
 181                         newRoot->u.pz = zv;
 182 
 183                         GC_BENCH_INC(zval_buffered);
 184                         GC_BENCH_INC(root_buf_length);
 185                         GC_BENCH_PEAK(root_buf_peak, root_buf_length);
 186                 }
 187         }
 188 }
 189 
 190 ZEND_API void gc_zobj_possible_root(zval *zv TSRMLS_DC)
 191 {
 192         struct _store_object *obj;
 193 
 194         if (UNEXPECTED(Z_OBJ_HT_P(zv)->get_gc == NULL ||
 195             EG(objects_store).object_buckets == NULL)) {
 196                 return;
 197         }
 198 
 199         GC_BENCH_INC(zobj_possible_root);
 200 
 201         obj = &EG(objects_store).object_buckets[Z_OBJ_HANDLE_P(zv)].bucket.obj;
 202         if (GC_GET_COLOR(obj->buffered) != GC_PURPLE) {
 203                 if (!GC_ADDRESS(obj->buffered)) {
 204                         gc_root_buffer *newRoot = GC_G(unused);
 205 
 206                         if (newRoot) {
 207                                 GC_G(unused) = newRoot->prev;
 208                         } else if (GC_G(first_unused) != GC_G(last_unused)) {
 209                                 newRoot = GC_G(first_unused);
 210                                 GC_G(first_unused)++;
 211                         } else {
 212                                 if (!GC_G(gc_enabled)) {
 213                                         return;
 214                                 }
 215                                 zv->refcount__gc++;
 216                                 gc_collect_cycles(TSRMLS_C);
 217                                 zv->refcount__gc--;
 218                                 newRoot = GC_G(unused);
 219                                 if (!newRoot) {
 220                                         return;
 221                                 }
 222                                 obj = &EG(objects_store).object_buckets[Z_OBJ_HANDLE_P(zv)].bucket.obj;
 223                                 GC_G(unused) = newRoot->prev;
 224                         }
 225 
 226                         GC_SET_PURPLE(obj->buffered);
 227                         newRoot->next = GC_G(roots).next;
 228                         newRoot->prev = &GC_G(roots);
 229                         GC_G(roots).next->prev = newRoot;
 230                         GC_G(roots).next = newRoot;
 231 
 232                         GC_SET_ADDRESS(obj->buffered, newRoot);
 233 
 234                         newRoot->handle = Z_OBJ_HANDLE_P(zv);
 235                         newRoot->u.handlers = Z_OBJ_HT_P(zv);
 236 
 237                         GC_BENCH_INC(zobj_buffered);
 238                         GC_BENCH_INC(root_buf_length);
 239                         GC_BENCH_PEAK(root_buf_peak, root_buf_length);
 240                 }
 241         }
 242 }
 243 
 244 ZEND_API void gc_remove_zval_from_buffer(zval *zv TSRMLS_DC)
 245 {
 246         gc_root_buffer* root_buffer = GC_ADDRESS(((zval_gc_info*)zv)->u.buffered);
 247 
 248         if (UNEXPECTED(GC_G(free_list) != NULL &&
 249                            GC_ZVAL_GET_COLOR(zv) == GC_BLACK) &&
 250                            (GC_ZVAL_ADDRESS(zv) < GC_G(buf) ||
 251                             GC_ZVAL_ADDRESS(zv) >= GC_G(last_unused))) {
 252                 /* The given zval is a garbage that is going to be deleted by
 253                  * currently running GC */
 254                 if (GC_G(next_to_free) == (zval_gc_info*)zv) {
 255                         GC_G(next_to_free) = ((zval_gc_info*)zv)->u.next;
 256                 }
 257                 return;
 258         }
 259         GC_BENCH_INC(zval_remove_from_buffer);
 260         GC_REMOVE_FROM_BUFFER(root_buffer);
 261         ((zval_gc_info*)zv)->u.buffered = NULL;
 262 }
 263 
 264 static void zval_scan_black(zval *pz TSRMLS_DC)
 265 {
 266         Bucket *p;
 267 
 268 tail_call:
 269         p = NULL;
 270         GC_ZVAL_SET_BLACK(pz);
 271 
 272         if (Z_TYPE_P(pz) == IS_OBJECT && EG(objects_store).object_buckets) {
 273                 zend_object_get_gc_t get_gc;
 274                 struct _store_object *obj = &EG(objects_store).object_buckets[Z_OBJ_HANDLE_P(pz)].bucket.obj;
 275 
 276                 obj->refcount++;
 277                 if (GC_GET_COLOR(obj->buffered) != GC_BLACK) {
 278                         GC_SET_BLACK(obj->buffered);
 279                         if (EXPECTED(EG(objects_store).object_buckets[Z_OBJ_HANDLE_P(pz)].valid &&
 280                                      (get_gc = Z_OBJ_HANDLER_P(pz, get_gc)) != NULL)) {
 281                                 int i, n;
 282                                 zval **table;
 283                                 HashTable *props = get_gc(pz, &table, &n TSRMLS_CC);
 284 
 285                                 while (n > 0 && !table[n-1]) n--;
 286                                 for (i = 0; i < n; i++) {
 287                                         if (table[i]) {
 288                                                 pz = table[i];
 289                                                 if (Z_TYPE_P(pz) != IS_ARRAY || Z_ARRVAL_P(pz) != &EG(symbol_table)) {
 290                                                         pz->refcount__gc++;
 291                                                 }
 292                                                 if (GC_ZVAL_GET_COLOR(pz) != GC_BLACK) {
 293                                                         if (!props && i == n - 1) {
 294                                                                 goto tail_call;
 295                                                         } else {
 296                                                                 zval_scan_black(pz TSRMLS_CC);
 297                                                         }
 298                                                 }
 299                                         }
 300                                 }
 301                                 if (!props) {
 302                                         return;
 303                                 }
 304                                 p = props->pListHead;
 305                         }
 306                 }
 307         } else if (Z_TYPE_P(pz) == IS_ARRAY) {
 308                 if (Z_ARRVAL_P(pz) != &EG(symbol_table)) {
 309                         p = Z_ARRVAL_P(pz)->pListHead;
 310                 }
 311         }
 312         while (p != NULL) {
 313                 pz = *(zval**)p->pData;
 314                 if (Z_TYPE_P(pz) != IS_ARRAY || Z_ARRVAL_P(pz) != &EG(symbol_table)) {
 315                         pz->refcount__gc++;
 316                 }
 317                 if (GC_ZVAL_GET_COLOR(pz) != GC_BLACK) {
 318                         if (p->pListNext == NULL) {
 319                                 goto tail_call;
 320                         } else {
 321                                 zval_scan_black(pz TSRMLS_CC);
 322                         }
 323                 }
 324                 p = p->pListNext;
 325         }
 326 }
 327 
 328 static void zobj_scan_black(struct _store_object *obj, zval *pz TSRMLS_DC)
 329 {
 330         Bucket *p;
 331         zend_object_get_gc_t get_gc;
 332 
 333         GC_SET_BLACK(obj->buffered);
 334         if (EXPECTED(EG(objects_store).object_buckets[Z_OBJ_HANDLE_P(pz)].valid &&
 335                      (get_gc = Z_OBJ_HANDLER_P(pz, get_gc)) != NULL)) {
 336                 int i, n;
 337                 zval **table;
 338                 HashTable *props = get_gc(pz, &table, &n TSRMLS_CC);
 339 
 340                 for (i = 0; i < n; i++) {
 341                         if (table[i]) {
 342                                 pz = table[i];
 343                                 if (Z_TYPE_P(pz) != IS_ARRAY || Z_ARRVAL_P(pz) != &EG(symbol_table)) {
 344                                         pz->refcount__gc++;
 345                                 }
 346                                 if (GC_ZVAL_GET_COLOR(pz) != GC_BLACK) {
 347                                         zval_scan_black(pz TSRMLS_CC);
 348                                 }
 349                         }
 350                 }
 351                 if (!props) {
 352                         return;
 353                 }
 354                 p = props->pListHead;
 355                 while (p != NULL) {
 356                         pz = *(zval**)p->pData;
 357                         if (Z_TYPE_P(pz) != IS_ARRAY || Z_ARRVAL_P(pz) != &EG(symbol_table)) {
 358                                 pz->refcount__gc++;
 359                         }
 360                         if (GC_ZVAL_GET_COLOR(pz) != GC_BLACK) {
 361                                 zval_scan_black(pz TSRMLS_CC);
 362                         }
 363                         p = p->pListNext;
 364                 }
 365         }
 366 }
 367 
 368 static void zval_mark_grey(zval *pz TSRMLS_DC)
 369 {
 370         Bucket *p;
 371 
 372 tail_call:
 373         if (GC_ZVAL_GET_COLOR(pz) != GC_GREY) {
 374                 p = NULL;
 375                 GC_BENCH_INC(zval_marked_grey);
 376                 GC_ZVAL_SET_COLOR(pz, GC_GREY);
 377 
 378                 if (Z_TYPE_P(pz) == IS_OBJECT && EG(objects_store).object_buckets) {
 379                         zend_object_get_gc_t get_gc;
 380                         struct _store_object *obj = &EG(objects_store).object_buckets[Z_OBJ_HANDLE_P(pz)].bucket.obj;
 381 
 382                         obj->refcount--;
 383                         if (GC_GET_COLOR(obj->buffered) != GC_GREY) {
 384                                 GC_BENCH_INC(zobj_marked_grey);
 385                                 GC_SET_COLOR(obj->buffered, GC_GREY);
 386                                 if (EXPECTED(EG(objects_store).object_buckets[Z_OBJ_HANDLE_P(pz)].valid &&
 387                                              (get_gc = Z_OBJ_HANDLER_P(pz, get_gc)) != NULL)) {
 388                                         int i, n;
 389                                         zval **table;
 390                                         HashTable *props = get_gc(pz, &table, &n TSRMLS_CC);
 391 
 392                                         while (n > 0 && !table[n-1]) n--;
 393                                         for (i = 0; i < n; i++) {
 394                                                 if (table[i]) {
 395                                                         pz = table[i];
 396                                                         if (Z_TYPE_P(pz) != IS_ARRAY || Z_ARRVAL_P(pz) != &EG(symbol_table)) {
 397                                                                 pz->refcount__gc--;
 398                                                         }
 399                                                         if (!props && i == n - 1) {
 400                                                                 goto tail_call;
 401                                                         } else {
 402                                                                 zval_mark_grey(pz TSRMLS_CC);
 403                                                         }
 404                                                 }
 405                                         }
 406                                         if (!props) {
 407                                                 return;
 408                                         }
 409                                         p = props->pListHead;
 410                                 }
 411                         }
 412                 } else if (Z_TYPE_P(pz) == IS_ARRAY) {
 413                         if (Z_ARRVAL_P(pz) == &EG(symbol_table)) {
 414                                 GC_ZVAL_SET_BLACK(pz);
 415                         } else {
 416                                 p = Z_ARRVAL_P(pz)->pListHead;
 417                         }
 418                 }
 419                 while (p != NULL) {
 420                         pz = *(zval**)p->pData;
 421                         if (Z_TYPE_P(pz) != IS_ARRAY || Z_ARRVAL_P(pz) != &EG(symbol_table)) {
 422                                 pz->refcount__gc--;
 423                         }
 424                         if (p->pListNext == NULL) {
 425                                 goto tail_call;
 426                         } else {
 427                                 zval_mark_grey(pz TSRMLS_CC);
 428                         }
 429                         p = p->pListNext;
 430                 }
 431         }
 432 }
 433 
 434 static void zobj_mark_grey(struct _store_object *obj, zval *pz TSRMLS_DC)
 435 {
 436         Bucket *p;
 437         zend_object_get_gc_t get_gc;
 438 
 439         if (GC_GET_COLOR(obj->buffered) != GC_GREY) {
 440                 GC_BENCH_INC(zobj_marked_grey);
 441                 GC_SET_COLOR(obj->buffered, GC_GREY);
 442                 if (EXPECTED(EG(objects_store).object_buckets[Z_OBJ_HANDLE_P(pz)].valid &&
 443                              (get_gc = Z_OBJ_HANDLER_P(pz, get_gc)) != NULL)) {
 444                         int i, n;
 445                         zval **table;
 446                         HashTable *props = get_gc(pz, &table, &n TSRMLS_CC);
 447 
 448                         for (i = 0; i < n; i++) {
 449                                 if (table[i]) {
 450                                         pz = table[i];
 451                                         if (Z_TYPE_P(pz) != IS_ARRAY || Z_ARRVAL_P(pz) != &EG(symbol_table)) {
 452                                                 pz->refcount__gc--;
 453                                         }
 454                                         zval_mark_grey(pz TSRMLS_CC);
 455                                 }
 456                         }
 457                         if (!props) {
 458                                 return;
 459                         }
 460                         p = props->pListHead;
 461                         while (p != NULL) {
 462                                 pz = *(zval**)p->pData;
 463                                 if (Z_TYPE_P(pz) != IS_ARRAY || Z_ARRVAL_P(pz) != &EG(symbol_table)) {
 464                                         pz->refcount__gc--;
 465                                 }
 466                                 zval_mark_grey(pz TSRMLS_CC);
 467                                 p = p->pListNext;
 468                         }
 469                 }
 470         }
 471 }
 472 
 473 static void gc_mark_roots(TSRMLS_D)
 474 {
 475         gc_root_buffer *current = GC_G(roots).next;
 476 
 477         while (current != &GC_G(roots)) {
 478                 if (current->handle) {
 479                         if (EG(objects_store).object_buckets) {
 480                                 struct _store_object *obj = &EG(objects_store).object_buckets[current->handle].bucket.obj;
 481 
 482                                 if (GC_GET_COLOR(obj->buffered) == GC_PURPLE) {
 483                                         zval z;
 484 
 485                                         INIT_PZVAL(&z);
 486                                         Z_OBJ_HANDLE(z) = current->handle;
 487                                         Z_OBJ_HT(z) = current->u.handlers;
 488                                         zobj_mark_grey(obj, &z TSRMLS_CC);
 489                                 } else {
 490                                         GC_SET_ADDRESS(obj->buffered, NULL);
 491                                         GC_REMOVE_FROM_BUFFER(current);
 492                                 }
 493                         }
 494                 } else {
 495                         if (GC_ZVAL_GET_COLOR(current->u.pz) == GC_PURPLE) {
 496                                 zval_mark_grey(current->u.pz TSRMLS_CC);
 497                         } else {
 498                                 GC_ZVAL_SET_ADDRESS(current->u.pz, NULL);
 499                                 GC_REMOVE_FROM_BUFFER(current);
 500                         }
 501                 }
 502                 current = current->next;
 503         }
 504 }
 505 
 506 static void zval_scan(zval *pz TSRMLS_DC)
 507 {
 508         Bucket *p;
 509 
 510 tail_call:      
 511         if (GC_ZVAL_GET_COLOR(pz) == GC_GREY) {
 512                 p = NULL;
 513                 if (pz->refcount__gc > 0) {
 514                         zval_scan_black(pz TSRMLS_CC);
 515                 } else {
 516                         GC_ZVAL_SET_COLOR(pz, GC_WHITE);
 517                         if (Z_TYPE_P(pz) == IS_OBJECT && EG(objects_store).object_buckets) {
 518                                 zend_object_get_gc_t get_gc;
 519                                 struct _store_object *obj = &EG(objects_store).object_buckets[Z_OBJ_HANDLE_P(pz)].bucket.obj;
 520 
 521                                 if (GC_GET_COLOR(obj->buffered) == GC_GREY) {
 522                                         if (obj->refcount > 0) {
 523                                                 zobj_scan_black(obj, pz TSRMLS_CC);
 524                                         } else {
 525                                                 GC_SET_COLOR(obj->buffered, GC_WHITE);
 526                                                 if (EXPECTED(EG(objects_store).object_buckets[Z_OBJ_HANDLE_P(pz)].valid &&
 527                                                              (get_gc = Z_OBJ_HANDLER_P(pz, get_gc)) != NULL)) {
 528                                                         int i, n;
 529                                                         zval **table;
 530                                                         HashTable *props = get_gc(pz, &table, &n TSRMLS_CC);
 531 
 532                                                         while (n > 0 && !table[n-1]) n--;
 533                                                         for (i = 0; i < n; i++) {
 534                                                                 if (table[i]) {
 535                                                                         pz = table[i];
 536                                                                         if (!props && i == n - 1) {
 537                                                                                 goto tail_call;
 538                                                                         } else {
 539                                                                                 zval_scan(pz TSRMLS_CC);
 540                                                                         }
 541                                                                 }
 542                                                         }
 543                                                         if (!props) {
 544                                                                 return;
 545                                                         }
 546                                                         p = props->pListHead;
 547                                                 }
 548                                         }
 549                                 }
 550                         } else if (Z_TYPE_P(pz) == IS_ARRAY) {
 551                                 if (Z_ARRVAL_P(pz) == &EG(symbol_table)) {
 552                                         GC_ZVAL_SET_BLACK(pz);
 553                                 } else {
 554                                         p = Z_ARRVAL_P(pz)->pListHead;
 555                                 }
 556                         }
 557                 }
 558                 while (p != NULL) {
 559                         if (p->pListNext == NULL) {
 560                                 pz = *(zval**)p->pData;
 561                                 goto tail_call;
 562                         } else {
 563                                 zval_scan(*(zval**)p->pData TSRMLS_CC);
 564                         }
 565                         p = p->pListNext;
 566                 }
 567         }
 568 }
 569 
 570 static void zobj_scan(zval *pz TSRMLS_DC)
 571 {
 572         Bucket *p;
 573         zend_object_get_gc_t get_gc;
 574 
 575         if (EG(objects_store).object_buckets) {
 576                 struct _store_object *obj = &EG(objects_store).object_buckets[Z_OBJ_HANDLE_P(pz)].bucket.obj;
 577 
 578                 if (GC_GET_COLOR(obj->buffered) == GC_GREY) {
 579                         if (obj->refcount > 0) {
 580                                 zobj_scan_black(obj, pz TSRMLS_CC);
 581                         } else {
 582                                 GC_SET_COLOR(obj->buffered, GC_WHITE);
 583                                 if (EXPECTED(EG(objects_store).object_buckets[Z_OBJ_HANDLE_P(pz)].valid &&
 584                                              (get_gc = Z_OBJ_HANDLER_P(pz, get_gc)) != NULL)) {
 585                                         int i, n;
 586                                         zval **table;
 587                                         HashTable *props = get_gc(pz, &table, &n TSRMLS_CC);
 588 
 589                                         for (i = 0; i < n; i++) {
 590                                                 if (table[i]) {
 591                                                         pz = table[i];
 592                                                         zval_scan(pz TSRMLS_CC);
 593                         }
 594                                         }
 595                                         if (!props) {
 596                                                 return;
 597                                         }
 598                                         p = props->pListHead;
 599                                         while (p != NULL) {
 600                                                 zval_scan(*(zval**)p->pData TSRMLS_CC);
 601                                                 p = p->pListNext;
 602                                         }
 603                                 }
 604                         }
 605                 }
 606         }
 607 }
 608 
 609 static void gc_scan_roots(TSRMLS_D)
 610 {
 611         gc_root_buffer *current = GC_G(roots).next;
 612 
 613         while (current != &GC_G(roots)) {
 614                 if (current->handle) {
 615                         zval z;
 616 
 617                         INIT_PZVAL(&z);
 618                         Z_OBJ_HANDLE(z) = current->handle;
 619                         Z_OBJ_HT(z) = current->u.handlers;
 620                         zobj_scan(&z TSRMLS_CC);
 621                 } else {
 622                         zval_scan(current->u.pz TSRMLS_CC);
 623                 }
 624                 current = current->next;
 625         }
 626 }
 627 
 628 static void zval_collect_white(zval *pz TSRMLS_DC)
 629 {
 630         Bucket *p;
 631 
 632 tail_call:
 633         if (((zval_gc_info*)(pz))->u.buffered == (gc_root_buffer*)GC_WHITE) {
 634                 p = NULL;
 635                 GC_ZVAL_SET_BLACK(pz);
 636 
 637                 if (Z_TYPE_P(pz) == IS_OBJECT && EG(objects_store).object_buckets) {
 638                         zend_object_get_gc_t get_gc;
 639                         struct _store_object *obj = &EG(objects_store).object_buckets[Z_OBJ_HANDLE_P(pz)].bucket.obj;
 640 
 641                         if (obj->buffered == (gc_root_buffer*)GC_WHITE) {
 642                                 /* PURPLE instead of BLACK to prevent buffering in nested gc calls */
 643                                 GC_SET_PURPLE(obj->buffered);
 644 
 645                                 if (EXPECTED(EG(objects_store).object_buckets[Z_OBJ_HANDLE_P(pz)].valid &&
 646                                              (get_gc = Z_OBJ_HANDLER_P(pz, get_gc)) != NULL)) {
 647                                         int i, n;
 648                                         zval **table, *zv;
 649                                         HashTable *props = get_gc(pz, &table, &n TSRMLS_CC);
 650 
 651                                         if (!props) {
 652                                                 /* restore refcount and put into list to free */
 653                                                 pz->refcount__gc++;
 654                                                 ((zval_gc_info*)pz)->u.next = GC_G(zval_to_free);
 655                                                 GC_G(zval_to_free) = (zval_gc_info*)pz;
 656                                         }
 657 
 658                                         while (n > 0 && !table[n-1]) n--;
 659                                         for (i = 0; i < n; i++) {
 660                                                 if (table[i]) {
 661                                                         zv = table[i];
 662                                                         if (Z_TYPE_P(zv) != IS_ARRAY || Z_ARRVAL_P(zv) != &EG(symbol_table)) {
 663                                                                 zv->refcount__gc++;
 664                                                         }
 665                                                         if (!props && i == n - 1) {
 666                                                                 pz = zv;
 667                                                                 goto tail_call;
 668                                                         } else {
 669                                                                 zval_collect_white(zv TSRMLS_CC);
 670                                                         }
 671                                                 }
 672                                         }
 673                                         if (!props) {
 674                                                 return;
 675                                         }
 676                                         p = props->pListHead;
 677                                 }
 678                         }
 679                 } else {
 680                         if (Z_TYPE_P(pz) == IS_ARRAY) {
 681                                 p = Z_ARRVAL_P(pz)->pListHead;
 682                         }
 683                 }
 684 
 685                 /* restore refcount and put into list to free */
 686                 pz->refcount__gc++;
 687                 ((zval_gc_info*)pz)->u.next = GC_G(zval_to_free);
 688                 GC_G(zval_to_free) = (zval_gc_info*)pz;
 689 
 690                 while (p != NULL) {
 691                         pz = *(zval**)p->pData;
 692                         if (Z_TYPE_P(pz) != IS_ARRAY || Z_ARRVAL_P(pz) != &EG(symbol_table)) {
 693                                 pz->refcount__gc++;
 694                         }
 695                         if (p->pListNext == NULL) {
 696                                 goto tail_call;
 697                         } else {
 698                                 zval_collect_white(pz TSRMLS_CC);
 699                         }
 700                         p = p->pListNext;
 701                 }
 702         }
 703 }
 704 
 705 static void zobj_collect_white(zval *pz TSRMLS_DC)
 706 {
 707         Bucket *p;
 708 
 709         if (EG(objects_store).object_buckets) {
 710                 zend_object_get_gc_t get_gc;
 711                 struct _store_object *obj = &EG(objects_store).object_buckets[Z_OBJ_HANDLE_P(pz)].bucket.obj;
 712 
 713                 if (obj->buffered == (gc_root_buffer*)GC_WHITE) {
 714                         /* PURPLE instead of BLACK to prevent buffering in nested gc calls */
 715                         GC_SET_PURPLE(obj->buffered);
 716 
 717                         if (EXPECTED(EG(objects_store).object_buckets[Z_OBJ_HANDLE_P(pz)].valid &&
 718                                      (get_gc = Z_OBJ_HANDLER_P(pz, get_gc)) != NULL)) {
 719                                 int i, n;
 720                                 zval **table;
 721                                 HashTable *props = get_gc(pz, &table, &n TSRMLS_CC);
 722 
 723                                 for (i = 0; i < n; i++) {
 724                                         if (table[i]) {
 725                                                 pz = table[i];
 726                                                 if (Z_TYPE_P(pz) != IS_ARRAY || Z_ARRVAL_P(pz) != &EG(symbol_table)) {
 727                                                         pz->refcount__gc++;
 728                                                 }
 729                                                 zval_collect_white(pz TSRMLS_CC);
 730                                         }
 731                                 }
 732                                 if (!props) {
 733                                         return;
 734                                 }
 735                                 p = props->pListHead;
 736                                 while (p != NULL) {
 737                                         pz = *(zval**)p->pData;
 738                                         if (Z_TYPE_P(pz) != IS_ARRAY || Z_ARRVAL_P(pz) != &EG(symbol_table)) {
 739                                                 pz->refcount__gc++;
 740                                         }
 741                                         zval_collect_white(pz TSRMLS_CC);
 742                                         p = p->pListNext;
 743                                 }
 744                         }
 745                 }
 746         }
 747 }
 748 
 749 static void gc_collect_roots(TSRMLS_D)
 750 {
 751         gc_root_buffer *current = GC_G(roots).next;
 752 
 753         while (current != &GC_G(roots)) {
 754                 if (current->handle) {
 755                         if (EG(objects_store).object_buckets) {
 756                                 struct _store_object *obj = &EG(objects_store).object_buckets[current->handle].bucket.obj;
 757                                 zval z;
 758 
 759                                 GC_SET_ADDRESS(obj->buffered, NULL);
 760                                 INIT_PZVAL(&z);
 761                                 Z_OBJ_HANDLE(z) = current->handle;
 762                                 Z_OBJ_HT(z) = current->u.handlers;
 763                                 zobj_collect_white(&z TSRMLS_CC);
 764                         }
 765                 } else {
 766                         GC_ZVAL_SET_ADDRESS(current->u.pz, NULL);
 767                         zval_collect_white(current->u.pz TSRMLS_CC);
 768                 }
 769 
 770                 GC_REMOVE_FROM_BUFFER(current);
 771                 current = current->next;
 772         }
 773 }
 774 
 775 #define FREE_LIST_END ((zval_gc_info*)(~(zend_uintptr_t)GC_COLOR))
 776 
 777 ZEND_API int gc_collect_cycles(TSRMLS_D)
 778 {
 779         int count = 0;
 780 
 781         if (GC_G(roots).next != &GC_G(roots)) {
 782                 zval_gc_info *p, *q, *orig_free_list, *orig_next_to_free;
 783 
 784                 if (GC_G(gc_active)) {
 785                         return 0;
 786                 }
 787                 GC_G(gc_runs)++;
 788                 GC_G(zval_to_free) = FREE_LIST_END;
 789                 GC_G(gc_active) = 1;
 790                 gc_mark_roots(TSRMLS_C);
 791                 gc_scan_roots(TSRMLS_C);
 792                 gc_collect_roots(TSRMLS_C);
 793 
 794                 orig_free_list = GC_G(free_list);
 795                 orig_next_to_free = GC_G(next_to_free);
 796                 p = GC_G(free_list) = GC_G(zval_to_free);
 797                 GC_G(zval_to_free) = NULL;
 798                 GC_G(gc_active) = 0;
 799 
 800                 /* First call destructors */
 801                 while (p != FREE_LIST_END) {
 802                         if (Z_TYPE(p->z) == IS_OBJECT) {
 803                                 if (EG(objects_store).object_buckets &&
 804                                         EG(objects_store).object_buckets[Z_OBJ_HANDLE(p->z)].valid &&
 805                                         EG(objects_store).object_buckets[Z_OBJ_HANDLE(p->z)].bucket.obj.refcount <= 0 &&
 806                                         EG(objects_store).object_buckets[Z_OBJ_HANDLE(p->z)].bucket.obj.dtor &&
 807                                         !EG(objects_store).object_buckets[Z_OBJ_HANDLE(p->z)].destructor_called) {
 808 
 809                                         EG(objects_store).object_buckets[Z_OBJ_HANDLE(p->z)].destructor_called = 1;
 810                                         EG(objects_store).object_buckets[Z_OBJ_HANDLE(p->z)].bucket.obj.refcount++;
 811                                         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);
 812                                         EG(objects_store).object_buckets[Z_OBJ_HANDLE(p->z)].bucket.obj.refcount--;
 813                                 }
 814                         }
 815                         count++;
 816                         p = p->u.next;
 817                 }
 818 
 819                 /* Destroy zvals */
 820                 p = GC_G(free_list);
 821                 while (p != FREE_LIST_END) {
 822                         GC_G(next_to_free) = p->u.next;
 823                         if (Z_TYPE(p->z) == IS_OBJECT) {
 824                                 if (EG(objects_store).object_buckets &&
 825                                         EG(objects_store).object_buckets[Z_OBJ_HANDLE(p->z)].valid &&
 826                                         EG(objects_store).object_buckets[Z_OBJ_HANDLE(p->z)].bucket.obj.refcount <= 0) {
 827                                         EG(objects_store).object_buckets[Z_OBJ_HANDLE(p->z)].bucket.obj.refcount = 1;
 828                                         Z_TYPE(p->z) = IS_NULL;
 829                                         zend_objects_store_del_ref_by_handle_ex(Z_OBJ_HANDLE(p->z), Z_OBJ_HT(p->z) TSRMLS_CC);
 830                                 }
 831                         } else if (Z_TYPE(p->z) == IS_ARRAY) {
 832                                 Z_TYPE(p->z) = IS_NULL;
 833                                 zend_hash_destroy(Z_ARRVAL(p->z));
 834                                 FREE_HASHTABLE(Z_ARRVAL(p->z));
 835                         } else {
 836                                 zval_dtor(&p->z);
 837                                 Z_TYPE(p->z) = IS_NULL;
 838                         }
 839                         p = GC_G(next_to_free);
 840                 }
 841 
 842                 /* Free zvals */
 843                 p = GC_G(free_list);
 844                 while (p != FREE_LIST_END) {
 845                         q = p->u.next;
 846                         FREE_ZVAL_EX(&p->z);
 847                         p = q;
 848                 }
 849                 GC_G(collected) += count;
 850                 GC_G(free_list) = orig_free_list;
 851                 GC_G(next_to_free) = orig_next_to_free;
 852         }
 853 
 854         return count;
 855 }
 856 
 857 /*
 858  * Local variables:
 859  * tab-width: 4
 860  * c-basic-offset: 4
 861  * indent-tabs-mode: t
 862  * End:
 863  */

/* [<][>][^][v][top][bottom][index][help] */