File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / php / Zend / zend_gc.c
Revision 1.1.1.2 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Tue May 29 12:34:35 2012 UTC (12 years, 1 month ago) by misho
Branches: php, MAIN
CVS tags: v5_4_3elwix, v5_4_17p0, HEAD
php 5.4.3+patches

    1: /*
    2:    +----------------------------------------------------------------------+
    3:    | Zend Engine                                                          |
    4:    +----------------------------------------------------------------------+
    5:    | Copyright (c) 1998-2012 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.2 2012/05/29 12:34:35 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: 				GC_SET_BLACK(obj->buffered);
  648: 
  649: 				if (EXPECTED(EG(objects_store).object_buckets[Z_OBJ_HANDLE_P(pz)].valid &&
  650: 				             (get_gc = Z_OBJ_HANDLER_P(pz, get_gc)) != NULL)) {
  651: 					int i, n;
  652: 					zval **table, *zv;
  653: 					HashTable *props = get_gc(pz, &table, &n TSRMLS_CC);
  654: 
  655: 					if (!props) {
  656: 						/* restore refcount and put into list to free */
  657: 						pz->refcount__gc++;
  658: 						((zval_gc_info*)pz)->u.next = GC_G(zval_to_free);
  659: 						GC_G(zval_to_free) = (zval_gc_info*)pz;
  660: 					}
  661: 
  662: 					while (n > 0 && !table[n-1]) n--;
  663: 					for (i = 0; i < n; i++) {
  664: 						if (table[i]) {
  665: 							zv = table[i];
  666: 							if (Z_TYPE_P(zv) != IS_ARRAY || Z_ARRVAL_P(zv) != &EG(symbol_table)) {
  667: 								zv->refcount__gc++;
  668: 							}
  669: 							if (!props && i == n - 1) {
  670: 								pz = zv;
  671: 								goto tail_call;
  672: 							} else {
  673: 								zval_collect_white(zv TSRMLS_CC);
  674: 							}
  675: 						}
  676: 					}
  677: 					if (!props) {
  678: 						return;
  679: 					}
  680: 					p = props->pListHead;
  681: 				}
  682: 			}
  683: 		} else {
  684: 			if (Z_TYPE_P(pz) == IS_ARRAY) {
  685: 				p = Z_ARRVAL_P(pz)->pListHead;
  686: 			}
  687: 		}
  688: 
  689: 		/* restore refcount and put into list to free */
  690: 		pz->refcount__gc++;
  691: 		((zval_gc_info*)pz)->u.next = GC_G(zval_to_free);
  692: 		GC_G(zval_to_free) = (zval_gc_info*)pz;
  693: 
  694: 		while (p != NULL) {
  695: 			pz = *(zval**)p->pData;
  696: 			if (Z_TYPE_P(pz) != IS_ARRAY || Z_ARRVAL_P(pz) != &EG(symbol_table)) {
  697: 				pz->refcount__gc++;
  698: 			}
  699: 			if (p->pListNext == NULL) {
  700: 				goto tail_call;
  701: 			} else {
  702: 				zval_collect_white(pz TSRMLS_CC);
  703: 			}
  704: 			p = p->pListNext;
  705: 		}
  706: 	}
  707: }
  708: 
  709: static void zobj_collect_white(zval *pz TSRMLS_DC)
  710: {
  711: 	Bucket *p;
  712: 
  713: 	if (EG(objects_store).object_buckets) {
  714: 		zend_object_get_gc_t get_gc;
  715: 		struct _store_object *obj = &EG(objects_store).object_buckets[Z_OBJ_HANDLE_P(pz)].bucket.obj;
  716: 
  717: 		if (obj->buffered == (gc_root_buffer*)GC_WHITE) {
  718: 			GC_SET_BLACK(obj->buffered);
  719: 
  720: 			if (EXPECTED(EG(objects_store).object_buckets[Z_OBJ_HANDLE_P(pz)].valid &&
  721: 			             (get_gc = Z_OBJ_HANDLER_P(pz, get_gc)) != NULL)) {
  722: 				int i, n;
  723: 				zval **table;
  724: 				HashTable *props = get_gc(pz, &table, &n TSRMLS_CC);
  725: 
  726: 				for (i = 0; i < n; i++) {
  727: 					if (table[i]) {
  728: 						pz = table[i];
  729: 						if (Z_TYPE_P(pz) != IS_ARRAY || Z_ARRVAL_P(pz) != &EG(symbol_table)) {
  730: 							pz->refcount__gc++;
  731: 						}
  732: 						zval_collect_white(pz TSRMLS_CC);
  733: 					}
  734: 				}
  735: 				if (!props) {
  736: 					return;
  737: 				}
  738: 				p = props->pListHead;
  739: 				while (p != NULL) {
  740: 					pz = *(zval**)p->pData;
  741: 					if (Z_TYPE_P(pz) != IS_ARRAY || Z_ARRVAL_P(pz) != &EG(symbol_table)) {
  742: 						pz->refcount__gc++;
  743: 					}
  744: 					zval_collect_white(pz TSRMLS_CC);
  745: 					p = p->pListNext;
  746: 				}
  747: 			}
  748: 		}
  749: 	}
  750: }
  751: 
  752: static void gc_collect_roots(TSRMLS_D)
  753: {
  754: 	gc_root_buffer *current = GC_G(roots).next;
  755: 
  756: 	while (current != &GC_G(roots)) {
  757: 		if (current->handle) {
  758: 			if (EG(objects_store).object_buckets) {
  759: 				struct _store_object *obj = &EG(objects_store).object_buckets[current->handle].bucket.obj;
  760: 				zval z;
  761: 
  762: 				GC_SET_ADDRESS(obj->buffered, NULL);
  763: 				INIT_PZVAL(&z);
  764: 				Z_OBJ_HANDLE(z) = current->handle;
  765: 				Z_OBJ_HT(z) = current->u.handlers;
  766: 				zobj_collect_white(&z TSRMLS_CC);
  767: 			}
  768: 		} else {
  769: 			GC_ZVAL_SET_ADDRESS(current->u.pz, NULL);
  770: 			zval_collect_white(current->u.pz TSRMLS_CC);
  771: 		}
  772: 
  773: 		GC_REMOVE_FROM_BUFFER(current);
  774: 		current = current->next;
  775: 	}
  776: }
  777: 
  778: #define FREE_LIST_END ((zval_gc_info*)(~(zend_uintptr_t)GC_COLOR))
  779: 
  780: ZEND_API int gc_collect_cycles(TSRMLS_D)
  781: {
  782: 	int count = 0;
  783: 
  784: 	if (GC_G(roots).next != &GC_G(roots)) {
  785: 		zval_gc_info *p, *q, *orig_free_list, *orig_next_to_free;
  786: 
  787: 		if (GC_G(gc_active)) {
  788: 			return 0;
  789: 		}
  790: 		GC_G(gc_runs)++;
  791: 		GC_G(zval_to_free) = FREE_LIST_END;
  792: 		GC_G(gc_active) = 1;
  793: 		gc_mark_roots(TSRMLS_C);
  794: 		gc_scan_roots(TSRMLS_C);
  795: 		gc_collect_roots(TSRMLS_C);
  796: 
  797: 		orig_free_list = GC_G(free_list);
  798: 		orig_next_to_free = GC_G(next_to_free);
  799: 		p = GC_G(free_list) = GC_G(zval_to_free);
  800: 		GC_G(zval_to_free) = NULL;
  801: 		GC_G(gc_active) = 0;
  802: 
  803: 		/* First call destructors */
  804: 		while (p != FREE_LIST_END) {
  805: 			if (Z_TYPE(p->z) == IS_OBJECT) {
  806: 				if (EG(objects_store).object_buckets &&
  807: 					EG(objects_store).object_buckets[Z_OBJ_HANDLE(p->z)].valid &&
  808: 					EG(objects_store).object_buckets[Z_OBJ_HANDLE(p->z)].bucket.obj.refcount <= 0 &&
  809: 					EG(objects_store).object_buckets[Z_OBJ_HANDLE(p->z)].bucket.obj.dtor &&
  810: 					!EG(objects_store).object_buckets[Z_OBJ_HANDLE(p->z)].destructor_called) {
  811: 
  812: 					EG(objects_store).object_buckets[Z_OBJ_HANDLE(p->z)].destructor_called = 1;
  813: 					EG(objects_store).object_buckets[Z_OBJ_HANDLE(p->z)].bucket.obj.refcount++;
  814: 					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);
  815: 					EG(objects_store).object_buckets[Z_OBJ_HANDLE(p->z)].bucket.obj.refcount--;
  816: 				}
  817: 			}
  818: 			count++;
  819: 			p = p->u.next;
  820: 		}
  821: 
  822: 		/* Destroy zvals */
  823: 		p = GC_G(free_list);
  824: 		while (p != FREE_LIST_END) {
  825: 			GC_G(next_to_free) = p->u.next;
  826: 			if (Z_TYPE(p->z) == IS_OBJECT) {
  827: 				if (EG(objects_store).object_buckets &&
  828: 					EG(objects_store).object_buckets[Z_OBJ_HANDLE(p->z)].valid &&
  829: 					EG(objects_store).object_buckets[Z_OBJ_HANDLE(p->z)].bucket.obj.refcount <= 0) {
  830: 					EG(objects_store).object_buckets[Z_OBJ_HANDLE(p->z)].bucket.obj.refcount = 1;
  831: 					Z_TYPE(p->z) = IS_NULL;
  832: 					zend_objects_store_del_ref_by_handle_ex(Z_OBJ_HANDLE(p->z), Z_OBJ_HT(p->z) TSRMLS_CC);
  833: 				}
  834: 			} else if (Z_TYPE(p->z) == IS_ARRAY) {
  835: 				Z_TYPE(p->z) = IS_NULL;
  836: 				zend_hash_destroy(Z_ARRVAL(p->z));
  837: 				FREE_HASHTABLE(Z_ARRVAL(p->z));
  838: 			} else {
  839: 				zval_dtor(&p->z);
  840: 				Z_TYPE(p->z) = IS_NULL;
  841: 			}
  842: 			p = GC_G(next_to_free);
  843: 		}
  844: 
  845: 		/* Free zvals */
  846: 		p = GC_G(free_list);
  847: 		while (p != FREE_LIST_END) {
  848: 			q = p->u.next;
  849: 			FREE_ZVAL_EX(&p->z);
  850: 			p = q;
  851: 		}
  852: 		GC_G(collected) += count;
  853: 		GC_G(free_list) = orig_free_list;
  854: 		GC_G(next_to_free) = orig_next_to_free;
  855: 	}
  856: 
  857: 	return count;
  858: }
  859: 
  860: /*
  861:  * Local variables:
  862:  * tab-width: 4
  863:  * c-basic-offset: 4
  864:  * indent-tabs-mode: t
  865:  * End:
  866:  */

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>