1 /*
2 +----------------------------------------------------------------------+
3 | Zend Engine |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 1998-2013 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
root_buffer_dtor(zend_gc_globals * gc_globals TSRMLS_DC)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
gc_globals_ctor_ex(zend_gc_globals * gc_globals TSRMLS_DC)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
gc_globals_ctor(TSRMLS_D)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
gc_globals_dtor(TSRMLS_D)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
gc_reset(TSRMLS_D)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
gc_init(TSRMLS_D)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
gc_zval_possible_root(zval * zv TSRMLS_DC)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 GC_ZVAL_SET_PURPLE(zv);
151
152 if (!GC_ZVAL_ADDRESS(zv)) {
153 gc_root_buffer *newRoot = GC_G(unused);
154
155 if (newRoot) {
156 GC_G(unused) = newRoot->prev;
157 } else if (GC_G(first_unused) != GC_G(last_unused)) {
158 newRoot = GC_G(first_unused);
159 GC_G(first_unused)++;
160 } else {
161 if (!GC_G(gc_enabled)) {
162 GC_ZVAL_SET_BLACK(zv);
163 return;
164 }
165 zv->refcount__gc++;
166 gc_collect_cycles(TSRMLS_C);
167 zv->refcount__gc--;
168 newRoot = GC_G(unused);
169 if (!newRoot) {
170 return;
171 }
172 GC_ZVAL_SET_PURPLE(zv);
173 GC_G(unused) = newRoot->prev;
174 }
175
176 newRoot->next = GC_G(roots).next;
177 newRoot->prev = &GC_G(roots);
178 GC_G(roots).next->prev = newRoot;
179 GC_G(roots).next = newRoot;
180
181 GC_ZVAL_SET_ADDRESS(zv, newRoot);
182
183 newRoot->handle = 0;
184 newRoot->u.pz = zv;
185
186 GC_BENCH_INC(zval_buffered);
187 GC_BENCH_INC(root_buf_length);
188 GC_BENCH_PEAK(root_buf_peak, root_buf_length);
189 }
190 }
191 }
192
gc_zobj_possible_root(zval * zv TSRMLS_DC)193 ZEND_API void gc_zobj_possible_root(zval *zv TSRMLS_DC)
194 {
195 struct _store_object *obj;
196
197 if (UNEXPECTED(Z_OBJ_HT_P(zv)->get_properties == NULL ||
198 EG(objects_store).object_buckets == NULL)) {
199 return;
200 }
201
202 GC_BENCH_INC(zobj_possible_root);
203
204 obj = &EG(objects_store).object_buckets[Z_OBJ_HANDLE_P(zv)].bucket.obj;
205 if (GC_GET_COLOR(obj->buffered) != GC_PURPLE) {
206 GC_SET_PURPLE(obj->buffered);
207 if (!GC_ADDRESS(obj->buffered)) {
208 gc_root_buffer *newRoot = GC_G(unused);
209
210 if (newRoot) {
211 GC_G(unused) = newRoot->prev;
212 } else if (GC_G(first_unused) != GC_G(last_unused)) {
213 newRoot = GC_G(first_unused);
214 GC_G(first_unused)++;
215 } else {
216 if (!GC_G(gc_enabled)) {
217 GC_ZVAL_SET_BLACK(zv);
218 return;
219 }
220 zv->refcount__gc++;
221 gc_collect_cycles(TSRMLS_C);
222 zv->refcount__gc--;
223 newRoot = GC_G(unused);
224 if (!newRoot) {
225 return;
226 }
227 obj = &EG(objects_store).object_buckets[Z_OBJ_HANDLE_P(zv)].bucket.obj;
228 GC_SET_PURPLE(obj->buffered);
229 GC_G(unused) = newRoot->prev;
230 }
231
232 newRoot->next = GC_G(roots).next;
233 newRoot->prev = &GC_G(roots);
234 GC_G(roots).next->prev = newRoot;
235 GC_G(roots).next = newRoot;
236
237 GC_SET_ADDRESS(obj->buffered, newRoot);
238
239 newRoot->handle = Z_OBJ_HANDLE_P(zv);
240 newRoot->u.handlers = Z_OBJ_HT_P(zv);
241
242 GC_BENCH_INC(zobj_buffered);
243 GC_BENCH_INC(root_buf_length);
244 GC_BENCH_PEAK(root_buf_peak, root_buf_length);
245 }
246 }
247 }
248
gc_remove_zval_from_buffer(zval * zv TSRMLS_DC)249 ZEND_API void gc_remove_zval_from_buffer(zval *zv TSRMLS_DC)
250 {
251 gc_root_buffer* root_buffer = GC_ADDRESS(((zval_gc_info*)zv)->u.buffered);
252
253 if (UNEXPECTED(GC_G(free_list) != NULL &&
254 GC_ZVAL_GET_COLOR(zv) == GC_BLACK) &&
255 (GC_ZVAL_ADDRESS(zv) < GC_G(buf) ||
256 GC_ZVAL_ADDRESS(zv) >= GC_G(last_unused))) {
257 /* The given zval is a garbage that is going to be deleted by
258 * currently running GC */
259 if (GC_G(next_to_free) == (zval_gc_info*)zv) {
260 GC_G(next_to_free) = ((zval_gc_info*)zv)->u.next;
261 }
262 return;
263 }
264 GC_BENCH_INC(zval_remove_from_buffer);
265 GC_REMOVE_FROM_BUFFER(root_buffer);
266 ((zval_gc_info*)zv)->u.buffered = NULL;
267 }
268
zval_scan_black(zval * pz TSRMLS_DC)269 static void zval_scan_black(zval *pz TSRMLS_DC)
270 {
271 Bucket *p;
272
273 tail_call:
274 p = NULL;
275 GC_ZVAL_SET_BLACK(pz);
276
277 if (Z_TYPE_P(pz) == IS_OBJECT && EG(objects_store).object_buckets) {
278 struct _store_object *obj = &EG(objects_store).object_buckets[Z_OBJ_HANDLE_P(pz)].bucket.obj;
279
280 obj->refcount++;
281 if (GC_GET_COLOR(obj->buffered) != GC_BLACK) {
282 GC_SET_BLACK(obj->buffered);
283 if (EXPECTED(EG(objects_store).object_buckets[Z_OBJ_HANDLE_P(pz)].valid &&
284 Z_OBJ_HANDLER_P(pz, get_properties) != NULL)) {
285 HashTable *props = Z_OBJPROP_P(pz);
286 if(!props) {
287 return;
288 }
289 p = props->pListHead;
290 }
291 }
292 } else if (Z_TYPE_P(pz) == IS_ARRAY) {
293 if (Z_ARRVAL_P(pz) != &EG(symbol_table)) {
294 p = Z_ARRVAL_P(pz)->pListHead;
295 }
296 }
297 while (p != NULL) {
298 pz = *(zval**)p->pData;
299 if (Z_TYPE_P(pz) != IS_ARRAY || Z_ARRVAL_P(pz) != &EG(symbol_table)) {
300 pz->refcount__gc++;
301 }
302 if (GC_ZVAL_GET_COLOR(pz) != GC_BLACK) {
303 if (p->pListNext == NULL) {
304 goto tail_call;
305 } else {
306 zval_scan_black(pz TSRMLS_CC);
307 }
308 }
309 p = p->pListNext;
310 }
311 }
312
zobj_scan_black(struct _store_object * obj,zval * pz TSRMLS_DC)313 static void zobj_scan_black(struct _store_object *obj, zval *pz TSRMLS_DC)
314 {
315 Bucket *p;
316
317 GC_SET_BLACK(obj->buffered);
318 if (EXPECTED(EG(objects_store).object_buckets[Z_OBJ_HANDLE_P(pz)].valid &&
319 Z_OBJ_HANDLER_P(pz, get_properties) != NULL)) {
320 HashTable *props = Z_OBJPROP_P(pz);
321 if(!props) {
322 return;
323 }
324 p = props->pListHead;
325 while (p != NULL) {
326 pz = *(zval**)p->pData;
327 if (Z_TYPE_P(pz) != IS_ARRAY || Z_ARRVAL_P(pz) != &EG(symbol_table)) {
328 pz->refcount__gc++;
329 }
330 if (GC_ZVAL_GET_COLOR(pz) != GC_BLACK) {
331 zval_scan_black(pz TSRMLS_CC);
332 }
333 p = p->pListNext;
334 }
335 }
336 }
337
zval_mark_grey(zval * pz TSRMLS_DC)338 static void zval_mark_grey(zval *pz TSRMLS_DC)
339 {
340 Bucket *p;
341
342 tail_call:
343 if (GC_ZVAL_GET_COLOR(pz) != GC_GREY) {
344 p = NULL;
345 GC_BENCH_INC(zval_marked_grey);
346 GC_ZVAL_SET_COLOR(pz, GC_GREY);
347
348 if (Z_TYPE_P(pz) == IS_OBJECT && EG(objects_store).object_buckets) {
349 struct _store_object *obj = &EG(objects_store).object_buckets[Z_OBJ_HANDLE_P(pz)].bucket.obj;
350
351 obj->refcount--;
352 if (GC_GET_COLOR(obj->buffered) != GC_GREY) {
353 GC_BENCH_INC(zobj_marked_grey);
354 GC_SET_COLOR(obj->buffered, GC_GREY);
355 if (EXPECTED(EG(objects_store).object_buckets[Z_OBJ_HANDLE_P(pz)].valid &&
356 Z_OBJ_HANDLER_P(pz, get_properties) != NULL)) {
357 HashTable *props = Z_OBJPROP_P(pz);
358 if(!props) {
359 return;
360 }
361 p = props->pListHead;
362 }
363 }
364 } else if (Z_TYPE_P(pz) == IS_ARRAY) {
365 if (Z_ARRVAL_P(pz) == &EG(symbol_table)) {
366 GC_ZVAL_SET_BLACK(pz);
367 } else {
368 p = Z_ARRVAL_P(pz)->pListHead;
369 }
370 }
371 while (p != NULL) {
372 pz = *(zval**)p->pData;
373 if (Z_TYPE_P(pz) != IS_ARRAY || Z_ARRVAL_P(pz) != &EG(symbol_table)) {
374 pz->refcount__gc--;
375 }
376 if (p->pListNext == NULL) {
377 goto tail_call;
378 } else {
379 zval_mark_grey(pz TSRMLS_CC);
380 }
381 p = p->pListNext;
382 }
383 }
384 }
385
zobj_mark_grey(struct _store_object * obj,zval * pz TSRMLS_DC)386 static void zobj_mark_grey(struct _store_object *obj, zval *pz TSRMLS_DC)
387 {
388 Bucket *p;
389
390 if (GC_GET_COLOR(obj->buffered) != GC_GREY) {
391 GC_BENCH_INC(zobj_marked_grey);
392 GC_SET_COLOR(obj->buffered, GC_GREY);
393 if (EXPECTED(EG(objects_store).object_buckets[Z_OBJ_HANDLE_P(pz)].valid &&
394 Z_OBJ_HANDLER_P(pz, get_properties) != NULL)) {
395 HashTable *props = Z_OBJPROP_P(pz);
396 if(!props) {
397 return;
398 }
399 p = props->pListHead;
400 while (p != NULL) {
401 pz = *(zval**)p->pData;
402 if (Z_TYPE_P(pz) != IS_ARRAY || Z_ARRVAL_P(pz) != &EG(symbol_table)) {
403 pz->refcount__gc--;
404 }
405 zval_mark_grey(pz TSRMLS_CC);
406 p = p->pListNext;
407 }
408 }
409 }
410 }
411
gc_mark_roots(TSRMLS_D)412 static void gc_mark_roots(TSRMLS_D)
413 {
414 gc_root_buffer *current = GC_G(roots).next;
415
416 while (current != &GC_G(roots)) {
417 if (current->handle) {
418 if (EG(objects_store).object_buckets) {
419 struct _store_object *obj = &EG(objects_store).object_buckets[current->handle].bucket.obj;
420
421 if (GC_GET_COLOR(obj->buffered) == GC_PURPLE) {
422 zval z;
423
424 INIT_PZVAL(&z);
425 Z_OBJ_HANDLE(z) = current->handle;
426 Z_OBJ_HT(z) = current->u.handlers;
427 zobj_mark_grey(obj, &z TSRMLS_CC);
428 } else {
429 GC_SET_ADDRESS(obj->buffered, NULL);
430 GC_REMOVE_FROM_BUFFER(current);
431 }
432 }
433 } else {
434 if (GC_ZVAL_GET_COLOR(current->u.pz) == GC_PURPLE) {
435 zval_mark_grey(current->u.pz TSRMLS_CC);
436 } else {
437 GC_ZVAL_SET_ADDRESS(current->u.pz, NULL);
438 GC_REMOVE_FROM_BUFFER(current);
439 }
440 }
441 current = current->next;
442 }
443 }
444
zval_scan(zval * pz TSRMLS_DC)445 static int zval_scan(zval *pz TSRMLS_DC)
446 {
447 Bucket *p;
448
449 tail_call:
450 if (GC_ZVAL_GET_COLOR(pz) == GC_GREY) {
451 p = NULL;
452 if (pz->refcount__gc > 0) {
453 zval_scan_black(pz TSRMLS_CC);
454 } else {
455 GC_ZVAL_SET_COLOR(pz, GC_WHITE);
456 if (Z_TYPE_P(pz) == IS_OBJECT && EG(objects_store).object_buckets) {
457 struct _store_object *obj = &EG(objects_store).object_buckets[Z_OBJ_HANDLE_P(pz)].bucket.obj;
458
459 if (GC_GET_COLOR(obj->buffered) == GC_GREY) {
460 if (obj->refcount > 0) {
461 zobj_scan_black(obj, pz TSRMLS_CC);
462 } else {
463 GC_SET_COLOR(obj->buffered, GC_WHITE);
464 if (EXPECTED(EG(objects_store).object_buckets[Z_OBJ_HANDLE_P(pz)].valid &&
465 Z_OBJ_HANDLER_P(pz, get_properties) != NULL)) {
466 HashTable *props = Z_OBJPROP_P(pz);
467 if(!props) {
468 return 0;
469 }
470 p = props->pListHead;
471 }
472 }
473 }
474 } else if (Z_TYPE_P(pz) == IS_ARRAY) {
475 if (Z_ARRVAL_P(pz) == &EG(symbol_table)) {
476 GC_ZVAL_SET_BLACK(pz);
477 } else {
478 p = Z_ARRVAL_P(pz)->pListHead;
479 }
480 }
481 }
482 while (p != NULL) {
483 if (p->pListNext == NULL) {
484 pz = *(zval**)p->pData;
485 goto tail_call;
486 } else {
487 zval_scan(*(zval**)p->pData TSRMLS_CC);
488 }
489 p = p->pListNext;
490 }
491 }
492 return 0;
493 }
494
zobj_scan(zval * pz TSRMLS_DC)495 static void zobj_scan(zval *pz TSRMLS_DC)
496 {
497 Bucket *p;
498
499 if (EG(objects_store).object_buckets) {
500 struct _store_object *obj = &EG(objects_store).object_buckets[Z_OBJ_HANDLE_P(pz)].bucket.obj;
501
502 if (GC_GET_COLOR(obj->buffered) == GC_GREY) {
503 if (obj->refcount > 0) {
504 zobj_scan_black(obj, pz TSRMLS_CC);
505 } else {
506 GC_SET_COLOR(obj->buffered, GC_WHITE);
507 if (EXPECTED(EG(objects_store).object_buckets[Z_OBJ_HANDLE_P(pz)].valid &&
508 Z_OBJ_HANDLER_P(pz, get_properties) != NULL)) {
509 HashTable *props = Z_OBJPROP_P(pz);
510 if(!props) {
511 return;
512 }
513 p = props->pListHead;
514 while (p != NULL) {
515 zval_scan(*(zval**)p->pData TSRMLS_CC);
516 p = p->pListNext;
517 }
518 }
519 }
520 }
521 }
522 }
523
gc_scan_roots(TSRMLS_D)524 static void gc_scan_roots(TSRMLS_D)
525 {
526 gc_root_buffer *current = GC_G(roots).next;
527
528 while (current != &GC_G(roots)) {
529 if (current->handle) {
530 zval z;
531
532 INIT_PZVAL(&z);
533 Z_OBJ_HANDLE(z) = current->handle;
534 Z_OBJ_HT(z) = current->u.handlers;
535 zobj_scan(&z TSRMLS_CC);
536 } else {
537 zval_scan(current->u.pz TSRMLS_CC);
538 }
539 current = current->next;
540 }
541 }
542
zval_collect_white(zval * pz TSRMLS_DC)543 static void zval_collect_white(zval *pz TSRMLS_DC)
544 {
545 Bucket *p;
546
547 tail_call:
548 if (((zval_gc_info*)(pz))->u.buffered == (gc_root_buffer*)GC_WHITE) {
549 p = NULL;
550 GC_ZVAL_SET_BLACK(pz);
551
552 if (Z_TYPE_P(pz) == IS_OBJECT && EG(objects_store).object_buckets) {
553 struct _store_object *obj = &EG(objects_store).object_buckets[Z_OBJ_HANDLE_P(pz)].bucket.obj;
554
555 if (obj->buffered == (gc_root_buffer*)GC_WHITE) {
556 /* PURPLE instead of BLACK to prevent buffering in nested gc calls */
557 GC_SET_PURPLE(obj->buffered);
558
559 if (EXPECTED(EG(objects_store).object_buckets[Z_OBJ_HANDLE_P(pz)].valid &&
560 Z_OBJ_HANDLER_P(pz, get_properties) != NULL)) {
561 HashTable *props = Z_OBJPROP_P(pz);
562 if(!props) {
563 return;
564 }
565 p = props->pListHead;
566 }
567 }
568 } else {
569 if (Z_TYPE_P(pz) == IS_ARRAY) {
570 p = Z_ARRVAL_P(pz)->pListHead;
571 }
572 }
573
574 /* restore refcount and put into list to free */
575 pz->refcount__gc++;
576 ((zval_gc_info*)pz)->u.next = GC_G(zval_to_free);
577 GC_G(zval_to_free) = (zval_gc_info*)pz;
578
579 while (p != NULL) {
580 pz = *(zval**)p->pData;
581 if (Z_TYPE_P(pz) != IS_ARRAY || Z_ARRVAL_P(pz) != &EG(symbol_table)) {
582 pz->refcount__gc++;
583 }
584 if (p->pListNext == NULL) {
585 goto tail_call;
586 } else {
587 zval_collect_white(pz TSRMLS_CC);
588 }
589 p = p->pListNext;
590 }
591 }
592 }
593
zobj_collect_white(zval * pz TSRMLS_DC)594 static void zobj_collect_white(zval *pz TSRMLS_DC)
595 {
596 Bucket *p;
597
598 if (EG(objects_store).object_buckets) {
599 struct _store_object *obj = &EG(objects_store).object_buckets[Z_OBJ_HANDLE_P(pz)].bucket.obj;
600
601 if (obj->buffered == (gc_root_buffer*)GC_WHITE) {
602 /* PURPLE instead of BLACK to prevent buffering in nested gc calls */
603 GC_SET_PURPLE(obj->buffered);
604
605 if (EXPECTED(EG(objects_store).object_buckets[Z_OBJ_HANDLE_P(pz)].valid &&
606 Z_OBJ_HANDLER_P(pz, get_properties) != NULL)) {
607 HashTable *props = Z_OBJPROP_P(pz);
608 if(!props) {
609 return;
610 }
611 p = props->pListHead;
612 while (p != NULL) {
613 pz = *(zval**)p->pData;
614 if (Z_TYPE_P(pz) != IS_ARRAY || Z_ARRVAL_P(pz) != &EG(symbol_table)) {
615 pz->refcount__gc++;
616 }
617 zval_collect_white(pz TSRMLS_CC);
618 p = p->pListNext;
619 }
620 }
621 }
622 }
623 }
624
gc_collect_roots(TSRMLS_D)625 static void gc_collect_roots(TSRMLS_D)
626 {
627 gc_root_buffer *current = GC_G(roots).next;
628
629 while (current != &GC_G(roots)) {
630 if (current->handle) {
631 if (EG(objects_store).object_buckets) {
632 struct _store_object *obj = &EG(objects_store).object_buckets[current->handle].bucket.obj;
633 zval z;
634
635 GC_SET_ADDRESS(obj->buffered, NULL);
636 INIT_PZVAL(&z);
637 Z_OBJ_HANDLE(z) = current->handle;
638 Z_OBJ_HT(z) = current->u.handlers;
639 zobj_collect_white(&z TSRMLS_CC);
640 }
641 } else {
642 GC_ZVAL_SET_ADDRESS(current->u.pz, NULL);
643 zval_collect_white(current->u.pz TSRMLS_CC);
644 }
645
646 GC_REMOVE_FROM_BUFFER(current);
647 current = current->next;
648 }
649 }
650
651 #define FREE_LIST_END ((zval_gc_info*)(~(zend_uintptr_t)GC_COLOR))
652
gc_collect_cycles(TSRMLS_D)653 ZEND_API int gc_collect_cycles(TSRMLS_D)
654 {
655 int count = 0;
656
657 if (GC_G(roots).next != &GC_G(roots)) {
658 zval_gc_info *p, *q, *orig_free_list, *orig_next_to_free;
659
660 if (GC_G(gc_active)) {
661 return 0;
662 }
663 GC_G(gc_runs)++;
664 GC_G(zval_to_free) = FREE_LIST_END;
665 GC_G(gc_active) = 1;
666 gc_mark_roots(TSRMLS_C);
667 gc_scan_roots(TSRMLS_C);
668 gc_collect_roots(TSRMLS_C);
669
670 orig_free_list = GC_G(free_list);
671 orig_next_to_free = GC_G(next_to_free);
672 p = GC_G(free_list) = GC_G(zval_to_free);
673 GC_G(zval_to_free) = NULL;
674 GC_G(gc_active) = 0;
675
676 /* First call destructors */
677 while (p != FREE_LIST_END) {
678 if (Z_TYPE(p->z) == IS_OBJECT) {
679 if (EG(objects_store).object_buckets &&
680 EG(objects_store).object_buckets[Z_OBJ_HANDLE(p->z)].valid &&
681 EG(objects_store).object_buckets[Z_OBJ_HANDLE(p->z)].bucket.obj.refcount <= 0 &&
682 EG(objects_store).object_buckets[Z_OBJ_HANDLE(p->z)].bucket.obj.dtor &&
683 !EG(objects_store).object_buckets[Z_OBJ_HANDLE(p->z)].destructor_called) {
684
685 EG(objects_store).object_buckets[Z_OBJ_HANDLE(p->z)].destructor_called = 1;
686 EG(objects_store).object_buckets[Z_OBJ_HANDLE(p->z)].bucket.obj.refcount++;
687 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);
688 EG(objects_store).object_buckets[Z_OBJ_HANDLE(p->z)].bucket.obj.refcount--;
689 }
690 }
691 count++;
692 p = p->u.next;
693 }
694
695 /* Destroy zvals */
696 p = GC_G(free_list);
697 while (p != FREE_LIST_END) {
698 GC_G(next_to_free) = p->u.next;
699 if (Z_TYPE(p->z) == IS_OBJECT) {
700 if (EG(objects_store).object_buckets &&
701 EG(objects_store).object_buckets[Z_OBJ_HANDLE(p->z)].valid &&
702 EG(objects_store).object_buckets[Z_OBJ_HANDLE(p->z)].bucket.obj.refcount <= 0) {
703 EG(objects_store).object_buckets[Z_OBJ_HANDLE(p->z)].bucket.obj.refcount = 1;
704 Z_TYPE(p->z) = IS_NULL;
705 zend_objects_store_del_ref_by_handle_ex(Z_OBJ_HANDLE(p->z), Z_OBJ_HT(p->z) TSRMLS_CC);
706 }
707 } else if (Z_TYPE(p->z) == IS_ARRAY) {
708 Z_TYPE(p->z) = IS_NULL;
709 zend_hash_destroy(Z_ARRVAL(p->z));
710 FREE_HASHTABLE(Z_ARRVAL(p->z));
711 } else {
712 zval_dtor(&p->z);
713 Z_TYPE(p->z) = IS_NULL;
714 }
715 p = GC_G(next_to_free);
716 }
717
718 /* Free zvals */
719 p = GC_G(free_list);
720 while (p != FREE_LIST_END) {
721 q = p->u.next;
722 FREE_ZVAL_EX(&p->z);
723 p = q;
724 }
725 GC_G(collected) += count;
726 GC_G(free_list) = orig_free_list;
727 GC_G(next_to_free) = orig_next_to_free;
728 }
729
730 return count;
731 }
732
733 /*
734 * Local variables:
735 * tab-width: 4
736 * c-basic-offset: 4
737 * indent-tabs-mode: t
738 * End:
739 */
740