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