1 /*
2 +----------------------------------------------------------------------+
3 | Zend Engine |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 1998-2014 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_gc == 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 zend_object_get_gc_t get_gc;
279 struct _store_object *obj = &EG(objects_store).object_buckets[Z_OBJ_HANDLE_P(pz)].bucket.obj;
280
281 obj->refcount++;
282 if (GC_GET_COLOR(obj->buffered) != GC_BLACK) {
283 GC_SET_BLACK(obj->buffered);
284 if (EXPECTED(EG(objects_store).object_buckets[Z_OBJ_HANDLE_P(pz)].valid &&
285 (get_gc = Z_OBJ_HANDLER_P(pz, get_gc)) != NULL)) {
286 int i, n;
287 zval **table;
288 HashTable *props = get_gc(pz, &table, &n TSRMLS_CC);
289
290 while (n > 0 && !table[n-1]) n--;
291 for (i = 0; i < n; i++) {
292 if (table[i]) {
293 pz = table[i];
294 if (Z_TYPE_P(pz) != IS_ARRAY || Z_ARRVAL_P(pz) != &EG(symbol_table)) {
295 pz->refcount__gc++;
296 }
297 if (GC_ZVAL_GET_COLOR(pz) != GC_BLACK) {
298 if (!props && i == n - 1) {
299 goto tail_call;
300 } else {
301 zval_scan_black(pz TSRMLS_CC);
302 }
303 }
304 }
305 }
306 if (!props) {
307 return;
308 }
309 p = props->pListHead;
310 }
311 }
312 } else if (Z_TYPE_P(pz) == IS_ARRAY) {
313 if (Z_ARRVAL_P(pz) != &EG(symbol_table)) {
314 p = Z_ARRVAL_P(pz)->pListHead;
315 }
316 }
317 while (p != NULL) {
318 pz = *(zval**)p->pData;
319 if (Z_TYPE_P(pz) != IS_ARRAY || Z_ARRVAL_P(pz) != &EG(symbol_table)) {
320 pz->refcount__gc++;
321 }
322 if (GC_ZVAL_GET_COLOR(pz) != GC_BLACK) {
323 if (p->pListNext == NULL) {
324 goto tail_call;
325 } else {
326 zval_scan_black(pz TSRMLS_CC);
327 }
328 }
329 p = p->pListNext;
330 }
331 }
332
zobj_scan_black(struct _store_object * obj,zval * pz TSRMLS_DC)333 static void zobj_scan_black(struct _store_object *obj, zval *pz TSRMLS_DC)
334 {
335 Bucket *p;
336 zend_object_get_gc_t get_gc;
337
338 GC_SET_BLACK(obj->buffered);
339 if (EXPECTED(EG(objects_store).object_buckets[Z_OBJ_HANDLE_P(pz)].valid &&
340 (get_gc = Z_OBJ_HANDLER_P(pz, get_gc)) != NULL)) {
341 int i, n;
342 zval **table;
343 HashTable *props = get_gc(pz, &table, &n TSRMLS_CC);
344
345 for (i = 0; i < n; i++) {
346 if (table[i]) {
347 pz = table[i];
348 if (Z_TYPE_P(pz) != IS_ARRAY || Z_ARRVAL_P(pz) != &EG(symbol_table)) {
349 pz->refcount__gc++;
350 }
351 if (GC_ZVAL_GET_COLOR(pz) != GC_BLACK) {
352 zval_scan_black(pz TSRMLS_CC);
353 }
354 }
355 }
356 if (!props) {
357 return;
358 }
359 p = props->pListHead;
360 while (p != NULL) {
361 pz = *(zval**)p->pData;
362 if (Z_TYPE_P(pz) != IS_ARRAY || Z_ARRVAL_P(pz) != &EG(symbol_table)) {
363 pz->refcount__gc++;
364 }
365 if (GC_ZVAL_GET_COLOR(pz) != GC_BLACK) {
366 zval_scan_black(pz TSRMLS_CC);
367 }
368 p = p->pListNext;
369 }
370 }
371 }
372
zval_mark_grey(zval * pz TSRMLS_DC)373 static void zval_mark_grey(zval *pz TSRMLS_DC)
374 {
375 Bucket *p;
376
377 tail_call:
378 if (GC_ZVAL_GET_COLOR(pz) != GC_GREY) {
379 p = NULL;
380 GC_BENCH_INC(zval_marked_grey);
381 GC_ZVAL_SET_COLOR(pz, GC_GREY);
382
383 if (Z_TYPE_P(pz) == IS_OBJECT && EG(objects_store).object_buckets) {
384 zend_object_get_gc_t get_gc;
385 struct _store_object *obj = &EG(objects_store).object_buckets[Z_OBJ_HANDLE_P(pz)].bucket.obj;
386
387 obj->refcount--;
388 if (GC_GET_COLOR(obj->buffered) != GC_GREY) {
389 GC_BENCH_INC(zobj_marked_grey);
390 GC_SET_COLOR(obj->buffered, GC_GREY);
391 if (EXPECTED(EG(objects_store).object_buckets[Z_OBJ_HANDLE_P(pz)].valid &&
392 (get_gc = Z_OBJ_HANDLER_P(pz, get_gc)) != NULL)) {
393 int i, n;
394 zval **table;
395 HashTable *props = get_gc(pz, &table, &n TSRMLS_CC);
396
397 while (n > 0 && !table[n-1]) n--;
398 for (i = 0; i < n; i++) {
399 if (table[i]) {
400 pz = table[i];
401 if (Z_TYPE_P(pz) != IS_ARRAY || Z_ARRVAL_P(pz) != &EG(symbol_table)) {
402 pz->refcount__gc--;
403 }
404 if (!props && i == n - 1) {
405 goto tail_call;
406 } else {
407 zval_mark_grey(pz TSRMLS_CC);
408 }
409 }
410 }
411 if (!props) {
412 return;
413 }
414 p = props->pListHead;
415 }
416 }
417 } else if (Z_TYPE_P(pz) == IS_ARRAY) {
418 if (Z_ARRVAL_P(pz) == &EG(symbol_table)) {
419 GC_ZVAL_SET_BLACK(pz);
420 } else {
421 p = Z_ARRVAL_P(pz)->pListHead;
422 }
423 }
424 while (p != NULL) {
425 pz = *(zval**)p->pData;
426 if (Z_TYPE_P(pz) != IS_ARRAY || Z_ARRVAL_P(pz) != &EG(symbol_table)) {
427 pz->refcount__gc--;
428 }
429 if (p->pListNext == NULL) {
430 goto tail_call;
431 } else {
432 zval_mark_grey(pz TSRMLS_CC);
433 }
434 p = p->pListNext;
435 }
436 }
437 }
438
zobj_mark_grey(struct _store_object * obj,zval * pz TSRMLS_DC)439 static void zobj_mark_grey(struct _store_object *obj, zval *pz TSRMLS_DC)
440 {
441 Bucket *p;
442 zend_object_get_gc_t get_gc;
443
444 if (GC_GET_COLOR(obj->buffered) != GC_GREY) {
445 GC_BENCH_INC(zobj_marked_grey);
446 GC_SET_COLOR(obj->buffered, GC_GREY);
447 if (EXPECTED(EG(objects_store).object_buckets[Z_OBJ_HANDLE_P(pz)].valid &&
448 (get_gc = Z_OBJ_HANDLER_P(pz, get_gc)) != NULL)) {
449 int i, n;
450 zval **table;
451 HashTable *props = get_gc(pz, &table, &n TSRMLS_CC);
452
453 for (i = 0; i < n; i++) {
454 if (table[i]) {
455 pz = table[i];
456 if (Z_TYPE_P(pz) != IS_ARRAY || Z_ARRVAL_P(pz) != &EG(symbol_table)) {
457 pz->refcount__gc--;
458 }
459 zval_mark_grey(pz TSRMLS_CC);
460 }
461 }
462 if (!props) {
463 return;
464 }
465 p = props->pListHead;
466 while (p != NULL) {
467 pz = *(zval**)p->pData;
468 if (Z_TYPE_P(pz) != IS_ARRAY || Z_ARRVAL_P(pz) != &EG(symbol_table)) {
469 pz->refcount__gc--;
470 }
471 zval_mark_grey(pz TSRMLS_CC);
472 p = p->pListNext;
473 }
474 }
475 }
476 }
477
gc_mark_roots(TSRMLS_D)478 static void gc_mark_roots(TSRMLS_D)
479 {
480 gc_root_buffer *current = GC_G(roots).next;
481
482 while (current != &GC_G(roots)) {
483 if (current->handle) {
484 if (EG(objects_store).object_buckets) {
485 struct _store_object *obj = &EG(objects_store).object_buckets[current->handle].bucket.obj;
486
487 if (GC_GET_COLOR(obj->buffered) == GC_PURPLE) {
488 zval z;
489
490 INIT_PZVAL(&z);
491 Z_OBJ_HANDLE(z) = current->handle;
492 Z_OBJ_HT(z) = current->u.handlers;
493 zobj_mark_grey(obj, &z TSRMLS_CC);
494 } else {
495 GC_SET_ADDRESS(obj->buffered, NULL);
496 GC_REMOVE_FROM_BUFFER(current);
497 }
498 }
499 } else {
500 if (GC_ZVAL_GET_COLOR(current->u.pz) == GC_PURPLE) {
501 zval_mark_grey(current->u.pz TSRMLS_CC);
502 } else {
503 GC_ZVAL_SET_ADDRESS(current->u.pz, NULL);
504 GC_REMOVE_FROM_BUFFER(current);
505 }
506 }
507 current = current->next;
508 }
509 }
510
zval_scan(zval * pz TSRMLS_DC)511 static void zval_scan(zval *pz TSRMLS_DC)
512 {
513 Bucket *p;
514
515 tail_call:
516 if (GC_ZVAL_GET_COLOR(pz) == GC_GREY) {
517 p = NULL;
518 if (pz->refcount__gc > 0) {
519 zval_scan_black(pz TSRMLS_CC);
520 } else {
521 GC_ZVAL_SET_COLOR(pz, GC_WHITE);
522 if (Z_TYPE_P(pz) == IS_OBJECT && EG(objects_store).object_buckets) {
523 zend_object_get_gc_t get_gc;
524 struct _store_object *obj = &EG(objects_store).object_buckets[Z_OBJ_HANDLE_P(pz)].bucket.obj;
525
526 if (GC_GET_COLOR(obj->buffered) == GC_GREY) {
527 if (obj->refcount > 0) {
528 zobj_scan_black(obj, pz TSRMLS_CC);
529 } else {
530 GC_SET_COLOR(obj->buffered, GC_WHITE);
531 if (EXPECTED(EG(objects_store).object_buckets[Z_OBJ_HANDLE_P(pz)].valid &&
532 (get_gc = Z_OBJ_HANDLER_P(pz, get_gc)) != NULL)) {
533 int i, n;
534 zval **table;
535 HashTable *props = get_gc(pz, &table, &n TSRMLS_CC);
536
537 while (n > 0 && !table[n-1]) n--;
538 for (i = 0; i < n; i++) {
539 if (table[i]) {
540 pz = table[i];
541 if (!props && i == n - 1) {
542 goto tail_call;
543 } else {
544 zval_scan(pz TSRMLS_CC);
545 }
546 }
547 }
548 if (!props) {
549 return;
550 }
551 p = props->pListHead;
552 }
553 }
554 }
555 } else if (Z_TYPE_P(pz) == IS_ARRAY) {
556 if (Z_ARRVAL_P(pz) == &EG(symbol_table)) {
557 GC_ZVAL_SET_BLACK(pz);
558 } else {
559 p = Z_ARRVAL_P(pz)->pListHead;
560 }
561 }
562 }
563 while (p != NULL) {
564 if (p->pListNext == NULL) {
565 pz = *(zval**)p->pData;
566 goto tail_call;
567 } else {
568 zval_scan(*(zval**)p->pData TSRMLS_CC);
569 }
570 p = p->pListNext;
571 }
572 }
573 }
574
zobj_scan(zval * pz TSRMLS_DC)575 static void zobj_scan(zval *pz TSRMLS_DC)
576 {
577 Bucket *p;
578 zend_object_get_gc_t get_gc;
579
580 if (EG(objects_store).object_buckets) {
581 struct _store_object *obj = &EG(objects_store).object_buckets[Z_OBJ_HANDLE_P(pz)].bucket.obj;
582
583 if (GC_GET_COLOR(obj->buffered) == GC_GREY) {
584 if (obj->refcount > 0) {
585 zobj_scan_black(obj, pz TSRMLS_CC);
586 } else {
587 GC_SET_COLOR(obj->buffered, GC_WHITE);
588 if (EXPECTED(EG(objects_store).object_buckets[Z_OBJ_HANDLE_P(pz)].valid &&
589 (get_gc = Z_OBJ_HANDLER_P(pz, get_gc)) != NULL)) {
590 int i, n;
591 zval **table;
592 HashTable *props = get_gc(pz, &table, &n TSRMLS_CC);
593
594 for (i = 0; i < n; i++) {
595 if (table[i]) {
596 pz = table[i];
597 zval_scan(pz TSRMLS_CC);
598 }
599 }
600 if (!props) {
601 return;
602 }
603 p = props->pListHead;
604 while (p != NULL) {
605 zval_scan(*(zval**)p->pData TSRMLS_CC);
606 p = p->pListNext;
607 }
608 }
609 }
610 }
611 }
612 }
613
gc_scan_roots(TSRMLS_D)614 static void gc_scan_roots(TSRMLS_D)
615 {
616 gc_root_buffer *current = GC_G(roots).next;
617
618 while (current != &GC_G(roots)) {
619 if (current->handle) {
620 zval z;
621
622 INIT_PZVAL(&z);
623 Z_OBJ_HANDLE(z) = current->handle;
624 Z_OBJ_HT(z) = current->u.handlers;
625 zobj_scan(&z TSRMLS_CC);
626 } else {
627 zval_scan(current->u.pz TSRMLS_CC);
628 }
629 current = current->next;
630 }
631 }
632
zval_collect_white(zval * pz TSRMLS_DC)633 static void zval_collect_white(zval *pz TSRMLS_DC)
634 {
635 Bucket *p;
636
637 tail_call:
638 if (((zval_gc_info*)(pz))->u.buffered == (gc_root_buffer*)GC_WHITE) {
639 p = NULL;
640 GC_ZVAL_SET_BLACK(pz);
641
642 if (Z_TYPE_P(pz) == IS_OBJECT && EG(objects_store).object_buckets) {
643 zend_object_get_gc_t get_gc;
644 struct _store_object *obj = &EG(objects_store).object_buckets[Z_OBJ_HANDLE_P(pz)].bucket.obj;
645
646 if (obj->buffered == (gc_root_buffer*)GC_WHITE) {
647 /* PURPLE instead of BLACK to prevent buffering in nested gc calls */
648 GC_SET_PURPLE(obj->buffered);
649
650 if (EXPECTED(EG(objects_store).object_buckets[Z_OBJ_HANDLE_P(pz)].valid &&
651 (get_gc = Z_OBJ_HANDLER_P(pz, get_gc)) != NULL)) {
652 int i, n;
653 zval **table, *zv;
654 HashTable *props = get_gc(pz, &table, &n TSRMLS_CC);
655
656 if (!props) {
657 /* restore refcount and put into list to free */
658 pz->refcount__gc++;
659 ((zval_gc_info*)pz)->u.next = GC_G(zval_to_free);
660 GC_G(zval_to_free) = (zval_gc_info*)pz;
661 }
662
663 while (n > 0 && !table[n-1]) n--;
664 for (i = 0; i < n; i++) {
665 if (table[i]) {
666 zv = table[i];
667 if (Z_TYPE_P(zv) != IS_ARRAY || Z_ARRVAL_P(zv) != &EG(symbol_table)) {
668 zv->refcount__gc++;
669 }
670 if (!props && i == n - 1) {
671 pz = zv;
672 goto tail_call;
673 } else {
674 zval_collect_white(zv TSRMLS_CC);
675 }
676 }
677 }
678 if (!props) {
679 return;
680 }
681 p = props->pListHead;
682 }
683 }
684 } else {
685 if (Z_TYPE_P(pz) == IS_ARRAY) {
686 p = Z_ARRVAL_P(pz)->pListHead;
687 }
688 }
689
690 /* restore refcount and put into list to free */
691 pz->refcount__gc++;
692 ((zval_gc_info*)pz)->u.next = GC_G(zval_to_free);
693 GC_G(zval_to_free) = (zval_gc_info*)pz;
694
695 while (p != NULL) {
696 pz = *(zval**)p->pData;
697 if (Z_TYPE_P(pz) != IS_ARRAY || Z_ARRVAL_P(pz) != &EG(symbol_table)) {
698 pz->refcount__gc++;
699 }
700 if (p->pListNext == NULL) {
701 goto tail_call;
702 } else {
703 zval_collect_white(pz TSRMLS_CC);
704 }
705 p = p->pListNext;
706 }
707 }
708 }
709
zobj_collect_white(zval * pz TSRMLS_DC)710 static void zobj_collect_white(zval *pz TSRMLS_DC)
711 {
712 Bucket *p;
713
714 if (EG(objects_store).object_buckets) {
715 zend_object_get_gc_t get_gc;
716 struct _store_object *obj = &EG(objects_store).object_buckets[Z_OBJ_HANDLE_P(pz)].bucket.obj;
717
718 if (obj->buffered == (gc_root_buffer*)GC_WHITE) {
719 /* PURPLE instead of BLACK to prevent buffering in nested gc calls */
720 GC_SET_PURPLE(obj->buffered);
721
722 if (EXPECTED(EG(objects_store).object_buckets[Z_OBJ_HANDLE_P(pz)].valid &&
723 (get_gc = Z_OBJ_HANDLER_P(pz, get_gc)) != NULL)) {
724 int i, n;
725 zval **table;
726 HashTable *props = get_gc(pz, &table, &n TSRMLS_CC);
727
728 for (i = 0; i < n; i++) {
729 if (table[i]) {
730 pz = table[i];
731 if (Z_TYPE_P(pz) != IS_ARRAY || Z_ARRVAL_P(pz) != &EG(symbol_table)) {
732 pz->refcount__gc++;
733 }
734 zval_collect_white(pz TSRMLS_CC);
735 }
736 }
737 if (!props) {
738 return;
739 }
740 p = props->pListHead;
741 while (p != NULL) {
742 pz = *(zval**)p->pData;
743 if (Z_TYPE_P(pz) != IS_ARRAY || Z_ARRVAL_P(pz) != &EG(symbol_table)) {
744 pz->refcount__gc++;
745 }
746 zval_collect_white(pz TSRMLS_CC);
747 p = p->pListNext;
748 }
749 }
750 }
751 }
752 }
753
gc_collect_roots(TSRMLS_D)754 static void gc_collect_roots(TSRMLS_D)
755 {
756 gc_root_buffer *current = GC_G(roots).next;
757
758 while (current != &GC_G(roots)) {
759 if (current->handle) {
760 if (EG(objects_store).object_buckets) {
761 struct _store_object *obj = &EG(objects_store).object_buckets[current->handle].bucket.obj;
762 zval z;
763
764 GC_SET_ADDRESS(obj->buffered, NULL);
765 INIT_PZVAL(&z);
766 Z_OBJ_HANDLE(z) = current->handle;
767 Z_OBJ_HT(z) = current->u.handlers;
768 zobj_collect_white(&z TSRMLS_CC);
769 }
770 } else {
771 GC_ZVAL_SET_ADDRESS(current->u.pz, NULL);
772 zval_collect_white(current->u.pz TSRMLS_CC);
773 }
774
775 GC_REMOVE_FROM_BUFFER(current);
776 current = current->next;
777 }
778 }
779
780 #define FREE_LIST_END ((zval_gc_info*)(~(zend_uintptr_t)GC_COLOR))
781
gc_collect_cycles(TSRMLS_D)782 ZEND_API int gc_collect_cycles(TSRMLS_D)
783 {
784 int count = 0;
785
786 if (GC_G(roots).next != &GC_G(roots)) {
787 zval_gc_info *p, *q, *orig_free_list, *orig_next_to_free;
788
789 if (GC_G(gc_active)) {
790 return 0;
791 }
792 GC_G(gc_runs)++;
793 GC_G(zval_to_free) = FREE_LIST_END;
794 GC_G(gc_active) = 1;
795 gc_mark_roots(TSRMLS_C);
796 gc_scan_roots(TSRMLS_C);
797 gc_collect_roots(TSRMLS_C);
798
799 orig_free_list = GC_G(free_list);
800 orig_next_to_free = GC_G(next_to_free);
801 p = GC_G(free_list) = GC_G(zval_to_free);
802 GC_G(zval_to_free) = NULL;
803 GC_G(gc_active) = 0;
804
805 /* First call destructors */
806 while (p != FREE_LIST_END) {
807 if (Z_TYPE(p->z) == IS_OBJECT) {
808 if (EG(objects_store).object_buckets &&
809 EG(objects_store).object_buckets[Z_OBJ_HANDLE(p->z)].valid &&
810 EG(objects_store).object_buckets[Z_OBJ_HANDLE(p->z)].bucket.obj.refcount <= 0 &&
811 EG(objects_store).object_buckets[Z_OBJ_HANDLE(p->z)].bucket.obj.dtor &&
812 !EG(objects_store).object_buckets[Z_OBJ_HANDLE(p->z)].destructor_called) {
813
814 EG(objects_store).object_buckets[Z_OBJ_HANDLE(p->z)].destructor_called = 1;
815 EG(objects_store).object_buckets[Z_OBJ_HANDLE(p->z)].bucket.obj.refcount++;
816 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);
817 EG(objects_store).object_buckets[Z_OBJ_HANDLE(p->z)].bucket.obj.refcount--;
818 }
819 }
820 count++;
821 p = p->u.next;
822 }
823
824 /* Destroy zvals */
825 p = GC_G(free_list);
826 while (p != FREE_LIST_END) {
827 GC_G(next_to_free) = p->u.next;
828 if (Z_TYPE(p->z) == IS_OBJECT) {
829 if (EG(objects_store).object_buckets &&
830 EG(objects_store).object_buckets[Z_OBJ_HANDLE(p->z)].valid &&
831 EG(objects_store).object_buckets[Z_OBJ_HANDLE(p->z)].bucket.obj.refcount <= 0) {
832 EG(objects_store).object_buckets[Z_OBJ_HANDLE(p->z)].bucket.obj.refcount = 1;
833 Z_TYPE(p->z) = IS_NULL;
834 zend_objects_store_del_ref_by_handle_ex(Z_OBJ_HANDLE(p->z), Z_OBJ_HT(p->z) TSRMLS_CC);
835 }
836 } else if (Z_TYPE(p->z) == IS_ARRAY) {
837 Z_TYPE(p->z) = IS_NULL;
838 zend_hash_destroy(Z_ARRVAL(p->z));
839 FREE_HASHTABLE(Z_ARRVAL(p->z));
840 } else {
841 zval_dtor(&p->z);
842 Z_TYPE(p->z) = IS_NULL;
843 }
844 p = GC_G(next_to_free);
845 }
846
847 /* Free zvals */
848 p = GC_G(free_list);
849 while (p != FREE_LIST_END) {
850 q = p->u.next;
851 FREE_ZVAL_EX(&p->z);
852 p = q;
853 }
854 GC_G(collected) += count;
855 GC_G(free_list) = orig_free_list;
856 GC_G(next_to_free) = orig_next_to_free;
857 }
858
859 return count;
860 }
861
862 /*
863 * Local variables:
864 * tab-width: 4
865 * c-basic-offset: 4
866 * indent-tabs-mode: t
867 * End:
868 */
869