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: zend_gc.c,v 1.1.1.4 2014/06/15 20:04:03 misho Exp $ */
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: {
35: if (gc_globals->buf) {
36: free(gc_globals->buf);
37: gc_globals->buf = NULL;
38: }
39: }
40:
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:
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:
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:
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:
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:
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:
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:
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:
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:
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:
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:
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:
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:
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:
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:
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:
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:
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:
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:
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: */
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>