xref: /PHP-5.3/Zend/zend_gc.c (revision 831fbcf3)
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