1/* 2 +----------------------------------------------------------------------+ 3 | Zend Engine | 4 +----------------------------------------------------------------------+ 5 | Copyright (c) 1998-2013 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#ifndef ZEND_GC_H 23#define ZEND_GC_H 24 25#ifndef GC_BENCH 26# define GC_BENCH 0 27#endif 28 29#if GC_BENCH 30# define GC_BENCH_INC(counter) GC_G(counter)++ 31# define GC_BENCH_DEC(counter) GC_G(counter)-- 32# define GC_BENCH_PEAK(peak, counter) do { \ 33 if (GC_G(counter) > GC_G(peak)) { \ 34 GC_G(peak) = GC_G(counter); \ 35 } \ 36 } while (0) 37#else 38# define GC_BENCH_INC(counter) 39# define GC_BENCH_DEC(counter) 40# define GC_BENCH_PEAK(peak, counter) 41#endif 42 43#define GC_COLOR 0x03 44 45#define GC_BLACK 0x00 46#define GC_WHITE 0x01 47#define GC_GREY 0x02 48#define GC_PURPLE 0x03 49 50#define GC_ADDRESS(v) \ 51 ((gc_root_buffer*)(((zend_uintptr_t)(v)) & ~GC_COLOR)) 52#define GC_SET_ADDRESS(v, a) \ 53 (v) = ((gc_root_buffer*)((((zend_uintptr_t)(v)) & GC_COLOR) | ((zend_uintptr_t)(a)))) 54#define GC_GET_COLOR(v) \ 55 (((zend_uintptr_t)(v)) & GC_COLOR) 56#define GC_SET_COLOR(v, c) \ 57 (v) = ((gc_root_buffer*)((((zend_uintptr_t)(v)) & ~GC_COLOR) | (c))) 58#define GC_SET_BLACK(v) \ 59 (v) = ((gc_root_buffer*)(((zend_uintptr_t)(v)) & ~GC_COLOR)) 60#define GC_SET_PURPLE(v) \ 61 (v) = ((gc_root_buffer*)(((zend_uintptr_t)(v)) | GC_PURPLE)) 62 63#define GC_ZVAL_INIT(z) \ 64 ((zval_gc_info*)(z))->u.buffered = NULL 65#define GC_ZVAL_ADDRESS(v) \ 66 GC_ADDRESS(((zval_gc_info*)(v))->u.buffered) 67#define GC_ZVAL_SET_ADDRESS(v, a) \ 68 GC_SET_ADDRESS(((zval_gc_info*)(v))->u.buffered, (a)) 69#define GC_ZVAL_GET_COLOR(v) \ 70 GC_GET_COLOR(((zval_gc_info*)(v))->u.buffered) 71#define GC_ZVAL_SET_COLOR(v, c) \ 72 GC_SET_COLOR(((zval_gc_info*)(v))->u.buffered, (c)) 73#define GC_ZVAL_SET_BLACK(v) \ 74 GC_SET_BLACK(((zval_gc_info*)(v))->u.buffered) 75#define GC_ZVAL_SET_PURPLE(v) \ 76 GC_SET_PURPLE(((zval_gc_info*)(v))->u.buffered) 77 78#define GC_OBJ_INIT(z) \ 79 (z)->buffered = NULL 80 81typedef struct _gc_root_buffer { 82 struct _gc_root_buffer *prev; /* double-linked list */ 83 struct _gc_root_buffer *next; 84 zend_object_handle handle; /* must be 0 for zval */ 85 union { 86 zval *pz; 87 const zend_object_handlers *handlers; 88 } u; 89} gc_root_buffer; 90 91typedef struct _zval_gc_info { 92 zval z; 93 union { 94 gc_root_buffer *buffered; 95 struct _zval_gc_info *next; 96 } u; 97} zval_gc_info; 98 99typedef struct _zend_gc_globals { 100 zend_bool gc_enabled; 101 zend_bool gc_active; 102 103 gc_root_buffer *buf; /* preallocated arrays of buffers */ 104 gc_root_buffer roots; /* list of possible roots of cycles */ 105 gc_root_buffer *unused; /* list of unused buffers */ 106 gc_root_buffer *first_unused; /* pointer to first unused buffer */ 107 gc_root_buffer *last_unused; /* pointer to last unused buffer */ 108 109 zval_gc_info *zval_to_free; /* temporaryt list of zvals to free */ 110 zval_gc_info *free_list; 111 zval_gc_info *next_to_free; 112 113 zend_uint gc_runs; 114 zend_uint collected; 115 116#if GC_BENCH 117 zend_uint root_buf_length; 118 zend_uint root_buf_peak; 119 zend_uint zval_possible_root; 120 zend_uint zobj_possible_root; 121 zend_uint zval_buffered; 122 zend_uint zobj_buffered; 123 zend_uint zval_remove_from_buffer; 124 zend_uint zobj_remove_from_buffer; 125 zend_uint zval_marked_grey; 126 zend_uint zobj_marked_grey; 127#endif 128 129} zend_gc_globals; 130 131#ifdef ZTS 132BEGIN_EXTERN_C() 133ZEND_API extern int gc_globals_id; 134END_EXTERN_C() 135#define GC_G(v) TSRMG(gc_globals_id, zend_gc_globals *, v) 136#else 137#define GC_G(v) (gc_globals.v) 138extern ZEND_API zend_gc_globals gc_globals; 139#endif 140 141BEGIN_EXTERN_C() 142ZEND_API int gc_collect_cycles(TSRMLS_D); 143ZEND_API void gc_zval_possible_root(zval *zv TSRMLS_DC); 144ZEND_API void gc_zobj_possible_root(zval *zv TSRMLS_DC); 145ZEND_API void gc_remove_zval_from_buffer(zval *zv TSRMLS_DC); 146ZEND_API void gc_globals_ctor(TSRMLS_D); 147ZEND_API void gc_globals_dtor(TSRMLS_D); 148ZEND_API void gc_init(TSRMLS_D); 149ZEND_API void gc_reset(TSRMLS_D); 150END_EXTERN_C() 151 152#define GC_ZVAL_CHECK_POSSIBLE_ROOT(z) \ 153 gc_zval_check_possible_root((z) TSRMLS_CC) 154 155#define GC_REMOVE_FROM_BUFFER(current) \ 156 gc_remove_from_buffer((current) TSRMLS_CC) 157 158#define GC_REMOVE_ZVAL_FROM_BUFFER(z) \ 159 if (GC_ADDRESS(((zval_gc_info*)z)->u.buffered)) { \ 160 gc_remove_zval_from_buffer(z TSRMLS_CC); \ 161 } 162 163#define GC_ZOBJ_CHECK_POSSIBLE_ROOT(zobject) \ 164 do { \ 165 if (EXPECTED(EG(objects_store).object_buckets != NULL) && \ 166 EG(objects_store).object_buckets[Z_OBJ_HANDLE_P(zobject)].valid) { \ 167 gc_zobj_possible_root(zobject TSRMLS_CC); \ 168 } \ 169 } while (0) 170 171#define GC_REMOVE_ZOBJ_FROM_BUFFER(obj) \ 172 do { \ 173 if (GC_ADDRESS((obj)->buffered) && !GC_G(gc_active)) { \ 174 GC_BENCH_INC(zobj_remove_from_buffer); \ 175 GC_REMOVE_FROM_BUFFER(GC_ADDRESS((obj)->buffered)); \ 176 (obj)->buffered = NULL; \ 177 } \ 178 } while (0) 179 180static zend_always_inline void gc_zval_check_possible_root(zval *z TSRMLS_DC) 181{ 182 if (z->type == IS_ARRAY || z->type == IS_OBJECT) { 183 gc_zval_possible_root(z TSRMLS_CC); 184 } 185} 186 187static zend_always_inline void gc_remove_from_buffer(gc_root_buffer *root TSRMLS_DC) 188{ 189 root->next->prev = root->prev; 190 root->prev->next = root->next; 191 root->prev = GC_G(unused); 192 GC_G(unused) = root; 193 GC_BENCH_DEC(root_buf_length); 194} 195 196#define ALLOC_PERMANENT_ZVAL(z) \ 197 do { \ 198 (z) = (zval*)malloc(sizeof(zval_gc_info)); \ 199 GC_ZVAL_INIT(z); \ 200 } while (0) 201 202/* The following macroses override macroses from zend_alloc.h */ 203#undef ALLOC_ZVAL 204#define ALLOC_ZVAL(z) \ 205 do { \ 206 (z) = (zval*)emalloc(sizeof(zval_gc_info)); \ 207 GC_ZVAL_INIT(z); \ 208 } while (0) 209 210#undef FREE_ZVAL 211#define FREE_ZVAL(z) \ 212 do { \ 213 GC_REMOVE_ZVAL_FROM_BUFFER(z); \ 214 efree(z); \ 215 } while (0) 216 217#undef ALLOC_ZVAL_REL 218#define ALLOC_ZVAL_REL(z) \ 219 do { \ 220 (z) = (zval*)emalloc_rel(sizeof(zval_gc_info)); \ 221 GC_ZVAL_INIT(z); \ 222 } while (0) 223 224#undef FREE_ZVAL_REL 225#define FREE_ZVAL_REL(z) \ 226 do { \ 227 GC_REMOVE_ZVAL_FROM_BUFFER(z); \ 228 efree_rel(z); \ 229 } while (0) 230 231#define FREE_ZVAL_EX(z) \ 232 efree(z) 233 234#define FREE_ZVAL_REL_EX(z) \ 235 efree_rel(z) 236 237#endif /* ZEND_GC_H */ 238 239/* 240 * Local variables: 241 * tab-width: 4 242 * c-basic-offset: 4 243 * indent-tabs-mode: t 244 * End: 245 */ 246