#ifndef ZEND_GC_H #define ZEND_GC_H #define GC_BLACK 0x00 #define GC_PURPLE 0x20 #define GC_WHITE 0x40 #define GC_GREY 0x60 #define GC_TYPE_NULL 0 #define GC_TYPE_ARRAY 1 #define GC_TYPE_OBJECT 2 #define GC_TYPE_ZOBJECT 3 #define INIT_GC(z) \ (z)->is_ref__gc &= 0x80; #define INIT_GC_OBJ(z) \ (z)->color = GC_BLACK; \ (z)->buffered = 0; typedef struct _zval_root_buffer { zend_uchar valid; zend_uint index; zend_uchar type; zval *pz; zend_object_value obj; struct _zval_root_buffer *prev; struct _zval_root_buffer *next; } zval_root_buffer; typedef struct _zval_gc_info { zval z; zend_bool added; zend_bool buffered; zend_uint index; struct _zval_gc_info *bucketPrev; struct _zval_gc_info *bucketNext; } zval_gc_info; #define Z_COLOR(pz) ((pz).is_ref__gc & 0x60) #define Z_SET_COLOR(pz, color) (pz).is_ref__gc = ((pz).is_ref__gc & 0x9f) | color; #define Z_SET_BLACK(pz) (pz).is_ref__gc &= 0x9f; #define Z_BUFFERED(pz) ((pz).is_ref__gc & 0x10) #define Z_SET_BUFFERED(pz) ((pz).is_ref__gc |= 0x10) #define Z_UNSET_BUFFERED(pz) ((pz).is_ref__gc &= 0xef) #define Z_COLOR_P(pz) Z_COLOR(*(pz)) #define Z_SET_COLOR_P(pz, color) Z_SET_COLOR(*(pz), color) #define Z_SET_BLACK_P(pz) Z_SET_BLACK(*(pz)) #define Z_BUFFERED_P(pz) Z_BUFFERED(*(pz)) #define Z_SET_BUFFERED_P(pz) Z_SET_BUFFERED(*(pz)) #define Z_UNSET_BUFFERED_P(pz) Z_UNSET_BUFFERED(*(pz)) #define GC_MAX_BUCKETS 0xfffff #define GC_BUCKET(pz) GC_G(gc_info_buckets)[((zend_uintptr_t)(pz)) & GC_MAX_BUCKETS] struct _zend_gc_globals { zend_bool gc_enabled; zend_bool gc_active; zend_bool gc_failure; zend_uint zval_root_buf_length; zval_root_buffer *zval_roots; zval_root_buffer *zval_root_buf; zval_root_buffer *zval_root_buf_unused; zval **zval_to_free; zend_uint zval_to_free_length; zend_uint zval_to_free_max_length; zval_gc_info **gc_info_buckets; #ifdef BENCH_GC FILE *logFile; #endif }; typedef struct _zend_gc_globals zend_gc_globals; #ifdef ZTS BEGIN_EXTERN_C() ZEND_API extern int gc_globals_id; END_EXTERN_C() #define GC_G(v) TSRMG(gc_globals_id, zend_gc_globals *, v) #else #define GC_G(v) (gc_globals.v) extern ZEND_API zend_gc_globals gc_globals; #endif #ifdef BENCH_GC extern ZEND_API unsigned long removeFromBuffer; extern ZEND_API unsigned long removeFromBufferLoops; extern ZEND_API unsigned long zvalPossibleRoots; extern ZEND_API unsigned long zvalPossibleRootsTemporary; extern ZEND_API unsigned long zvalPossibleRootsNonstandard; extern ZEND_API unsigned long zvalPossibleRootsAlreadyBuffered; extern ZEND_API unsigned long zvalPossibleRootsObject; extern ZEND_API unsigned long zvalPossibleRootsArray; extern ZEND_API unsigned long zobjPossibleRoots; extern ZEND_API unsigned long zobjPossibleRootsAlreadyBuffered; extern ZEND_API unsigned long cycleCollections; extern ZEND_API unsigned long objectsCollected; extern ZEND_API unsigned long zvalMarkFaulty; extern ZEND_API unsigned long zvalMarkedGrey; extern ZEND_API unsigned long zobjMarkedGrey; #endif BEGIN_EXTERN_C() ZEND_API void gc_collect_cycles(TSRMLS_D); ZEND_API int zval_possible_root(zval *zv TSRMLS_DC); ZEND_API int zend_object_possible_root(zend_object_value objval TSRMLS_DC); ZEND_API void gc_globals_ctor(TSRMLS_D); ZEND_API void gc_globals_dtor(TSRMLS_D); ZEND_API void gc_root_buffer_ctor(TSRMLS_D); END_EXTERN_C() #define ZVAL_ROOT_BUFFER_MAX_ENTRIES 10000 #define CHECK_POSSIBLE_ROOT(z) \ if (UNEXPECTED(GC_G(gc_enabled))) { \ if (Z_REFCOUNT_P(z) > 0 && ((z)->type == IS_ARRAY || (z)->type == IS_CONSTANT_ARRAY || (z)->type == IS_OBJECT)) { \ failure = zval_possible_root((z) TSRMLS_CC); \ } else { \ Z_SET_BLACK_P(z); \ } \ } #define CHECK_POSSIBLE_OBJECT_ROOT(handle) \ if (UNEXPECTED(GC_G(gc_enabled))) { \ if (EXPECTED(EG(objects_store).object_buckets != NULL)) { \ if ( EG(objects_store).object_buckets[handle].valid == 1 && \ EG(objects_store).object_buckets[handle].bucket.obj.refcount > 0) { \ zend_object_possible_root(zobject->value.obj TSRMLS_CC); \ } else { \ EG(objects_store).object_buckets[handle].bucket.obj.color = GC_BLACK; \ } \ } \ } #define ROOT_BUFFER_ADD_TO_FREELIST(current, nextRoot) \ if ((nextRoot) != NULL) \ (nextRoot)->prev = (current)->prev; \ if ((current)->prev == NULL) \ GC_G(zval_root_buf) = (nextRoot); \ else \ (current)->prev->next = (nextRoot); \ if (GC_G(zval_root_buf_unused) != NULL) \ GC_G(zval_root_buf_unused)->prev = (current); \ (current)->next = GC_G(zval_root_buf_unused); \ (current)->prev = NULL; \ (current)->valid = 0; \ (current)->type = IS_NULL; \ (current)->pz = NULL; \ GC_G(zval_root_buf_unused) = (current); \ GC_G(zval_root_buf_length)--; static inline void remove_zval_from_buffer(zval* z) { if (GC_G(gc_enabled) && GC_G(gc_info_buckets) != NULL) { if (!GC_G(gc_active)) { #ifdef BENCH_GC removeFromBuffer++; #endif if (((zval_gc_info*)z)->buffered) { zval_root_buffer* root_buffer = &GC_G(zval_roots)[((zval_gc_info*)z)->index]; ROOT_BUFFER_ADD_TO_FREELIST(root_buffer, root_buffer->next) ((zval_gc_info*)z)->buffered = 0; } } INIT_GC(z); } } static inline zval_gc_info* get_gc_info(zval* z) { zval_gc_info* gc_info = GC_BUCKET(z); while (gc_info != NULL && gc_info != (zval_gc_info*)z) { gc_info = gc_info->bucketNext; #ifdef BENCH_GC removeFromBufferLoops++; #endif } return gc_info; } static inline void remove_gc_info(zval* z) { if (GC_G(gc_info_buckets) != NULL) { zval_gc_info* gc_info = (zval_gc_info*)(z); if (gc_info->added) { if (gc_info->bucketNext != NULL) gc_info->bucketNext->bucketPrev = gc_info->bucketPrev; if (gc_info->bucketPrev == NULL) GC_BUCKET(gc_info) = gc_info->bucketNext; else gc_info->bucketPrev->bucketNext = gc_info->bucketNext; } } } static inline zval* add_gc_info(zval_gc_info* gc_info) { if (GC_G(gc_enabled) && GC_G(gc_info_buckets) != NULL) { gc_info->added = 1; gc_info->buffered = 0; gc_info->index = 0; if (GC_BUCKET(gc_info) != NULL) { gc_info->bucketNext = GC_BUCKET(gc_info); GC_BUCKET(gc_info)->bucketPrev = gc_info; } else { gc_info->bucketNext = NULL; } gc_info->bucketPrev = NULL; GC_BUCKET(gc_info) = gc_info; } else { gc_info->added = 0; } return &(gc_info->z); } #define ADD_GC_INFO(z) add_gc_info(z) #define REMOVE_GC_INFO(z) remove_gc_info(z) #define GET_GC_INFO(z) get_gc_info(z) #define REMOVE_ZVAL_FROM_BUFFER(z) remove_zval_from_buffer(z) #define REMOVE_ZEND_OBJECT_FROM_BUFFER(obj) \ if ((obj)->buffered >= 1 && (obj)->buffered <= ZVAL_ROOT_BUFFER_MAX_ENTRIES) { \ zval_root_buffer* current = &(GC_G(zval_roots))[(obj)->buffered]; \ if (current->valid == 1) { \ ROOT_BUFFER_ADD_TO_FREELIST(current, current->next) \ } \ (obj)->color = GC_BLACK; \ (obj)->buffered = 0; \ } #endif /* ZEND_GC_H */ /* * Local variables: * tab-width: 4 * c-basic-offset: 4 * indent-tabs-mode: t * End: */