From: Mikko Rasa Date: Mon, 28 Nov 2022 12:08:29 +0000 (+0200) Subject: Enforce no creation or destruction of objects during tick X-Git-Url: http://git.tdb.fi/?a=commitdiff_plain;h=48051ee2bab13f65e48c371e453b9ea65920921e;p=libs%2Fgame.git Enforce no creation or destruction of objects during tick --- diff --git a/source/game/accessguard.cpp b/source/game/accessguard.cpp new file mode 100644 index 0000000..d4c2113 --- /dev/null +++ b/source/game/accessguard.cpp @@ -0,0 +1,61 @@ +#include "accessguard.h" + +namespace Msp::Game { + +thread_local AccessGuard *AccessGuard::instance = nullptr; + +AccessGuard::AccessGuard() +{ + if(!instance) + instance = this; +} + +AccessGuard::AccessGuard(AccessGuard &&other): + flags(std::move(other.flags)) +{ + if(&other==instance) + instance = this; +} + +AccessGuard &AccessGuard::operator=(AccessGuard &&other) +{ + flags = std::move(other.flags); + if(&other==instance) + instance = this; + return *this; +} + +AccessGuard::~AccessGuard() +{ + if(this==instance) + instance = nullptr; +} + +AccessGuard &AccessGuard::get_instance() +{ + if(!instance) + throw std::logic_error("no AccessGuard instance"); + return *instance; +} + +void AccessGuard::block_all() +{ + default_flag = BLOCKED; + for(uint8_t &f: flags) + f = BLOCKED; +} + +void AccessGuard::unblock_all() +{ + default_flag = UNBLOCKED; + for(uint8_t &f: flags) + f = UNBLOCKED; +} + +unsigned AccessGuard::get_next_index() +{ + static unsigned next_index = 0; + return next_index++; +} + +} // namespace Msp::Game diff --git a/source/game/accessguard.h b/source/game/accessguard.h new file mode 100644 index 0000000..c64efe7 --- /dev/null +++ b/source/game/accessguard.h @@ -0,0 +1,99 @@ +#ifndef MSP_GAME_ACCESSGUARD_H_ +#define MSP_GAME_ACCESSGUARD_H_ + +#include +#include +#include +#include +#include + +namespace Msp::Game { + +class invalid_access: public std::logic_error +{ +public: + invalid_access(const std::string &w): logic_error(w) { } +}; + + +class AccessGuard +{ +public: + struct Create { static std::string describe() { return "create"; } }; + struct Destroy { static std::string describe() { return "destroy"; } }; + + template + struct BlockForScope: NonCopyable + { + BlockForScope() { get_instance().block(); } + ~BlockForScope() { get_instance().unblock(); } + }; + +private: + const uint8_t UNBLOCKED = 0; + const uint8_t BLOCKED = 1; + + std::uint8_t default_flag = UNBLOCKED; + std::vector flags; + + static thread_local AccessGuard *instance; + +public: + AccessGuard(); + AccessGuard(AccessGuard &&); + AccessGuard &operator=(AccessGuard &&); + ~AccessGuard(); + + static AccessGuard &get_instance(); + + void block_all(); + void unblock_all(); + +private: + static unsigned get_next_index(); + + template + static unsigned get_index(); + + template + std::uint8_t &get(); + +public: + template + void block() { get() = BLOCKED; } + + template + void unblock() { get() = UNBLOCKED; } + + template + void check() { if(get()!=UNBLOCKED) throw invalid_access(T::describe()); } +}; + + +template +inline unsigned AccessGuard::get_index() +{ + static unsigned index = get_next_index(); + return index; +} + +template +inline std::uint8_t &AccessGuard::get() +{ + unsigned index = get_index(); + if(flags.size()<=index) + flags.resize(index+1, default_flag); + return flags[index]; +} + +template<> +inline void AccessGuard::block() +{ block_all(); } + +template<> +inline void AccessGuard::unblock() +{ unblock_all(); } + +} // namespace Msp::Game + +#endif diff --git a/source/game/director.cpp b/source/game/director.cpp index b06dff4..4a4c126 100644 --- a/source/game/director.cpp +++ b/source/game/director.cpp @@ -10,7 +10,11 @@ namespace Msp::Game { Director::Director(DataFile::Collection &r): resources(r), event_source(event_bus) -{ } +{ +#if DEBUG + access_guard.emplace(); +#endif +} // Hide ~unique_ptr from the header Director::~Director() diff --git a/source/game/director.h b/source/game/director.h index de19185..1be3e8f 100644 --- a/source/game/director.h +++ b/source/game/director.h @@ -2,10 +2,12 @@ #define MSP_GAME_DIRECTOR_H_ #include +#include #include #include #include #include +#include "accessguard.h" #include "eventbus.h" #include "events.h" #include "eventsource.h" @@ -20,6 +22,7 @@ public: using EventSource = Game::EventSource; private: + std::optional access_guard; DataFile::Collection &resources; EventBus event_bus; EventSource event_source; diff --git a/source/game/owned.h b/source/game/owned.h index 7159c7d..fc2acab 100644 --- a/source/game/owned.h +++ b/source/game/owned.h @@ -2,6 +2,7 @@ #define MSP_GAME_OWNED_H_ #include +#include "accessguard.h" #include "events.h" #include "handle.h" #include "stage.h" @@ -43,6 +44,10 @@ template template Owned::Owned(Handle

parent, Args &&... args) { +#ifdef DEBUG + AccessGuard::get_instance().check(); +#endif + if(!parent) throw std::invalid_argument("Owned::Owned"); @@ -89,6 +94,10 @@ void Owned::destroy() if(!obj) return; +#ifdef DEBUG + AccessGuard::get_instance().check(); +#endif + Stage &stage = get_stage(*obj); if constexpr(std::is_base_of_v) diff --git a/source/game/stage.cpp b/source/game/stage.cpp index 81893e1..bdd0ed6 100644 --- a/source/game/stage.cpp +++ b/source/game/stage.cpp @@ -1,4 +1,5 @@ #include "stage.h" +#include "accessguard.h" #include "camera.h" #include "root.h" #include "system.h" @@ -53,8 +54,14 @@ void Stage::synthesize_initial_events(Handle entity, EventObserver &targ void Stage::tick(Time::TimeDelta dt) { - for(const auto &s: systems) - s->tick(dt); + { +#ifdef DEBUG + AccessGuard::BlockForScope _block;; +#endif + for(const auto &s: systems) + s->tick(dt); + } + for(const auto &s: systems) s->deferred_tick(); }