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