1 : /*
2 : +----------------------------------------------------------------------+
3 : | Zend Engine |
4 : +----------------------------------------------------------------------+
5 : | Copyright (c) 1998-2009 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: zend_gc.c 278220 2009-04-03 18:52:21Z dmitry $ */
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 17665 : {
35 17665 : if (gc_globals->buf) {
36 17664 : free(gc_globals->buf);
37 17664 : gc_globals->buf = NULL;
38 : }
39 17665 : }
40 :
41 : static void gc_globals_ctor_ex(zend_gc_globals *gc_globals TSRMLS_DC)
42 17633 : {
43 17633 : gc_globals->gc_enabled = 0;
44 17633 : gc_globals->gc_active = 0;
45 :
46 17633 : gc_globals->buf = NULL;
47 :
48 17633 : gc_globals->roots.next = &gc_globals->roots;
49 17633 : gc_globals->roots.prev = &gc_globals->roots;
50 17633 : gc_globals->unused = NULL;
51 17633 : gc_globals->zval_to_free = NULL;
52 17633 : gc_globals->free_list = NULL;
53 17633 : gc_globals->next_to_free = NULL;
54 :
55 17633 : gc_globals->gc_runs = 0;
56 17633 : 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 17633 : }
71 :
72 : ZEND_API void gc_globals_ctor(TSRMLS_D)
73 17633 : {
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 17633 : gc_globals_ctor_ex(&gc_globals);
78 : #endif
79 17633 : }
80 :
81 : ZEND_API void gc_globals_dtor(TSRMLS_D)
82 17665 : {
83 : #ifndef ZTS
84 17665 : root_buffer_dtor(&gc_globals TSRMLS_DC);
85 : #endif
86 17665 : }
87 :
88 : ZEND_API void gc_reset(TSRMLS_D)
89 35251 : {
90 35251 : GC_G(gc_runs) = 0;
91 35251 : 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 35251 : GC_G(roots).next = &GC_G(roots);
107 35251 : GC_G(roots).prev = &GC_G(roots);
108 :
109 35251 : if (GC_G(buf)) {
110 35249 : GC_G(unused) = NULL;
111 35249 : GC_G(first_unused) = GC_G(buf);
112 :
113 35249 : GC_G(zval_to_free) = NULL;
114 : } else {
115 2 : GC_G(unused) = NULL;
116 2 : GC_G(first_unused) = NULL;
117 2 : GC_G(last_unused) = NULL;
118 : }
119 35251 : }
120 :
121 : ZEND_API void gc_init(TSRMLS_D)
122 17638 : {
123 17638 : if (GC_G(buf) == NULL && GC_G(gc_enabled)) {
124 17632 : GC_G(buf) = (gc_root_buffer*) malloc(sizeof(gc_root_buffer) * GC_ROOT_BUFFER_MAX_ENTRIES);
125 17632 : GC_G(last_unused) = &GC_G(buf)[GC_ROOT_BUFFER_MAX_ENTRIES];
126 17632 : gc_reset(TSRMLS_C);
127 : }
128 17638 : }
129 :
130 : ZEND_API void gc_zval_possible_root(zval *zv TSRMLS_DC)
131 8640643 : {
132 8640643 : 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 10026 : return;
140 : }
141 :
142 8630617 : if (zv->type == IS_OBJECT) {
143 1239614 : GC_ZOBJ_CHECK_POSSIBLE_ROOT(zv);
144 1239614 : return;
145 : }
146 :
147 : GC_BENCH_INC(zval_possible_root);
148 :
149 7391003 : if (GC_ZVAL_GET_COLOR(zv) != GC_PURPLE) {
150 1886512 : GC_ZVAL_SET_PURPLE(zv);
151 :
152 1886512 : if (!GC_ZVAL_ADDRESS(zv)) {
153 1886512 : gc_root_buffer *newRoot = GC_G(unused);
154 :
155 1886512 : if (newRoot) {
156 1728459 : GC_G(unused) = newRoot->prev;
157 158053 : } else if (GC_G(first_unused) != GC_G(last_unused)) {
158 158021 : newRoot = GC_G(first_unused);
159 158021 : GC_G(first_unused)++;
160 : } else {
161 32 : if (!GC_G(gc_enabled)) {
162 19 : GC_ZVAL_SET_BLACK(zv);
163 19 : return;
164 : }
165 13 : zv->refcount__gc++;
166 13 : gc_collect_cycles(TSRMLS_C);
167 13 : zv->refcount__gc--;
168 13 : newRoot = GC_G(unused);
169 13 : if (!newRoot) {
170 0 : return;
171 : }
172 13 : GC_ZVAL_SET_PURPLE(zv);
173 13 : GC_G(unused) = newRoot->prev;
174 : }
175 :
176 1886493 : newRoot->next = GC_G(roots).next;
177 1886493 : newRoot->prev = &GC_G(roots);
178 1886493 : GC_G(roots).next->prev = newRoot;
179 1886493 : GC_G(roots).next = newRoot;
180 :
181 1886493 : GC_ZVAL_SET_ADDRESS(zv, newRoot);
182 :
183 1886493 : newRoot->handle = 0;
184 1886493 : 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 :
193 : ZEND_API void gc_zobj_possible_root(zval *zv TSRMLS_DC)
194 1448019 : {
195 : struct _store_object *obj;
196 :
197 1448019 : if (UNEXPECTED(Z_OBJ_HT_P(zv)->get_properties == NULL ||
198 : EG(objects_store).object_buckets == NULL)) {
199 3945 : return;
200 : }
201 :
202 : GC_BENCH_INC(zobj_possible_root);
203 :
204 1444074 : obj = &EG(objects_store).object_buckets[Z_OBJ_HANDLE_P(zv)].bucket.obj;
205 1444074 : if (GC_GET_COLOR(obj->buffered) != GC_PURPLE) {
206 614687 : GC_SET_PURPLE(obj->buffered);
207 614687 : if (!GC_ADDRESS(obj->buffered)) {
208 614687 : gc_root_buffer *newRoot = GC_G(unused);
209 :
210 614687 : if (newRoot) {
211 594783 : GC_G(unused) = newRoot->prev;
212 19904 : } else if (GC_G(first_unused) != GC_G(last_unused)) {
213 19904 : newRoot = GC_G(first_unused);
214 19904 : GC_G(first_unused)++;
215 : } else {
216 0 : if (!GC_G(gc_enabled)) {
217 0 : GC_ZVAL_SET_BLACK(zv);
218 0 : return;
219 : }
220 0 : zv->refcount__gc++;
221 0 : gc_collect_cycles(TSRMLS_C);
222 0 : zv->refcount__gc--;
223 0 : newRoot = GC_G(unused);
224 0 : if (!newRoot) {
225 0 : return;
226 : }
227 0 : obj = &EG(objects_store).object_buckets[Z_OBJ_HANDLE_P(zv)].bucket.obj;
228 0 : GC_SET_PURPLE(obj->buffered);
229 0 : GC_G(unused) = newRoot->prev;
230 : }
231 :
232 614687 : newRoot->next = GC_G(roots).next;
233 614687 : newRoot->prev = &GC_G(roots);
234 614687 : GC_G(roots).next->prev = newRoot;
235 614687 : GC_G(roots).next = newRoot;
236 :
237 614687 : GC_SET_ADDRESS(obj->buffered, newRoot);
238 :
239 614687 : newRoot->handle = Z_OBJ_HANDLE_P(zv);
240 614687 : 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 :
249 : ZEND_API void gc_remove_zval_from_buffer(zval *zv TSRMLS_DC)
250 1744399 : {
251 1744399 : gc_root_buffer* root_buffer = GC_ADDRESS(((zval_gc_info*)zv)->u.buffered);
252 :
253 1744399 : 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 0 : if (GC_G(next_to_free) == (zval_gc_info*)zv) {
260 0 : GC_G(next_to_free) = ((zval_gc_info*)zv)->u.next;
261 : }
262 0 : return;
263 : }
264 : GC_BENCH_INC(zval_remove_from_buffer);
265 1744399 : GC_REMOVE_FROM_BUFFER(root_buffer);
266 1744399 : ((zval_gc_info*)zv)->u.buffered = NULL;
267 : }
268 :
269 : static void zval_scan_black(zval *pz TSRMLS_DC)
270 252422 : {
271 : Bucket *p;
272 :
273 252422 : tail_call:
274 252422 : p = NULL;
275 252422 : GC_ZVAL_SET_BLACK(pz);
276 :
277 266430 : if (Z_TYPE_P(pz) == IS_OBJECT && EG(objects_store).object_buckets) {
278 14008 : struct _store_object *obj = &EG(objects_store).object_buckets[Z_OBJ_HANDLE_P(pz)].bucket.obj;
279 :
280 14008 : obj->refcount++;
281 14008 : if (GC_GET_COLOR(obj->buffered) != GC_BLACK) {
282 14006 : GC_SET_BLACK(obj->buffered);
283 14006 : if (EXPECTED(EG(objects_store).object_buckets[Z_OBJ_HANDLE_P(pz)].valid &&
284 : Z_OBJ_HANDLER_P(pz, get_properties) != NULL)) {
285 14006 : p = Z_OBJPROP_P(pz)->pListHead;
286 : }
287 : }
288 238414 : } else if (Z_TYPE_P(pz) == IS_ARRAY) {
289 123063 : if (Z_ARRVAL_P(pz) != &EG(symbol_table)) {
290 123063 : p = Z_ARRVAL_P(pz)->pListHead;
291 : }
292 : }
293 539140 : while (p != NULL) {
294 150358 : pz = *(zval**)p->pData;
295 150358 : if (Z_TYPE_P(pz) != IS_ARRAY || Z_ARRVAL_P(pz) != &EG(symbol_table)) {
296 150358 : pz->refcount__gc++;
297 : }
298 150358 : if (GC_ZVAL_GET_COLOR(pz) != GC_BLACK) {
299 129354 : if (p->pListNext == NULL) {
300 116062 : goto tail_call;
301 : } else {
302 13292 : zval_scan_black(pz TSRMLS_CC);
303 : }
304 : }
305 34296 : p = p->pListNext;
306 : }
307 136360 : }
308 :
309 : static void zobj_scan_black(struct _store_object *obj, zval *pz TSRMLS_DC)
310 2004 : {
311 : Bucket *p;
312 :
313 2004 : GC_SET_BLACK(obj->buffered);
314 2004 : if (EXPECTED(EG(objects_store).object_buckets[Z_OBJ_HANDLE_P(pz)].valid &&
315 : Z_OBJ_HANDLER_P(pz, get_properties) != NULL)) {
316 2004 : p = Z_OBJPROP_P(pz)->pListHead;
317 6018 : while (p != NULL) {
318 2010 : pz = *(zval**)p->pData;
319 2010 : if (Z_TYPE_P(pz) != IS_ARRAY || Z_ARRVAL_P(pz) != &EG(symbol_table)) {
320 2010 : pz->refcount__gc++;
321 : }
322 2010 : if (GC_ZVAL_GET_COLOR(pz) != GC_BLACK) {
323 2010 : zval_scan_black(pz TSRMLS_CC);
324 : }
325 2010 : p = p->pListNext;
326 : }
327 : }
328 2004 : }
329 :
330 : static void zval_mark_grey(zval *pz TSRMLS_DC)
331 335591 : {
332 : Bucket *p;
333 :
334 335591 : tail_call:
335 335591 : if (GC_ZVAL_GET_COLOR(pz) != GC_GREY) {
336 283544 : p = NULL;
337 : GC_BENCH_INC(zval_marked_grey);
338 283544 : GC_ZVAL_SET_COLOR(pz, GC_GREY);
339 :
340 307573 : if (Z_TYPE_P(pz) == IS_OBJECT && EG(objects_store).object_buckets) {
341 24029 : struct _store_object *obj = &EG(objects_store).object_buckets[Z_OBJ_HANDLE_P(pz)].bucket.obj;
342 :
343 24029 : obj->refcount--;
344 24029 : if (GC_GET_COLOR(obj->buffered) != GC_GREY) {
345 : GC_BENCH_INC(zobj_marked_grey);
346 14015 : GC_SET_COLOR(obj->buffered, GC_GREY);
347 14015 : if (EXPECTED(EG(objects_store).object_buckets[Z_OBJ_HANDLE_P(pz)].valid &&
348 : Z_OBJ_HANDLER_P(pz, get_properties) != NULL)) {
349 14015 : p = Z_OBJPROP_P(pz)->pListHead;
350 : }
351 : }
352 259515 : } else if (Z_TYPE_P(pz) == IS_ARRAY) {
353 144112 : if (Z_ARRVAL_P(pz) == &EG(symbol_table)) {
354 2 : GC_ZVAL_SET_BLACK(pz);
355 : } else {
356 144110 : p = Z_ARRVAL_P(pz)->pListHead;
357 : }
358 : }
359 590441 : while (p != NULL) {
360 181471 : pz = *(zval**)p->pData;
361 181471 : if (Z_TYPE_P(pz) != IS_ARRAY || Z_ARRVAL_P(pz) != &EG(symbol_table)) {
362 181470 : pz->refcount__gc--;
363 : }
364 181471 : if (p->pListNext == NULL) {
365 158118 : goto tail_call;
366 : } else {
367 23353 : zval_mark_grey(pz TSRMLS_CC);
368 : }
369 23353 : p = p->pListNext;
370 : }
371 : }
372 177473 : }
373 :
374 : static void zobj_mark_grey(struct _store_object *obj, zval *pz TSRMLS_DC)
375 2016 : {
376 : Bucket *p;
377 :
378 2016 : if (GC_GET_COLOR(obj->buffered) != GC_GREY) {
379 : GC_BENCH_INC(zobj_marked_grey);
380 2016 : GC_SET_COLOR(obj->buffered, GC_GREY);
381 2016 : if (EXPECTED(EG(objects_store).object_buckets[Z_OBJ_HANDLE_P(pz)].valid &&
382 : Z_OBJ_HANDLER_P(pz, get_properties) != NULL)) {
383 2016 : p = Z_OBJPROP_P(pz)->pListHead;
384 16058 : while (p != NULL) {
385 12026 : pz = *(zval**)p->pData;
386 12026 : if (Z_TYPE_P(pz) != IS_ARRAY || Z_ARRVAL_P(pz) != &EG(symbol_table)) {
387 12026 : pz->refcount__gc--;
388 : }
389 12026 : zval_mark_grey(pz TSRMLS_CC);
390 12026 : p = p->pListNext;
391 : }
392 : }
393 : }
394 2016 : }
395 :
396 : static void gc_mark_roots(TSRMLS_D)
397 67 : {
398 67 : gc_root_buffer *current = GC_G(roots).next;
399 :
400 144266 : while (current != &GC_G(roots)) {
401 146161 : if (current->handle && EG(objects_store).object_buckets) {
402 2029 : struct _store_object *obj = &EG(objects_store).object_buckets[current->handle].bucket.obj;
403 :
404 2029 : if (GC_GET_COLOR(obj->buffered) == GC_PURPLE) {
405 : zval z;
406 :
407 2016 : INIT_PZVAL(&z);
408 2016 : Z_OBJ_HANDLE(z) = current->handle;
409 2016 : Z_OBJ_HT(z) = current->u.handlers;
410 2016 : zobj_mark_grey(obj, &z TSRMLS_CC);
411 : } else {
412 13 : GC_SET_ADDRESS(obj->buffered, NULL);
413 13 : GC_REMOVE_FROM_BUFFER(current);
414 : }
415 : } else {
416 142103 : if (GC_ZVAL_GET_COLOR(current->u.pz) == GC_PURPLE) {
417 142094 : zval_mark_grey(current->u.pz TSRMLS_CC);
418 : } else {
419 9 : GC_ZVAL_SET_ADDRESS(current->u.pz, NULL);
420 9 : GC_REMOVE_FROM_BUFFER(current);
421 : }
422 : }
423 144132 : current = current->next;
424 : }
425 67 : }
426 :
427 : static int zval_scan(zval *pz TSRMLS_DC)
428 185228 : {
429 : Bucket *p;
430 :
431 185228 : tail_call:
432 185228 : if (GC_ZVAL_GET_COLOR(pz) == GC_GREY) {
433 154183 : p = NULL;
434 154183 : if (pz->refcount__gc > 0) {
435 121058 : zval_scan_black(pz TSRMLS_CC);
436 : } else {
437 33125 : GC_ZVAL_SET_COLOR(pz, GC_WHITE);
438 43149 : if (Z_TYPE_P(pz) == IS_OBJECT && EG(objects_store).object_buckets) {
439 10024 : struct _store_object *obj = &EG(objects_store).object_buckets[Z_OBJ_HANDLE_P(pz)].bucket.obj;
440 :
441 10024 : if (GC_GET_COLOR(obj->buffered) == GC_GREY) {
442 11 : if (obj->refcount > 0) {
443 1 : zobj_scan_black(obj, pz TSRMLS_CC);
444 : } else {
445 10 : GC_SET_COLOR(obj->buffered, GC_WHITE);
446 10 : if (EXPECTED(EG(objects_store).object_buckets[Z_OBJ_HANDLE_P(pz)].valid &&
447 : Z_OBJ_HANDLER_P(pz, get_properties) != NULL)) {
448 10 : p = Z_OBJPROP_P(pz)->pListHead;
449 : }
450 : }
451 : }
452 23101 : } else if (Z_TYPE_P(pz) == IS_ARRAY) {
453 21049 : if (Z_ARRVAL_P(pz) == &EG(symbol_table)) {
454 0 : GC_ZVAL_SET_BLACK(pz);
455 : } else {
456 21049 : p = Z_ARRVAL_P(pz)->pListHead;
457 : }
458 : }
459 : }
460 318428 : while (p != NULL) {
461 31117 : if (p->pListNext == NULL) {
462 21055 : pz = *(zval**)p->pData;
463 21055 : goto tail_call;
464 : } else {
465 10062 : zval_scan(*(zval**)p->pData TSRMLS_CC);
466 : }
467 10062 : p = p->pListNext;
468 : }
469 : }
470 164173 : return 0;
471 : }
472 :
473 : static void zobj_scan(zval *pz TSRMLS_DC)
474 4016 : {
475 : Bucket *p;
476 :
477 4016 : if (EG(objects_store).object_buckets) {
478 4016 : struct _store_object *obj = &EG(objects_store).object_buckets[Z_OBJ_HANDLE_P(pz)].bucket.obj;
479 :
480 4016 : if (GC_GET_COLOR(obj->buffered) == GC_GREY) {
481 4016 : if (obj->refcount > 0) {
482 2003 : zobj_scan_black(obj, pz TSRMLS_CC);
483 : } else {
484 2013 : GC_SET_COLOR(obj->buffered, GC_WHITE);
485 2013 : if (EXPECTED(EG(objects_store).object_buckets[Z_OBJ_HANDLE_P(pz)].valid &&
486 : Z_OBJ_HANDLER_P(pz, get_properties) != NULL)) {
487 2013 : p = Z_OBJPROP_P(pz)->pListHead;
488 16043 : while (p != NULL) {
489 12017 : zval_scan(*(zval**)p->pData TSRMLS_CC);
490 12017 : p = p->pListNext;
491 : }
492 : }
493 : }
494 : }
495 : }
496 4016 : }
497 :
498 : static void gc_scan_roots(TSRMLS_D)
499 67 : {
500 67 : gc_root_buffer *current = GC_G(roots).next;
501 :
502 146244 : while (current != &GC_G(roots)) {
503 146110 : if (current->handle) {
504 : zval z;
505 :
506 4016 : INIT_PZVAL(&z);
507 4016 : Z_OBJ_HANDLE(z) = current->handle;
508 4016 : Z_OBJ_HT(z) = current->u.handlers;
509 4016 : zobj_scan(&z TSRMLS_CC);
510 : } else {
511 142094 : zval_scan(current->u.pz TSRMLS_CC);
512 : }
513 146110 : current = current->next;
514 : }
515 67 : }
516 :
517 : static void zval_collect_white(zval *pz TSRMLS_DC)
518 183223 : {
519 : Bucket *p;
520 :
521 183223 : tail_call:
522 183223 : if (((zval_gc_info*)(pz))->u.buffered == (gc_root_buffer*)GC_WHITE) {
523 31120 : p = NULL;
524 31120 : GC_ZVAL_SET_BLACK(pz);
525 :
526 41141 : if (Z_TYPE_P(pz) == IS_OBJECT && EG(objects_store).object_buckets) {
527 10021 : struct _store_object *obj = &EG(objects_store).object_buckets[Z_OBJ_HANDLE_P(pz)].bucket.obj;
528 :
529 10021 : if (obj->buffered == (gc_root_buffer*)GC_WHITE) {
530 9 : GC_SET_BLACK(obj->buffered);
531 :
532 9 : if (EXPECTED(EG(objects_store).object_buckets[Z_OBJ_HANDLE_P(pz)].valid &&
533 : Z_OBJ_HANDLER_P(pz, get_properties) != NULL)) {
534 9 : p = Z_OBJPROP_P(pz)->pListHead;
535 : }
536 : }
537 : } else {
538 21099 : if (Z_TYPE_P(pz) == IS_ARRAY) {
539 21047 : p = Z_ARRVAL_P(pz)->pListHead;
540 : }
541 : }
542 :
543 : /* restore refcount and put into list to free */
544 31120 : pz->refcount__gc++;
545 31120 : ((zval_gc_info*)pz)->u.next = GC_G(zval_to_free);
546 31120 : GC_G(zval_to_free) = (zval_gc_info*)pz;
547 :
548 72301 : while (p != NULL) {
549 31113 : pz = *(zval**)p->pData;
550 31113 : if (Z_TYPE_P(pz) != IS_ARRAY || Z_ARRVAL_P(pz) != &EG(symbol_table)) {
551 31112 : pz->refcount__gc++;
552 : }
553 31113 : if (p->pListNext == NULL) {
554 21052 : goto tail_call;
555 : } else {
556 10061 : zval_collect_white(pz TSRMLS_CC);
557 : }
558 10061 : p = p->pListNext;
559 : }
560 : }
561 162171 : }
562 :
563 : static void zobj_collect_white(zval *pz TSRMLS_DC)
564 4016 : {
565 : Bucket *p;
566 :
567 4016 : if (EG(objects_store).object_buckets) {
568 4016 : struct _store_object *obj = &EG(objects_store).object_buckets[Z_OBJ_HANDLE_P(pz)].bucket.obj;
569 :
570 4016 : if (obj->buffered == (gc_root_buffer*)GC_WHITE) {
571 12 : GC_SET_BLACK(obj->buffered);
572 :
573 12 : if (EXPECTED(EG(objects_store).object_buckets[Z_OBJ_HANDLE_P(pz)].valid &&
574 : Z_OBJ_HANDLER_P(pz, get_properties) != NULL)) {
575 12 : p = Z_OBJPROP_P(pz)->pListHead;
576 10040 : while (p != NULL) {
577 10016 : pz = *(zval**)p->pData;
578 10016 : if (Z_TYPE_P(pz) != IS_ARRAY || Z_ARRVAL_P(pz) != &EG(symbol_table)) {
579 10016 : pz->refcount__gc++;
580 : }
581 10016 : zval_collect_white(pz TSRMLS_CC);
582 10016 : p = p->pListNext;
583 : }
584 : }
585 : }
586 : }
587 4016 : }
588 :
589 : static void gc_collect_roots(TSRMLS_D)
590 67 : {
591 67 : gc_root_buffer *current = GC_G(roots).next;
592 :
593 146244 : while (current != &GC_G(roots)) {
594 150126 : if (current->handle && EG(objects_store).object_buckets) {
595 4016 : struct _store_object *obj = &EG(objects_store).object_buckets[current->handle].bucket.obj;
596 : zval z;
597 :
598 4016 : GC_SET_ADDRESS(obj->buffered, NULL);
599 4016 : INIT_PZVAL(&z);
600 4016 : Z_OBJ_HANDLE(z) = current->handle;
601 4016 : Z_OBJ_HT(z) = current->u.handlers;
602 4016 : zobj_collect_white(&z TSRMLS_CC);
603 : } else {
604 142094 : GC_ZVAL_SET_ADDRESS(current->u.pz, NULL);
605 142094 : zval_collect_white(current->u.pz TSRMLS_CC);
606 : }
607 :
608 146110 : GC_REMOVE_FROM_BUFFER(current);
609 146110 : current = current->next;
610 : }
611 67 : }
612 :
613 : #define FREE_LIST_END ((zval_gc_info*)(~(zend_uintptr_t)GC_COLOR))
614 :
615 : ZEND_API int gc_collect_cycles(TSRMLS_D)
616 16347 : {
617 16347 : int count = 0;
618 :
619 16347 : if (GC_G(roots).next != &GC_G(roots)) {
620 : zval_gc_info *p, *q, *orig_free_list, *orig_next_to_free;
621 :
622 67 : if (GC_G(gc_active)) {
623 0 : return 0;
624 : }
625 67 : GC_G(gc_runs)++;
626 67 : GC_G(zval_to_free) = FREE_LIST_END;
627 67 : GC_G(gc_active) = 1;
628 67 : gc_mark_roots(TSRMLS_C);
629 67 : gc_scan_roots(TSRMLS_C);
630 67 : gc_collect_roots(TSRMLS_C);
631 :
632 67 : orig_free_list = GC_G(free_list);
633 67 : orig_next_to_free = GC_G(next_to_free);
634 67 : p = GC_G(free_list) = GC_G(zval_to_free);
635 67 : GC_G(zval_to_free) = NULL;
636 67 : GC_G(gc_active) = 0;
637 :
638 : /* First call destructors */
639 31254 : while (p != FREE_LIST_END) {
640 31120 : if (Z_TYPE(p->z) == IS_OBJECT) {
641 10021 : if (EG(objects_store).object_buckets &&
642 : EG(objects_store).object_buckets[Z_OBJ_HANDLE(p->z)].valid &&
643 : EG(objects_store).object_buckets[Z_OBJ_HANDLE(p->z)].bucket.obj.refcount <= 0 &&
644 : EG(objects_store).object_buckets[Z_OBJ_HANDLE(p->z)].bucket.obj.dtor &&
645 : !EG(objects_store).object_buckets[Z_OBJ_HANDLE(p->z)].destructor_called) {
646 :
647 21 : EG(objects_store).object_buckets[Z_OBJ_HANDLE(p->z)].destructor_called = 1;
648 21 : EG(objects_store).object_buckets[Z_OBJ_HANDLE(p->z)].bucket.obj.refcount++;
649 21 : 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);
650 21 : EG(objects_store).object_buckets[Z_OBJ_HANDLE(p->z)].bucket.obj.refcount--;
651 : }
652 : }
653 31120 : count++;
654 31120 : p = p->u.next;
655 : }
656 :
657 : /* Destroy zvals */
658 67 : p = GC_G(free_list);
659 31254 : while (p != FREE_LIST_END) {
660 31120 : GC_G(next_to_free) = p->u.next;
661 31120 : if (Z_TYPE(p->z) == IS_OBJECT) {
662 10021 : if (EG(objects_store).object_buckets &&
663 : EG(objects_store).object_buckets[Z_OBJ_HANDLE(p->z)].valid &&
664 : EG(objects_store).object_buckets[Z_OBJ_HANDLE(p->z)].bucket.obj.refcount <= 0) {
665 21 : EG(objects_store).object_buckets[Z_OBJ_HANDLE(p->z)].bucket.obj.refcount = 1;
666 21 : Z_TYPE(p->z) = IS_NULL;
667 21 : zend_objects_store_del_ref_by_handle_ex(Z_OBJ_HANDLE(p->z), Z_OBJ_HT(p->z) TSRMLS_CC);
668 : }
669 21099 : } else if (Z_TYPE(p->z) == IS_ARRAY) {
670 21047 : Z_TYPE(p->z) = IS_NULL;
671 21047 : zend_hash_destroy(Z_ARRVAL(p->z));
672 21047 : FREE_HASHTABLE(Z_ARRVAL(p->z));
673 : } else {
674 52 : zval_dtor(&p->z);
675 52 : Z_TYPE(p->z) = IS_NULL;
676 : }
677 31120 : p = GC_G(next_to_free);
678 : }
679 :
680 : /* Free zvals */
681 67 : p = GC_G(free_list);
682 31254 : while (p != FREE_LIST_END) {
683 31120 : q = p->u.next;
684 31120 : FREE_ZVAL_EX(&p->z);
685 31120 : p = q;
686 : }
687 67 : GC_G(collected) += count;
688 67 : GC_G(free_list) = orig_free_list;
689 67 : GC_G(next_to_free) = orig_next_to_free;
690 : }
691 :
692 16347 : return count;
693 : }
694 :
695 : /*
696 : * Local variables:
697 : * tab-width: 4
698 : * c-basic-offset: 4
699 : * indent-tabs-mode: t
700 : * End:
701 : */
|