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 278221 2009-04-03 18:52:36Z 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 17039 : {
35 17039 : if (gc_globals->buf) {
36 17038 : free(gc_globals->buf);
37 17038 : gc_globals->buf = NULL;
38 : }
39 17039 : }
40 :
41 : static void gc_globals_ctor_ex(zend_gc_globals *gc_globals TSRMLS_DC)
42 17007 : {
43 17007 : gc_globals->gc_enabled = 0;
44 17007 : gc_globals->gc_active = 0;
45 :
46 17007 : gc_globals->buf = NULL;
47 :
48 17007 : gc_globals->roots.next = &gc_globals->roots;
49 17007 : gc_globals->roots.prev = &gc_globals->roots;
50 17007 : gc_globals->unused = NULL;
51 17007 : gc_globals->zval_to_free = NULL;
52 17007 : gc_globals->free_list = NULL;
53 17007 : gc_globals->next_to_free = NULL;
54 :
55 17007 : gc_globals->gc_runs = 0;
56 17007 : 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 17007 : }
71 :
72 : ZEND_API void gc_globals_ctor(TSRMLS_D)
73 17007 : {
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 17007 : gc_globals_ctor_ex(&gc_globals);
78 : #endif
79 17007 : }
80 :
81 : ZEND_API void gc_globals_dtor(TSRMLS_D)
82 17039 : {
83 : #ifndef ZTS
84 17039 : root_buffer_dtor(&gc_globals TSRMLS_DC);
85 : #endif
86 17039 : }
87 :
88 : ZEND_API void gc_reset(TSRMLS_D)
89 33999 : {
90 33999 : GC_G(gc_runs) = 0;
91 33999 : 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 33999 : GC_G(roots).next = &GC_G(roots);
107 33999 : GC_G(roots).prev = &GC_G(roots);
108 :
109 33999 : if (GC_G(buf)) {
110 33997 : GC_G(unused) = NULL;
111 33997 : GC_G(first_unused) = GC_G(buf);
112 :
113 33997 : 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 33999 : }
120 :
121 : ZEND_API void gc_init(TSRMLS_D)
122 17012 : {
123 17012 : if (GC_G(buf) == NULL && GC_G(gc_enabled)) {
124 17006 : GC_G(buf) = (gc_root_buffer*) malloc(sizeof(gc_root_buffer) * GC_ROOT_BUFFER_MAX_ENTRIES);
125 17006 : GC_G(last_unused) = &GC_G(buf)[GC_ROOT_BUFFER_MAX_ENTRIES];
126 17006 : gc_reset(TSRMLS_C);
127 : }
128 17012 : }
129 :
130 : ZEND_API void gc_zval_possible_root(zval *zv TSRMLS_DC)
131 42988744 : {
132 42988744 : 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 42978718 : if (zv->type == IS_OBJECT) {
143 1227396 : GC_ZOBJ_CHECK_POSSIBLE_ROOT(zv);
144 1227396 : return;
145 : }
146 :
147 : GC_BENCH_INC(zval_possible_root);
148 :
149 41751322 : if (GC_ZVAL_GET_COLOR(zv) != GC_PURPLE) {
150 1955644 : GC_ZVAL_SET_PURPLE(zv);
151 :
152 1955644 : if (!GC_ZVAL_ADDRESS(zv)) {
153 1955644 : gc_root_buffer *newRoot = GC_G(unused);
154 :
155 1955644 : if (newRoot) {
156 1817349 : GC_G(unused) = newRoot->prev;
157 138295 : } else if (GC_G(first_unused) != GC_G(last_unused)) {
158 138274 : newRoot = GC_G(first_unused);
159 138274 : GC_G(first_unused)++;
160 : } else {
161 21 : if (!GC_G(gc_enabled)) {
162 8 : GC_ZVAL_SET_BLACK(zv);
163 8 : 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 1955636 : newRoot->next = GC_G(roots).next;
177 1955636 : newRoot->prev = &GC_G(roots);
178 1955636 : GC_G(roots).next->prev = newRoot;
179 1955636 : GC_G(roots).next = newRoot;
180 :
181 1955636 : GC_ZVAL_SET_ADDRESS(zv, newRoot);
182 :
183 1955636 : newRoot->handle = 0;
184 1955636 : 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 1436633 : {
195 : struct _store_object *obj;
196 :
197 1436633 : if (UNEXPECTED(Z_OBJ_HT_P(zv)->get_properties == NULL ||
198 : EG(objects_store).object_buckets == NULL)) {
199 3888 : return;
200 : }
201 :
202 : GC_BENCH_INC(zobj_possible_root);
203 :
204 1432745 : obj = &EG(objects_store).object_buckets[Z_OBJ_HANDLE_P(zv)].bucket.obj;
205 1432745 : if (GC_GET_COLOR(obj->buffered) != GC_PURPLE) {
206 615668 : GC_SET_PURPLE(obj->buffered);
207 615668 : if (!GC_ADDRESS(obj->buffered)) {
208 615668 : gc_root_buffer *newRoot = GC_G(unused);
209 :
210 615668 : if (newRoot) {
211 596199 : GC_G(unused) = newRoot->prev;
212 19469 : } else if (GC_G(first_unused) != GC_G(last_unused)) {
213 19469 : newRoot = GC_G(first_unused);
214 19469 : 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 615668 : newRoot->next = GC_G(roots).next;
233 615668 : newRoot->prev = &GC_G(roots);
234 615668 : GC_G(roots).next->prev = newRoot;
235 615668 : GC_G(roots).next = newRoot;
236 :
237 615668 : GC_SET_ADDRESS(obj->buffered, newRoot);
238 :
239 615668 : newRoot->handle = Z_OBJ_HANDLE_P(zv);
240 615668 : 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 1813544 : {
251 1813544 : gc_root_buffer* root_buffer = GC_ADDRESS(((zval_gc_info*)zv)->u.buffered);
252 :
253 1813544 : 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 1813544 : GC_REMOVE_FROM_BUFFER(root_buffer);
266 1813544 : ((zval_gc_info*)zv)->u.buffered = NULL;
267 : }
268 :
269 : static void zval_scan_black(zval *pz TSRMLS_DC)
270 251071 : {
271 : Bucket *p;
272 :
273 251071 : tail_call:
274 251071 : p = NULL;
275 251071 : GC_ZVAL_SET_BLACK(pz);
276 :
277 265079 : 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 237063 : } else if (Z_TYPE_P(pz) == IS_ARRAY) {
289 123034 : if (Z_ARRVAL_P(pz) != &EG(symbol_table)) {
290 123034 : p = Z_ARRVAL_P(pz)->pListHead;
291 : }
292 : }
293 535150 : while (p != NULL) {
294 149042 : pz = *(zval**)p->pData;
295 149042 : if (Z_TYPE_P(pz) != IS_ARRAY || Z_ARRVAL_P(pz) != &EG(symbol_table)) {
296 149042 : pz->refcount__gc++;
297 : }
298 149042 : if (GC_ZVAL_GET_COLOR(pz) != GC_BLACK) {
299 128038 : if (p->pListNext == NULL) {
300 116034 : goto tail_call;
301 : } else {
302 12004 : zval_scan_black(pz TSRMLS_CC);
303 : }
304 : }
305 33008 : p = p->pListNext;
306 : }
307 135037 : }
308 :
309 : static void zobj_scan_black(struct _store_object *obj, zval *pz TSRMLS_DC)
310 2003 : {
311 : Bucket *p;
312 :
313 2003 : GC_SET_BLACK(obj->buffered);
314 2003 : if (EXPECTED(EG(objects_store).object_buckets[Z_OBJ_HANDLE_P(pz)].valid &&
315 : Z_OBJ_HANDLER_P(pz, get_properties) != NULL)) {
316 2003 : p = Z_OBJPROP_P(pz)->pListHead;
317 6009 : while (p != NULL) {
318 2003 : pz = *(zval**)p->pData;
319 2003 : if (Z_TYPE_P(pz) != IS_ARRAY || Z_ARRVAL_P(pz) != &EG(symbol_table)) {
320 2003 : pz->refcount__gc++;
321 : }
322 2003 : if (GC_ZVAL_GET_COLOR(pz) != GC_BLACK) {
323 2003 : zval_scan_black(pz TSRMLS_CC);
324 : }
325 2003 : p = p->pListNext;
326 : }
327 : }
328 2003 : }
329 :
330 : static void zval_mark_grey(zval *pz TSRMLS_DC)
331 334240 : {
332 : Bucket *p;
333 :
334 334240 : tail_call:
335 334240 : if (GC_ZVAL_GET_COLOR(pz) != GC_GREY) {
336 282193 : p = NULL;
337 : GC_BENCH_INC(zval_marked_grey);
338 282193 : GC_ZVAL_SET_COLOR(pz, GC_GREY);
339 :
340 306222 : 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 258164 : } else if (Z_TYPE_P(pz) == IS_ARRAY) {
353 144083 : if (Z_ARRVAL_P(pz) == &EG(symbol_table)) {
354 2 : GC_ZVAL_SET_BLACK(pz);
355 : } else {
356 144081 : p = Z_ARRVAL_P(pz)->pListHead;
357 : }
358 : }
359 586451 : while (p != NULL) {
360 180155 : pz = *(zval**)p->pData;
361 180155 : if (Z_TYPE_P(pz) != IS_ARRAY || Z_ARRVAL_P(pz) != &EG(symbol_table)) {
362 180154 : pz->refcount__gc--;
363 : }
364 180155 : if (p->pListNext == NULL) {
365 158090 : goto tail_call;
366 : } else {
367 22065 : zval_mark_grey(pz TSRMLS_CC);
368 : }
369 22065 : p = p->pListNext;
370 : }
371 : }
372 176150 : }
373 :
374 : static void zobj_mark_grey(struct _store_object *obj, zval *pz TSRMLS_DC)
375 2015 : {
376 : Bucket *p;
377 :
378 2015 : if (GC_GET_COLOR(obj->buffered) != GC_GREY) {
379 : GC_BENCH_INC(zobj_marked_grey);
380 2015 : GC_SET_COLOR(obj->buffered, GC_GREY);
381 2015 : if (EXPECTED(EG(objects_store).object_buckets[Z_OBJ_HANDLE_P(pz)].valid &&
382 : Z_OBJ_HANDLER_P(pz, get_properties) != NULL)) {
383 2015 : p = Z_OBJPROP_P(pz)->pListHead;
384 16049 : while (p != NULL) {
385 12019 : pz = *(zval**)p->pData;
386 12019 : if (Z_TYPE_P(pz) != IS_ARRAY || Z_ARRVAL_P(pz) != &EG(symbol_table)) {
387 12019 : pz->refcount__gc--;
388 : }
389 12019 : zval_mark_grey(pz TSRMLS_CC);
390 12019 : p = p->pListNext;
391 : }
392 : }
393 : }
394 2015 : }
395 :
396 : static void gc_mark_roots(TSRMLS_D)
397 65 : {
398 65 : gc_root_buffer *current = GC_G(roots).next;
399 :
400 144233 : while (current != &GC_G(roots)) {
401 146131 : if (current->handle && EG(objects_store).object_buckets) {
402 2028 : struct _store_object *obj = &EG(objects_store).object_buckets[current->handle].bucket.obj;
403 :
404 2028 : if (GC_GET_COLOR(obj->buffered) == GC_PURPLE) {
405 : zval z;
406 :
407 2015 : INIT_PZVAL(&z);
408 2015 : Z_OBJ_HANDLE(z) = current->handle;
409 2015 : Z_OBJ_HT(z) = current->u.handlers;
410 2015 : 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 142075 : if (GC_ZVAL_GET_COLOR(current->u.pz) == GC_PURPLE) {
417 142066 : 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 144103 : current = current->next;
424 : }
425 65 : }
426 :
427 : static int zval_scan(zval *pz TSRMLS_DC)
428 185200 : {
429 : Bucket *p;
430 :
431 185200 : tail_call:
432 185200 : if (GC_ZVAL_GET_COLOR(pz) == GC_GREY) {
433 154155 : p = NULL;
434 154155 : if (pz->refcount__gc > 0) {
435 121030 : 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 318372 : 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 164145 : return 0;
471 : }
472 :
473 : static void zobj_scan(zval *pz TSRMLS_DC)
474 4015 : {
475 : Bucket *p;
476 :
477 4015 : if (EG(objects_store).object_buckets) {
478 4015 : struct _store_object *obj = &EG(objects_store).object_buckets[Z_OBJ_HANDLE_P(pz)].bucket.obj;
479 :
480 4015 : if (GC_GET_COLOR(obj->buffered) == GC_GREY) {
481 4015 : if (obj->refcount > 0) {
482 2002 : 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 4015 : }
497 :
498 : static void gc_scan_roots(TSRMLS_D)
499 65 : {
500 65 : gc_root_buffer *current = GC_G(roots).next;
501 :
502 146211 : while (current != &GC_G(roots)) {
503 146081 : if (current->handle) {
504 : zval z;
505 :
506 4015 : INIT_PZVAL(&z);
507 4015 : Z_OBJ_HANDLE(z) = current->handle;
508 4015 : Z_OBJ_HT(z) = current->u.handlers;
509 4015 : zobj_scan(&z TSRMLS_CC);
510 : } else {
511 142066 : zval_scan(current->u.pz TSRMLS_CC);
512 : }
513 146081 : current = current->next;
514 : }
515 65 : }
516 :
517 : static void zval_collect_white(zval *pz TSRMLS_DC)
518 183195 : {
519 : Bucket *p;
520 :
521 183195 : tail_call:
522 183195 : 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 162143 : }
562 :
563 : static void zobj_collect_white(zval *pz TSRMLS_DC)
564 4015 : {
565 : Bucket *p;
566 :
567 4015 : if (EG(objects_store).object_buckets) {
568 4015 : struct _store_object *obj = &EG(objects_store).object_buckets[Z_OBJ_HANDLE_P(pz)].bucket.obj;
569 :
570 4015 : 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 4015 : }
588 :
589 : static void gc_collect_roots(TSRMLS_D)
590 65 : {
591 65 : gc_root_buffer *current = GC_G(roots).next;
592 :
593 146211 : while (current != &GC_G(roots)) {
594 150096 : if (current->handle && EG(objects_store).object_buckets) {
595 4015 : struct _store_object *obj = &EG(objects_store).object_buckets[current->handle].bucket.obj;
596 : zval z;
597 :
598 4015 : GC_SET_ADDRESS(obj->buffered, NULL);
599 4015 : INIT_PZVAL(&z);
600 4015 : Z_OBJ_HANDLE(z) = current->handle;
601 4015 : Z_OBJ_HT(z) = current->u.handlers;
602 4015 : zobj_collect_white(&z TSRMLS_CC);
603 : } else {
604 142066 : GC_ZVAL_SET_ADDRESS(current->u.pz, NULL);
605 142066 : zval_collect_white(current->u.pz TSRMLS_CC);
606 : }
607 :
608 146081 : GC_REMOVE_FROM_BUFFER(current);
609 146081 : current = current->next;
610 : }
611 65 : }
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 15595 : {
617 15595 : int count = 0;
618 :
619 15595 : if (GC_G(roots).next != &GC_G(roots)) {
620 : zval_gc_info *p, *q, *orig_free_list, *orig_next_to_free;
621 :
622 65 : if (GC_G(gc_active)) {
623 0 : return 0;
624 : }
625 65 : GC_G(gc_runs)++;
626 65 : GC_G(zval_to_free) = FREE_LIST_END;
627 65 : GC_G(gc_active) = 1;
628 65 : gc_mark_roots(TSRMLS_C);
629 65 : gc_scan_roots(TSRMLS_C);
630 65 : gc_collect_roots(TSRMLS_C);
631 :
632 65 : orig_free_list = GC_G(free_list);
633 65 : orig_next_to_free = GC_G(next_to_free);
634 65 : p = GC_G(free_list) = GC_G(zval_to_free);
635 65 : GC_G(zval_to_free) = NULL;
636 65 : GC_G(gc_active) = 0;
637 :
638 : /* First call destructors */
639 31250 : 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 65 : p = GC_G(free_list);
659 31250 : 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 65 : p = GC_G(free_list);
682 31250 : while (p != FREE_LIST_END) {
683 31120 : q = p->u.next;
684 31120 : FREE_ZVAL_EX(&p->z);
685 31120 : p = q;
686 : }
687 65 : GC_G(collected) += count;
688 65 : GC_G(free_list) = orig_free_list;
689 65 : GC_G(next_to_free) = orig_next_to_free;
690 : }
691 :
692 15595 : 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 : */
|