File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / php / Zend / zend_gc.c
Revision 1.1.1.4 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Sun Jun 15 20:04:03 2014 UTC (10 years, 4 months ago) by misho
Branches: php, MAIN
CVS tags: v5_4_29, HEAD
php 5.4.29

    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>