From 2a8b556b16c4f39843508223dd92ee2543ff1e7d Mon Sep 17 00:00:00 2001 From: BlackMATov Date: Sat, 12 Oct 2019 21:39:42 +0700 Subject: [PATCH] safe gobject destroying --- headers/enduro2d/high/_high.hpp | 7 +- headers/enduro2d/high/gobject.hpp | 49 +++------- headers/enduro2d/high/node.hpp | 2 + headers/enduro2d/high/world.hpp | 7 +- sources/enduro2d/high/gobject.cpp | 41 ++++++++ sources/enduro2d/high/node.cpp | 4 + sources/enduro2d/high/starter.cpp | 1 + sources/enduro2d/high/world.cpp | 150 ++++++++++++++++++++---------- 8 files changed, 170 insertions(+), 91 deletions(-) create mode 100644 sources/enduro2d/high/gobject.cpp diff --git a/headers/enduro2d/high/_high.hpp b/headers/enduro2d/high/_high.hpp index 2e239c2f..527e4b38 100644 --- a/headers/enduro2d/high/_high.hpp +++ b/headers/enduro2d/high/_high.hpp @@ -69,7 +69,6 @@ namespace e2d class atlas; class flipbook; - class gobject; class model; class node; class prefab; @@ -77,4 +76,10 @@ namespace e2d class sprite; class starter; class world; + + class gobject; + template < typename T > + class gcomponent; + template < typename T > + class const_gcomponent; } diff --git a/headers/enduro2d/high/gobject.hpp b/headers/enduro2d/high/gobject.hpp index 347e6319..8d931416 100644 --- a/headers/enduro2d/high/gobject.hpp +++ b/headers/enduro2d/high/gobject.hpp @@ -8,17 +8,6 @@ #include "_high.hpp" -namespace e2d -{ - class gobject; - - template < typename T > - class gcomponent; - - template < typename T > - class const_gcomponent; -} - namespace e2d { class gobject final { @@ -33,6 +22,8 @@ namespace e2d bool valid() const noexcept; explicit operator bool() const noexcept; + void destroy() noexcept; + template < typename T > gcomponent component() noexcept; @@ -110,31 +101,23 @@ namespace e2d namespace e2d { + class gobject_destroying_state_ilist_tag; + using gobject_destroying_states = intrusive_list< + gobject::state, + gobject_destroying_state_ilist_tag>; + class gobject::state : private noncopyable - , public ref_counter { + , public ref_counter + , public intrusive_list_hook { public: virtual ~state() noexcept {} - virtual bool valid() const noexcept = 0; + virtual void destroy() noexcept = 0; + virtual bool invalided() const noexcept = 0; virtual ecs::entity raw_entity() noexcept = 0; virtual ecs::const_entity raw_entity() const noexcept = 0; }; - inline const gobject::state_iptr& gobject::internal_state() const noexcept { - return state_; - } - - inline gobject::gobject(state_iptr state) - : state_(std::move(state)) {} - - inline bool gobject::valid() const noexcept { - return state_ && state_->valid(); - } - - inline gobject::operator bool() const noexcept { - return valid(); - } - template < typename T > gcomponent gobject::component() noexcept { return gcomponent(*this); @@ -145,16 +128,6 @@ namespace e2d return const_gcomponent(*this); } - inline ecs::entity gobject::raw_entity() noexcept { - E2D_ASSERT(valid()); - return state_->raw_entity(); - } - - inline ecs::const_entity gobject::raw_entity() const noexcept { - E2D_ASSERT(valid()); - return state_->raw_entity(); - } - template < typename T > ecs::component gobject::raw_component() noexcept { E2D_ASSERT(valid()); diff --git a/headers/enduro2d/high/node.hpp b/headers/enduro2d/high/node.hpp index fbc3b63a..9a6de45e 100644 --- a/headers/enduro2d/high/node.hpp +++ b/headers/enduro2d/high/node.hpp @@ -42,6 +42,8 @@ namespace e2d static node_iptr create(gobject owner, const node_iptr& parent, const t3f& transform); void owner(gobject owner) noexcept; + + gobject& owner() noexcept; const gobject& owner() const noexcept; void transform(const t3f& transform) noexcept; diff --git a/headers/enduro2d/high/world.hpp b/headers/enduro2d/high/world.hpp index e1811fbe..41a90873 100644 --- a/headers/enduro2d/high/world.hpp +++ b/headers/enduro2d/high/world.hpp @@ -51,12 +51,17 @@ namespace e2d gobject instantiate(const prefab& prefab, const gobject& parent, const t3f& transform); gobject instantiate(const prefab& prefab, const node_iptr& parent, const t3f& transform); - void destroy_instance(const gobject& inst) noexcept; + void destroy_instance(gobject& inst) noexcept; + void finalize_instances() noexcept; gobject resolve(ecs::entity_id ent) const noexcept; gobject resolve(const ecs::const_entity& ent) const noexcept; + private: + void destroy_instances_() noexcept; + void finalize_instance_(gobject& inst) noexcept; private: ecs::registry registry_; hash_map gobjects_; + gobject_destroying_states destroying_states_; }; } diff --git a/sources/enduro2d/high/gobject.cpp b/sources/enduro2d/high/gobject.cpp new file mode 100644 index 00000000..a1d9a87a --- /dev/null +++ b/sources/enduro2d/high/gobject.cpp @@ -0,0 +1,41 @@ +/******************************************************************************* +* This file is part of the "Enduro2D" +* For conditions of distribution and use, see copyright notice in LICENSE.md +* Copyright (C) 2018-2019, by Matvey Cherevko (blackmatov@gmail.com) +******************************************************************************/ + +#include + +namespace e2d +{ + const gobject::state_iptr& gobject::internal_state() const noexcept { + return state_; + } + + gobject::gobject(state_iptr state) + : state_(std::move(state)) {} + + bool gobject::valid() const noexcept { + return state_ && !state_->invalided(); + } + + gobject::operator bool() const noexcept { + return valid(); + } + + void gobject::destroy() noexcept { + if ( valid() ) { + state_->destroy(); + } + } + + ecs::entity gobject::raw_entity() noexcept { + E2D_ASSERT(valid()); + return state_->raw_entity(); + } + + ecs::const_entity gobject::raw_entity() const noexcept { + E2D_ASSERT(valid()); + return state_->raw_entity(); + } +} diff --git a/sources/enduro2d/high/node.cpp b/sources/enduro2d/high/node.cpp index 659e4442..d72a3d78 100644 --- a/sources/enduro2d/high/node.cpp +++ b/sources/enduro2d/high/node.cpp @@ -69,6 +69,10 @@ namespace e2d owner_ = std::move(owner); } + gobject& node::owner() noexcept { + return owner_; + } + const gobject& node::owner() const noexcept { return owner_; } diff --git a/sources/enduro2d/high/starter.cpp b/sources/enduro2d/high/starter.cpp index 8cc67463..54a6d7c1 100644 --- a/sources/enduro2d/high/starter.cpp +++ b/sources/enduro2d/high/starter.cpp @@ -63,6 +63,7 @@ namespace the().registry().process_systems_in_range( world::priority_update_section_begin, world::priority_update_section_end); + the().finalize_instances(); return !the().should_close() || (application_ && !application_->on_should_close()); } diff --git a/sources/enduro2d/high/world.cpp b/sources/enduro2d/high/world.cpp index 6dd1a833..3811b9fe 100644 --- a/sources/enduro2d/high/world.cpp +++ b/sources/enduro2d/high/world.cpp @@ -13,49 +13,58 @@ namespace using namespace e2d; class gobject_state final : public gobject::state { + private: + enum flag_masks : u32 { + fm_invalided = 1u << 0, + fm_destroying = 1u << 1, + }; public: - gobject_state(ecs::registry& registry) - : entity_(registry.create_entity()) {} + gobject_state(world& w, ecs::entity e) + : world_(w) + , entity_(std::move(e)) {} - gobject_state(ecs::registry& registry, const ecs::prototype& proto) - : entity_(registry.create_entity(proto)) {} - - ~gobject_state() final { - destroy(); + void mark_invalided() noexcept { + math::set_flags_inplace(flags_, fm_invalided); } - void destroy() noexcept { - if ( valid() ) { - entity_.destroy(); - valid_ = false; - } + void mark_destroying() noexcept { + math::set_flags_inplace(flags_, fm_destroying); } - bool valid() const noexcept final { - return valid_; + bool destroying() const noexcept { + return math::check_any_flags(flags_, fm_destroying); + } + public: + void destroy() noexcept final { + gobject go{this}; + world_.destroy_instance(go); + } + + bool invalided() const noexcept final { + return math::check_any_flags(flags_, fm_invalided); } ecs::entity raw_entity() noexcept final { - E2D_ASSERT(valid()); + E2D_ASSERT(!invalided()); return entity_; } ecs::const_entity raw_entity() const noexcept final { - E2D_ASSERT(valid()); + E2D_ASSERT(!invalided()); return entity_; } private: - bool valid_{true}; + world& world_; ecs::entity entity_; + u32 flags_{0u}; }; } namespace e2d { world::~world() noexcept { - while ( !gobjects_.empty() ) { - destroy_instance(gobjects_.begin()->second); - } + destroy_instances_(); + finalize_instances(); } ecs::registry& world::registry() noexcept { @@ -67,7 +76,9 @@ namespace e2d } gobject world::instantiate() { - gobject inst{make_intrusive(registry_)}; + gobject inst{make_intrusive( + *this, + registry_.create_entity())}; gobjects_.emplace(inst.raw_entity().id(), inst); try { @@ -78,7 +89,7 @@ namespace e2d } inst_a.assign(n); } catch (...) { - destroy_instance(inst); + finalize_instance_(inst); throw; } @@ -86,7 +97,9 @@ namespace e2d } gobject world::instantiate(const prefab& prefab) { - gobject inst{make_intrusive(registry_, prefab.prototype())}; + gobject inst{make_intrusive( + *this, + registry_.create_entity(prefab.prototype()))}; gobjects_.emplace(inst.raw_entity().id(), inst); try { @@ -97,7 +110,7 @@ namespace e2d } inst_a.assign(n); } catch (...) { - destroy_instance(inst); + finalize_instance_(inst); throw; } @@ -109,12 +122,12 @@ namespace e2d gcomponent child_a{child}; inst_a->node()->add_child(child_a->node()); } catch (...) { - destroy_instance(child); + finalize_instance_(child); throw; } } } catch (...) { - destroy_instance(inst); + finalize_instance_(inst); throw; } @@ -124,14 +137,19 @@ namespace e2d gobject world::instantiate(const gobject& parent) { gobject go = instantiate(); if ( go && parent ) { - gcomponent parent_a{parent}; - if ( !parent_a ) { - parent_a.assign(node::create(parent)); + try { + gcomponent parent_a{parent}; + if ( !parent_a ) { + parent_a.assign(node::create(parent)); + } + if ( !parent_a->node() ) { + parent_a->node(node::create(parent)); + } + parent_a->node()->add_child(gcomponent{go}->node()); + } catch (...) { + finalize_instance_(go); + throw; } - if ( !parent_a->node() ) { - parent_a->node(node::create(parent)); - } - parent_a->node()->add_child(gcomponent{go}->node()); } return go; } @@ -147,14 +165,19 @@ namespace e2d gobject world::instantiate(const prefab& prefab, const gobject& parent) { gobject go = instantiate(prefab); if ( go && parent ) { - gcomponent parent_a{parent}; - if ( !parent_a ) { - parent_a.assign(node::create(parent)); + try { + gcomponent parent_a{parent}; + if ( !parent_a ) { + parent_a.assign(node::create(parent)); + } + if ( !parent_a->node() ) { + parent_a->node(node::create(parent)); + } + parent_a->node()->add_child(gcomponent{go}->node()); + } catch (...) { + finalize_instance_(go); + throw; } - if ( !parent_a->node() ) { - parent_a->node(node::create(parent)); - } - parent_a->node()->add_child(gcomponent{go}->node()); } return go; } @@ -199,19 +222,21 @@ namespace e2d return go; } - void world::destroy_instance(const gobject& inst) noexcept { - gcomponent inst_a{inst}; - auto inst_n = inst_a ? inst_a->node() : nullptr; - - if ( inst_n ) { - inst_n->for_each_child([this](const const_node_iptr& child_n){ - destroy_instance(child_n->owner()); - }); + void world::destroy_instance(gobject& inst) noexcept { + auto gstate = inst + ? dynamic_pointer_cast(inst.internal_state()) + : nullptr; + if ( gstate && !gstate->destroying() ) { + gstate->mark_destroying(); + destroying_states_.push_back(*gstate); } + } - if ( inst ) { - gobjects_.erase(inst.raw_entity().id()); - dynamic_pointer_cast(inst.internal_state())->destroy(); + void world::finalize_instances() noexcept { + while ( !destroying_states_.empty() ) { + gobject inst{&destroying_states_.front()}; + destroying_states_.pop_front(); + finalize_instance_(inst); } } @@ -230,4 +255,27 @@ namespace e2d ? iter->second : gobject(); } + + void world::destroy_instances_() noexcept { + for ( auto& [_, go] : gobjects_ ) { + destroy_instance(go); + } + } + + void world::finalize_instance_(gobject& inst) noexcept { + gcomponent inst_a{inst}; + auto inst_n = inst_a ? inst_a->node() : nullptr; + + if ( inst_n ) { + inst_n->for_each_child([this](const node_iptr& child_n){ + destroy_instance(child_n->owner()); + }); + } + + if ( inst ) { + inst.raw_entity().destroy(); + gobjects_.erase(inst.raw_entity().id()); + dynamic_pointer_cast(inst.internal_state())->mark_invalided(); + } + } }