FazBrowse GitHub Viewer | Trending |
URL:
| Home
Tools: [Download Repo ZIP]   [Original HTTPS Page]

GitHub Viewer

#include "daScript/misc/platform.h" #include "daScript/misc/gc_node.h" #include "daScript/misc/crash_handler.h" #include #include namespace das { // debugging id - to break on static uint64_t gc_break_on_id = 0; // installed by the AST layer (ast_gc_report.cpp); see gc_node.h DAS_API gc_describe_fn gc_node_describe_hook = nullptr; // ---- gc_root statics ---- static thread_local gc_root s_gc_thread_root; static thread_local gc_root * s_gc_active_root = &s_gc_thread_root; std::atomic gc_root::gc_next_id { 1 }; static void gc_check_break ( uint64_t id ); gc_root & gc_root::gc_get_thread_root() { return s_gc_thread_root; } gc_root * & gc_root::gc_get_active_root() { return s_gc_active_root; } // ---- gc_root ---- gc_root::~gc_root() { if ( gc_first ) { gc_sweep(); } } void gc_root::gc_link ( gc_node * node ) { DAS_ASSERT(node && "gc_link: null node"); DAS_ASSERT(node->gc_owner == nullptr && "gc_link: node already on a root"); // gc_check_break(node->gc_id); node->gc_owner = this; node->gc_prev = gc_last; node->gc_next = nullptr; if ( gc_last ) { gc_last->gc_next = node; } else { gc_first = node; } gc_last = node; gc_count++; } void gc_root::gc_unlink ( gc_node * node ) { DAS_ASSERT(node && "gc_unlink: null node"); DAS_ASSERT(node->gc_owner == this && "gc_unlink: node not on this root"); // gc_check_break(node->gc_id); if ( node->gc_prev ) { node->gc_prev->gc_next = node->gc_next; } else { gc_first = node->gc_next; } if ( node->gc_next ) { node->gc_next->gc_prev = node->gc_prev; } else { gc_last = node->gc_prev; } node->gc_prev = nullptr; node->gc_next = nullptr; node->gc_owner = nullptr; DAS_ASSERT(gc_count > 0); gc_count--; } void gc_root::gc_sweep() { gc_collecting = true; while ( gc_first ) { auto node = gc_first; gc_unlink(node); uint32_t swept_magic = GC_MAGIC_SWEPT_PREFIX | (node->gc_magic & 0xFFFFu); #if DAS_GC_DEBUG // !!! DO NOT REMOVE !!! // Memory poisoning mode for debugging use-after-sweep. // Destroys the object but keeps memory allocated with 0xCD fill. // gc_magic (with type tag) and gc_id are restored so crashed nodes can be identified. // Set DAS_GC_DEBUG=1 to enable, then use DAS_GC_BREAK_ON_ID to find the creator. { uint64_t saved_id = node->gc_id; node->~gc_node(); #ifdef _MSC_VER auto sz = _msize(node); #elif defined(__APPLE__) auto sz = malloc_size(node); #else auto sz = malloc_usable_size(node); #endif memset(node, 0xCD, sz); node->gc_magic = swept_magic; node->gc_id = saved_id; } #else node->gc_magic = swept_magic; delete node; #endif } gc_collecting = false; } void gc_root::gc_dump_to_thread_root() { auto & threadRoot = gc_get_thread_root(); while ( gc_first ) { auto node = gc_first; gc_unlink(node); threadRoot.gc_link(node); } } void gc_root::gc_report() const { DAS_FATAL_LOG("gc_root %p: count=%" PRIu64 "\n", (const void *)this, gc_count); char atbuf[256]; auto node = gc_first; while ( node ) { atbuf[0] = 0; if ( gc_node_describe_hook ) gc_node_describe_hook(node, atbuf, (int)sizeof(atbuf)); DAS_FATAL_LOG(" node %p: id=%" PRIu64 " type=%s magic=0x%08x %s\n", (const void *)node, node->gc_id, node->gc_type_name(), node->gc_magic, atbuf); node = node->gc_next; } } // ---- gc_node debug break ---- // Set gc_break_on_id via debugger or env DAS_GC_BREAK_ON_ID to break // when a specific gc_id is assigned. Useful for tracing where a leaked node was born. static uint64_t gc_init_break_on_id() { if ( auto env = getenv("DAS_GC_BREAK_ON_ID") ) { return strtoull(env, nullptr, 0); } return 0; } static void gc_check_break ( uint64_t id ) { if ( gc_break_on_id == 0 ) { static bool once = true; if ( once ) { once = false; gc_break_on_id = gc_init_break_on_id(); } } if ( gc_break_on_id && id == gc_break_on_id ) { DAS_FATAL_LOG("gc_node break: id=%" PRIu64 "\n", id); print_current_stack_trace(); os_debug_break(); } } // ---- gc_node ---- gc_node::gc_node() { gc_id = gc_root::gc_next_id.fetch_add(1, std::memory_order_relaxed); gc_check_break(gc_id); gc_root::gc_get_active_root()->gc_link(this); } gc_node::gc_node ( const gc_node & ) { // New node with new ID, linked to the active root. Does NOT copy list position. gc_id = gc_root::gc_next_id.fetch_add(1, std::memory_order_relaxed); gc_check_break(gc_id); gc_root::gc_get_active_root()->gc_link(this); } gc_node::gc_node ( gc_node && src ) { // Take over source's list position gc_magic = src.gc_magic; gc_id = src.gc_id; gc_prev = src.gc_prev; gc_next = src.gc_next; gc_owner = src.gc_owner; // Patch neighbors to point to us if ( gc_prev ) { gc_prev->gc_next = this; } else if ( gc_owner ) { gc_owner->gc_first = this; } if ( gc_next ) { gc_next->gc_prev = this; } else if ( gc_owner ) { gc_owner->gc_last = this; } // Detach source src.gc_prev = nullptr; src.gc_next = nullptr; src.gc_owner = nullptr; src.gc_magic = GC_MAGIC_DEAD; } gc_node & gc_node::operator = ( gc_node && src ) { if ( this == &src ) return *this; // Unlink self from current root if ( gc_owner ) { gc_owner->gc_unlink(this); } // Take over source's list position gc_magic = src.gc_magic; gc_id = src.gc_id; gc_prev = src.gc_prev; gc_next = src.gc_next; gc_owner = src.gc_owner; if ( gc_prev ) { gc_prev->gc_next = this; } else if ( gc_owner ) { gc_owner->gc_first = this; } if ( gc_next ) { gc_next->gc_prev = this; } else if ( gc_owner ) { gc_owner->gc_last = this; } src.gc_prev = nullptr; src.gc_next = nullptr; src.gc_owner = nullptr; src.gc_magic = GC_MAGIC_DEAD; return *this; } gc_node::~gc_node() { gc_check_break(gc_id); if ( gc_owner ) { // Node is being deleted outside of gc_sweep. // During the migration period, this can happen legitimately when old code // paths delete TypeDecl. In DAS_GC_DEBUG mode, assert to catch bugs. #if DAS_GC_DEBUG if ( !gc_owner->gc_collecting ) { DAS_FATAL_LOG("gc_node id=%" PRIu64 " deleted outside of gc_sweep\n", gc_id); DAS_ASSERTF(false, "gc_node id=%" PRIu64 " deleted outside of gc_sweep", gc_id); } #endif gc_owner->gc_unlink(this); } if ( gc_is_alive() ) { gc_magic = GC_MAGIC_DEAD; } } void gc_node::gc_unlink() { if ( gc_owner ) { gc_owner->gc_unlink(this); } } void gc_node::gc_link ( gc_root * root ) { DAS_ASSERT(gc_owner == nullptr && "gc_link: already linked"); root->gc_link(this); } void gc_node::gc_assign ( gc_root * new_root ) { if ( gc_owner == new_root ) return; if ( gc_owner ) { gc_owner->gc_unlink(this); } new_root->gc_link(this); } const char * gc_node::gc_type_name() const { uint16_t tag = gc_magic & 0xFFFFu; switch ( tag ) { case GC_TAG_TYPEDECL: return "TypeDecl"; case GC_TAG_EXPRESSION: return "Expression"; case GC_TAG_MAKEFIELDDECL: return "MakeFieldDecl"; case GC_TAG_MAKESTRUCT: return "MakeStruct"; case GC_TAG_FUNCTION: return "Function"; case GC_TAG_ENUMERATION: return "Enumeration"; case GC_TAG_STRUCTURE: return "Structure"; case GC_TAG_VARIABLE: return "Variable"; default: return "gc_node"; } } void gc_node::gc_verify() const { if ( !gc_is_alive() ) { const char * typeName = gc_type_name(); if ( (gc_magic & 0xFFFF0000u) == GC_MAGIC_SWEPT_PREFIX ) { DAS_FATAL_ERROR("%s id=%" PRIu64 " was already swept (use-after-sweep)", typeName, gc_id); } else if ( gc_magic == GC_MAGIC_DEAD ) { DAS_FATAL_ERROR("%s id=%" PRIu64 " was already deleted (use-after-free)", typeName, gc_id); } else { DAS_FATAL_ERROR("%s id=%" PRIu64 " has corrupt magic=0x%08x", typeName, gc_id, gc_magic); } } } // ---- gc_guard ---- gc_guard::gc_guard() { saved_thread_root = gc_root::gc_get_active_root(); gc_root::gc_get_active_root() = &guard_root; } gc_guard::~gc_guard() { gc_root::gc_get_active_root() = saved_thread_root; guard_root.gc_sweep(); } gc_active_scope::gc_active_scope ( gc_root * use ) { saved_active = gc_root::gc_get_active_root(); gc_root::gc_get_active_root() = use; } gc_active_scope::~gc_active_scope() { gc_root::gc_get_active_root() = saved_active; } }

Back | FazBrowse Home | New Git URL