diff --git a/CMakeLists.txt b/CMakeLists.txt index 6762ca3..e674563 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -29,6 +29,7 @@ set(CMAKE_CXX_STANDARD 20) add_library( rt OBJECT src/rt/rt.cc + src/rt/objects/region.cc src/rt/ui/mermaid.cc src/rt/core/builtin.cc ) diff --git a/src/rt/core.h b/src/rt/core.h index f39ad14..2793f48 100644 --- a/src/rt/core.h +++ b/src/rt/core.h @@ -1,26 +1,11 @@ -#include "objects/dyn_object.h" +#include "objects/prototype_object.h" #include "rt.h" #include namespace rt::core { - class PrototypeObject : public objects::DynObject - { - std::string name; - - public: - PrototypeObject(std::string name_, objects::DynObject* prototype = nullptr) - : objects::DynObject(prototype), name(name_) - {} - - std::string get_name() override - { - std::stringstream stream; - stream << "[" << name << "]"; - return stream.str(); - } - }; + using PrototypeObject = objects::PrototypeObject; inline PrototypeObject* framePrototypeObject() { @@ -39,7 +24,7 @@ namespace rt::core if (parent_frame) { auto old_value = this->set(objects::ParentField, parent_frame); - add_reference(this, parent_frame); + objects::add_reference(this, parent_frame); assert(!old_value); } } diff --git a/src/rt/core/builtin.cc b/src/rt/core/builtin.cc index 4810b02..db6d1d6 100644 --- a/src/rt/core/builtin.cc +++ b/src/rt/core/builtin.cc @@ -27,7 +27,7 @@ namespace rt::core { auto value = stack->back(); mermaid->add_always_hide(value); - remove_reference(frame, value); + rt::remove_reference(frame, value); stack->pop_back(); } @@ -42,7 +42,7 @@ namespace rt::core auto value = stack->back(); mermaid->remove_unreachable_hide(value); mermaid->remove_always_hide(value); - remove_reference(frame, value); + rt::remove_reference(frame, value); stack->pop_back(); } @@ -66,7 +66,7 @@ namespace rt::core auto region = pop(stack, "region for cown creation"); auto cown = make_cown(region); - move_reference(frame, cown, region); + rt::move_reference(frame, cown, region); return cown; }); @@ -89,7 +89,7 @@ namespace rt::core auto value = pop(stack, "object to freeze"); freeze(value); - remove_reference(frame, value); + rt::remove_reference(frame, value); return std::nullopt; }); @@ -98,8 +98,8 @@ namespace rt::core assert(args == 1); auto value = pop(stack, "region source"); - create_region(value); - remove_reference(frame, value); + rt::create_region(value); + rt::remove_reference(frame, value); return std::nullopt; }); diff --git a/src/rt/objects/dyn_object.h b/src/rt/objects/dyn_object.h index 0a6cde8..05a0e7a 100644 --- a/src/rt/objects/dyn_object.h +++ b/src/rt/objects/dyn_object.h @@ -27,6 +27,11 @@ namespace rt::objects const std::string PrototypeField{"__proto__"}; const std::string ParentField{"__parent__"}; + Region* get_region(DynObject* obj); + + Region* get_local_region(); + void set_local_region(Region* region); + // Representation of objects class DynObject { @@ -39,8 +44,8 @@ namespace rt::objects friend void dealloc(DynObject* obj); template friend void visit(Edge, Pre, Post); - - thread_local static RegionPointer local_region; + friend Region* get_region(DynObject* obj); + friend void add_to_region(Region* r, DynObject* target); // TODO: Not concurrency safe inline static size_t count{0}; @@ -53,68 +58,7 @@ namespace rt::objects std::map fields{}; - static Region* get_region(DynObject* obj) - { - if ((obj == nullptr) || obj->is_immutable()) - return nullptr; - return obj->region.get_ptr(); - } - - bool is_local_object() - { - return region.get_ptr() == get_local_region(); - } - - static void remove_region_reference(Region* src, Region* target) - { - if (src == target) - { - std::cout << "Same region, no need to do anything" << std::endl; - return; - } - - // Handle immutable case - if (target == nullptr) - return; - - if (src == get_local_region()) - { - Region::dec_lrc(target); - return; - } - - if (src) - { - assert(target->parent == src); - Region::dec_prc(target); - } - return; - } - - static void add_region_reference(Region* src_region, DynObject* target) - { - if (target->is_immutable()) - return; - - auto target_region = get_region(target); - if (src_region == target_region) - return; - - if (src_region == get_local_region()) - { - Region::inc_lrc(target_region); - return; - } - - if (target_region == get_local_region()) - { - target->add_to_region(src_region); - return; - } - - Region::set_parent(target_region, src_region); - } - + public: size_t change_rc(signed delta) { std::cout << "Change RC: " << get_name() << " " << rc << " + " << delta @@ -134,50 +78,6 @@ namespace rt::objects delta; } - void add_to_region(Region* r) - { - size_t internal_references{0}; - size_t rc_of_added_objects{0}; - visit(this, [&](Edge e) { - auto obj = e.target; - if (obj == nullptr || obj->is_immutable()) - return false; - - if (obj->is_local_object()) - { - std::cout << "Adding object to region: " << obj->get_name() - << " rc = " << obj->rc << std::endl; - rc_of_added_objects += obj->rc; - internal_references++; - obj->region = {r}; - get_local_region()->objects.erase(obj); - r->objects.insert(obj); - return true; - } - - auto obj_region = get_region(obj); - if (obj_region == r) - { - std::cout << "Adding internal reference to object: " - << obj->get_name() << std::endl; - internal_references++; - return false; - } - - Region::set_parent(obj_region, r); - Region::dec_lrc(obj_region); - return false; - }); - r->local_reference_count += rc_of_added_objects - internal_references; - - std::cout << "Added " << rc_of_added_objects - internal_references - << " to LRC of region" << std::endl; - std::cout << "Region LRC: " << r->local_reference_count << std::endl; - std::cout << "Internal references found: " << internal_references - << std::endl; - } - - public: // prototype is borrowed, the caller does not need to provide an RC. DynObject(DynObject* prototype_ = nullptr, bool first_frame = false) : prototype(prototype_) @@ -194,7 +94,7 @@ namespace rt::objects if (prototype != nullptr) { // prototype->change_rc(1); - add_reference(this, prototype); + objects::add_reference(this, prototype); } std::cout << "Allocate: " << this << std::endl; } @@ -224,6 +124,11 @@ namespace rt::objects std::cout << "Deallocate: " << get_name() << std::endl; } + size_t get_rc() + { + return rc; + } + /// @brief The string representation of this value to /// TODO remove virtual once we have primitive functions. virtual std::string get_name() @@ -333,127 +238,17 @@ namespace rt::objects return prototype; } - static void add_reference(DynObject* src, DynObject* target) - { - if (target == nullptr) - return; - - target->change_rc(1); - - auto src_region = get_region(src); - add_region_reference(src_region, target); - } - - static void - remove_reference(DynObject* src_initial, DynObject* old_dst_initial) - { - visit( - {src_initial, "", old_dst_initial}, - [&](Edge e) { - if (e.target == nullptr) - return false; - - std::cout << "Remove reference from: " << e.src->get_name() << " to " - << e.target->get_name() << std::endl; - bool result = e.target->change_rc(-1) == 0; - - remove_region_reference(get_region(e.src), get_region(e.target)); - return result; - }, - [&](DynObject* obj) { delete obj; }); - - Region::collect(); - } - - static void - move_reference(DynObject* src, DynObject* dst, DynObject* target) - { - if (target == nullptr || target->is_immutable()) - return; - - auto src_region = get_region(src); - auto dst_region = get_region(dst); - - if (src_region == dst_region) - return; - - auto target_region = get_region(target); - - add_region_reference(dst_region, target); - // Note that target_region and get_region(target) are not necessarily the - // same. - remove_region_reference(src_region, target_region); - } - - void create_region() - { - Region* r = new Region(); - add_to_region(r); - // Add root reference as external. - r->local_reference_count++; - } - static size_t get_count() { return count; } - static Region* get_local_region() - { - return local_region; - } - static std::set get_objects() { return all_objects; } }; - inline void destruct(DynObject* obj) - { - // Called from the region destructor. - // Remove all references to other objects. - // If in the same region, then just remove the RC, but don't try to collect - // as the whole region is being torndown including any potential cycles. - auto same_region = [](DynObject* src, DynObject* target) { - return DynObject::get_region(src) == DynObject::get_region(target); - }; - for (auto& [key, field] : obj->fields) - { - if (field == nullptr) - continue; - if (same_region(obj, field)) - { - // Same region just remove the rc, but don't try to collect. - field->change_rc(-1); - continue; - } - - auto old_value = obj->set(key, nullptr); - remove_reference(obj, old_value); - } - - if (same_region(obj, obj->prototype)) - { - obj->prototype->change_rc(-1); - } - else - { - auto old_value = obj->set_prototype(nullptr); - remove_reference(obj, old_value); - } - } - - inline void dealloc(DynObject* obj) - { - // Called from the region destructor. - // So remove from region if in one. - // This ensures we don't try to remove it from the set that is being - // iterated. - obj->region = nullptr; - delete obj; - } - template inline void visit(Edge e, Pre pre, Post post) { diff --git a/src/rt/objects/prototype_object.h b/src/rt/objects/prototype_object.h new file mode 100644 index 0000000..79f1731 --- /dev/null +++ b/src/rt/objects/prototype_object.h @@ -0,0 +1,22 @@ + +#include "dyn_object.h" + +namespace rt::objects +{ + class PrototypeObject : public objects::DynObject + { + std::string name; + + public: + PrototypeObject(std::string name_, objects::DynObject* prototype = nullptr) + : objects::DynObject(prototype), name(name_) + {} + + std::string get_name() + { + std::stringstream stream; + stream << "[" << name << "]"; + return stream.str(); + } + }; +} \ No newline at end of file diff --git a/src/rt/objects/region.cc b/src/rt/objects/region.cc new file mode 100644 index 0000000..5e6f58c --- /dev/null +++ b/src/rt/objects/region.cc @@ -0,0 +1,222 @@ +#include "dyn_object.h" + +namespace rt::objects +{ + Region* get_region(DynObject* obj) + { + if ((obj == nullptr) || obj->is_immutable()) + return nullptr; + return obj->region.get_ptr(); + } + + thread_local objects::RegionPointer local_region = new Region(); + + Region* get_local_region() + { + return local_region; + } + + void set_local_region(Region* region) + { + local_region = region; + } + + void add_to_region(Region* r, DynObject* target) + { + size_t internal_references{0}; + size_t rc_of_added_objects{0}; + + visit(target, [&](Edge e) { + auto obj = e.target; + if (obj == nullptr || obj->is_immutable()) + return false; + + if (obj->region.get_ptr() == get_local_region()) + { + std::cout << "Adding object to region: " << obj->get_name() + << " rc = " << obj->get_rc() << std::endl; + rc_of_added_objects += obj->get_rc(); + internal_references++; + obj->region = {r}; + get_local_region()->objects.erase(obj); + r->objects.insert(obj); + return true; + } + + auto obj_region = get_region(obj); + if (obj_region == r) + { + std::cout << "Adding internal reference to object: " << obj->get_name() + << std::endl; + internal_references++; + return false; + } + + Region::set_parent(obj_region, r); + Region::dec_lrc(obj_region); + return false; + }); + + r->local_reference_count += rc_of_added_objects - internal_references; + + std::cout << "Added " << rc_of_added_objects - internal_references + << " to LRC of region" << std::endl; + std::cout << "Region LRC: " << r->local_reference_count << std::endl; + std::cout << "Internal references found: " << internal_references + << std::endl; + } + + void remove_region_reference(Region* src, Region* target) + { + if (src == target) + { + std::cout << "Same region, no need to do anything" << std::endl; + return; + } + + // Handle immutable case + if (target == nullptr) + return; + + if (src == get_local_region()) + { + Region::dec_lrc(target); + return; + } + + if (src) + { + assert(target->parent == src); + target->parent = nullptr; + } + return; + } + + void add_region_reference(Region* src_region, DynObject* target) + { + if (target->is_immutable()) + return; + + auto target_region = get_region(target); + if (src_region == target_region) + return; + + if (src_region == get_local_region()) + { + Region::inc_lrc(target_region); + return; + } + + if (target_region == get_local_region()) + { + add_to_region(src_region, target); + return; + } + + Region::set_parent(target_region, src_region); + } + + void add_reference(DynObject* src, DynObject* target) + { + if (target == nullptr) + return; + + target->change_rc(1); + + auto src_region = get_region(src); + add_region_reference(src_region, target); + } + + void remove_reference(DynObject* src_initial, DynObject* old_dst_initial) + { + visit( + {src_initial, "", old_dst_initial}, + [&](Edge e) { + if (e.target == nullptr) + return false; + + std::cout << "Remove reference from: " << e.src->get_name() << " to " + << e.target->get_name() << std::endl; + bool result = e.target->change_rc(-1) == 0; + + remove_region_reference(get_region(e.src), get_region(e.target)); + return result; + }, + [&](DynObject* obj) { delete obj; }); + + Region::collect(); + } + + void move_reference(DynObject* src, DynObject* dst, DynObject* target) + { + if (target == nullptr || target->is_immutable()) + return; + + auto src_region = get_region(src); + auto dst_region = get_region(dst); + + if (src_region == dst_region) + return; + + auto target_region = get_region(target); + + add_region_reference(dst_region, target); + // Note that target_region and get_region(target) are not necessarily the + // same. + remove_region_reference(src_region, target_region); + } + + void destruct(DynObject* obj) + { + // Called from the region destructor. + // Remove all references to other objects. + // If in the same region, then just remove the RC, but don't try to collect + // as the whole region is being torndown including any potential cycles. + auto same_region = [](DynObject* src, DynObject* target) { + return get_region(src) == get_region(target); + }; + for (auto& [key, field] : obj->fields) + { + if (field == nullptr) + continue; + if (same_region(obj, field)) + { + // Same region just remove the rc, but don't try to collect. + field->change_rc(-1); + continue; + } + + auto old_value = obj->set(key, nullptr); + remove_reference(obj, old_value); + } + + if (same_region(obj, obj->prototype)) + { + obj->prototype->change_rc(-1); + } + else + { + auto old_value = obj->set_prototype(nullptr); + remove_reference(obj, old_value); + } + } + + void dealloc(DynObject* obj) + { + // Called from the region destructor. + // So remove from region if in one. + // This ensures we don't try to remove it from the set that is being + // iterated. + obj->region = nullptr; + delete obj; + } + + void create_region(DynObject* obj) + { + Region* r = new Region(); + add_to_region(r, obj); + // Add root reference as external. + r->local_reference_count++; + } + +} diff --git a/src/rt/objects/region.h b/src/rt/objects/region.h index 21a87c0..1a29220 100644 --- a/src/rt/objects/region.h +++ b/src/rt/objects/region.h @@ -9,7 +9,15 @@ namespace rt::objects { class DynObject; - + struct Region; + + void add_to_region(Region* r, DynObject* target); + void remove_region_reference(Region* src, Region* target); + void add_region_reference(Region* src_region, DynObject* target); + void add_reference(DynObject* src, DynObject* target); + void remove_reference(DynObject* src_initial, DynObject* old_dst_initial); + void move_reference(DynObject* src, DynObject* dst, DynObject* target); + void create_region(DynObject* object); void destruct(DynObject* obj); void dealloc(DynObject* obj); @@ -27,11 +35,6 @@ namespace rt::objects // This guarantees that the regions for trees. Region* parent{nullptr}; - // The parent reference count is the number of references to the region from - // the parent region. In classic Verona we considered this as 0 or 1, but - // by tracking dynamically we can allow multiple references. - size_t parent_reference_count{0}; - // The number of direct subregions, whose LRC is non-zero size_t sub_region_reference_count{0}; @@ -99,40 +102,10 @@ namespace rt::objects } } - static void dec_prc(Region* r) - { - std::cout << "Dropping parent reference: " << r << std::endl; - assert(r->parent_reference_count != 0); - r->parent_reference_count--; - if (r->parent_reference_count != 0) - return; - - // This is the last parent reference, so region no longer has a parent. - // If it has internal references, then we need to decrement the parents - // local reference count. - if (r->combined_lrc() != 0) - dec_sbrc(r); - else - { - std::cout << "Collecting region: " << r << std::endl; - to_collect.push_back(r); - } - // Unset parent pointer. - r->parent = nullptr; - } - static void set_parent(Region* r, Region* p) { assert(r->local_reference_count != 0); - r->parent_reference_count++; - - // Check if already parented, if so increment the parent reference count. - if (r->parent == p) - { - return; - } - // Check if already parented to another region. if (r->parent != nullptr) ui::error( @@ -149,7 +122,6 @@ namespace rt::objects // Set the parent and increment the parent reference count. r->parent = p; - assert(r->parent_reference_count == 1); // If the sub-region has local references, then we need the parent to have // a local reference to. diff --git a/src/rt/rt.cc b/src/rt/rt.cc index 475eb8a..51edf53 100644 --- a/src/rt/rt.cc +++ b/src/rt/rt.cc @@ -60,9 +60,6 @@ namespace rt return new core::CownObject(region); } - thread_local objects::RegionPointer objects::DynObject::local_region = - new Region(); - void freeze(objects::DynObject* obj) { // Cown specific handling of the freeze operation is handled by the @@ -72,7 +69,7 @@ namespace rt void create_region(objects::DynObject* object) { - object->create_region(); + objects::create_region(object); } objects::DynObject* get(objects::DynObject* obj, std::string key) @@ -151,12 +148,12 @@ namespace rt void add_reference(objects::DynObject* src, objects::DynObject* target) { - objects::DynObject::add_reference(src, target); + objects::add_reference(src, target); } void remove_reference(objects::DynObject* src, objects::DynObject* target) { - objects::DynObject::remove_reference(src, target); + objects::remove_reference(src, target); } void move_reference( @@ -164,7 +161,7 @@ namespace rt objects::DynObject* dst, objects::DynObject* target) { - objects::DynObject::move_reference(src, dst, target); + objects::move_reference(src, dst, target); } size_t pre_run(ui::UI* ui) @@ -194,7 +191,7 @@ namespace rt if (objects::DynObject::get_count() != initial_count) { std::cout << "Cycles detected in local region." << std::endl; - auto roots = objects::DynObject::get_local_region()->get_objects(); + auto roots = objects::get_local_region()->get_objects(); roots.erase( std::remove_if( roots.begin(), @@ -211,7 +208,7 @@ namespace rt obj->freeze(); } - objects::DynObject::get_local_region()->terminate_region(); + objects::get_local_region()->terminate_region(); if (objects::DynObject::get_count() != initial_count) { std::cout << "Memory leak detected!" << std::endl; diff --git a/src/rt/ui/mermaid.cc b/src/rt/ui/mermaid.cc index 92911ee..ca162f1 100644 --- a/src/rt/ui/mermaid.cc +++ b/src/rt/ui/mermaid.cc @@ -101,7 +101,7 @@ namespace rt::ui out << " ]" << (unreachable ? ":::unreachable" : "") << std::endl; - auto region = objects::DynObject::get_region(dst); + auto region = objects::get_region(dst); if (region != nullptr) { region_strings[region].push_back(curr_id); @@ -143,7 +143,7 @@ namespace rt::ui { out << "subgraph "; - if (region == objects::DynObject::get_local_region()) + if (region == objects::get_local_region()) { out << "local region" << std::endl; } @@ -152,8 +152,7 @@ namespace rt::ui out << std::endl; out << " region" << region << "[\\" << region << "
lrc=" << region->local_reference_count - << "
sbrc=" << region->sub_region_reference_count - << "
prc=" << region->parent_reference_count << "/]" + << "
sbrc=" << region->sub_region_reference_count << "/]" << std::endl; } for (auto obj : objects)