void gc_init2(){ gc_mark_roots(); }
ZEND_API int zend_gc_collect_cycles(void) { int count = 0; if (GC_G(num_roots)) { gc_root_buffer *current, *last; zend_refcounted *p; uint32_t gc_flags = 0; uint32_t idx, end; gc_stack stack; stack.prev = NULL; stack.next = NULL; if (GC_G(gc_active)) { return 0; } GC_TRACE("Collecting cycles"); GC_G(gc_runs)++; GC_G(gc_active) = 1; GC_TRACE("Marking roots"); gc_mark_roots(&stack); GC_TRACE("Scanning roots"); gc_scan_roots(&stack); GC_TRACE("Collecting roots"); count = gc_collect_roots(&gc_flags, &stack); gc_stack_free(&stack); if (!GC_G(num_roots)) { /* nothing to free */ GC_TRACE("Nothing to free"); GC_G(gc_active) = 0; return 0; } end = GC_G(first_unused); if (gc_flags & GC_HAS_DESTRUCTORS) { uint32_t *refcounts; GC_TRACE("Calling destructors"); // TODO: may be use emalloc() ??? refcounts = pemalloc(sizeof(uint32_t) * end, 1); /* Remember reference counters before calling destructors */ idx = GC_FIRST_ROOT; current = GC_IDX2PTR(GC_FIRST_ROOT); while (idx != end) { if (GC_IS_GARBAGE(current->ref)) { p = GC_GET_PTR(current->ref); refcounts[idx] = GC_REFCOUNT(p); } current++; idx++; } /* Call destructors * * The root buffer might be reallocated during destructors calls, * make sure to reload pointers as necessary. */ idx = GC_FIRST_ROOT; while (idx != end) { current = GC_IDX2PTR(idx); if (GC_IS_GARBAGE(current->ref)) { p = GC_GET_PTR(current->ref); if (GC_TYPE(p) == IS_OBJECT && !(OBJ_FLAGS(p) & IS_OBJ_DESTRUCTOR_CALLED)) { zend_object *obj = (zend_object*)p; GC_TRACE_REF(obj, "calling destructor"); GC_ADD_FLAGS(obj, IS_OBJ_DESTRUCTOR_CALLED); if (obj->handlers->dtor_obj != zend_objects_destroy_object || obj->ce->destructor) { GC_ADDREF(obj); obj->handlers->dtor_obj(obj); GC_DELREF(obj); } } } idx++; } /* Remove values captured in destructors */ idx = GC_FIRST_ROOT; current = GC_IDX2PTR(GC_FIRST_ROOT); while (idx != end) { if (GC_IS_GARBAGE(current->ref)) { p = GC_GET_PTR(current->ref); if (GC_REFCOUNT(p) > refcounts[idx]) { gc_remove_nested_data_from_buffer(p, current); } } current++; idx++; } pefree(refcounts, 1); if (GC_G(gc_protected)) { /* something went wrong */ return 0; } } /* Destroy zvals */ GC_TRACE("Destroying zvals"); GC_G(gc_protected) = 1; current = GC_IDX2PTR(GC_FIRST_ROOT); last = GC_IDX2PTR(GC_G(first_unused)); while (current != last) { if (GC_IS_GARBAGE(current->ref)) { p = GC_GET_PTR(current->ref); GC_TRACE_REF(p, "destroying"); if (GC_TYPE(p) == IS_OBJECT) { zend_object *obj = (zend_object*)p; EG(objects_store).object_buckets[obj->handle] = SET_OBJ_INVALID(obj); GC_TYPE_INFO(obj) = IS_NULL | (GC_TYPE_INFO(obj) & ~GC_TYPE_MASK); if (!(OBJ_FLAGS(obj) & IS_OBJ_FREE_CALLED)) { GC_ADD_FLAGS(obj, IS_OBJ_FREE_CALLED); GC_ADDREF(obj); obj->handlers->free_obj(obj); GC_DELREF(obj); } ZEND_OBJECTS_STORE_ADD_TO_FREE_LIST(obj->handle); current->ref = GC_MAKE_GARBAGE(((char*)obj) - obj->handlers->offset); } else if (GC_TYPE(p) == IS_ARRAY) { zend_array *arr = (zend_array*)p; GC_TYPE_INFO(arr) = IS_NULL | (GC_TYPE_INFO(arr) & ~GC_TYPE_MASK); /* GC may destroy arrays with rc>1. This is valid and safe. */ HT_ALLOW_COW_VIOLATION(arr); zend_hash_destroy(arr); } } current++; } /* Free objects */ current = GC_IDX2PTR(GC_FIRST_ROOT); while (current != last) { if (GC_IS_GARBAGE(current->ref)) { p = GC_GET_PTR(current->ref); GC_LINK_UNUSED(current); GC_G(num_roots)--; efree(p); } current++; } GC_TRACE("Collection finished"); GC_G(collected) += count; GC_G(gc_protected) = 0; GC_G(gc_active) = 0; } gc_compact(); return count; }