]> git.tdb.fi Git - libs/game.git/commitdiff
Enforce no creation or destruction of objects during tick
authorMikko Rasa <tdb@tdb.fi>
Mon, 28 Nov 2022 12:08:29 +0000 (14:08 +0200)
committerMikko Rasa <tdb@tdb.fi>
Mon, 28 Nov 2022 12:08:29 +0000 (14:08 +0200)
source/game/accessguard.cpp [new file with mode: 0644]
source/game/accessguard.h [new file with mode: 0644]
source/game/director.cpp
source/game/director.h
source/game/owned.h
source/game/stage.cpp

diff --git a/source/game/accessguard.cpp b/source/game/accessguard.cpp
new file mode 100644 (file)
index 0000000..d4c2113
--- /dev/null
@@ -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 (file)
index 0000000..c64efe7
--- /dev/null
@@ -0,0 +1,99 @@
+#ifndef MSP_GAME_ACCESSGUARD_H_
+#define MSP_GAME_ACCESSGUARD_H_
+
+#include <cstdint>
+#include <stdexcept>
+#include <string>
+#include <vector>
+#include <msp/core/noncopyable.h>
+
+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<typename T = void>
+       struct BlockForScope: NonCopyable
+       {
+               BlockForScope() { get_instance().block<T>(); }
+               ~BlockForScope() { get_instance().unblock<T>(); }
+       };
+
+private:
+       const uint8_t UNBLOCKED = 0;
+       const uint8_t BLOCKED = 1;
+
+       std::uint8_t default_flag = UNBLOCKED;
+       std::vector<std::uint8_t> 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<typename T>
+       static unsigned get_index();
+
+       template<typename T>
+       std::uint8_t &get();
+
+public:
+       template<typename T>
+       void block() { get<T>() = BLOCKED; }
+
+       template<typename T>
+       void unblock() { get<T>() = UNBLOCKED; }
+
+       template<typename T>
+       void check() { if(get<T>()!=UNBLOCKED) throw invalid_access(T::describe()); }
+};
+
+
+template<typename T>
+inline unsigned AccessGuard::get_index()
+{
+       static unsigned index = get_next_index();
+       return index;
+}
+
+template<typename T>
+inline std::uint8_t &AccessGuard::get()
+{
+       unsigned index = get_index<T>();
+       if(flags.size()<=index)
+               flags.resize(index+1, default_flag);
+       return flags[index];
+}
+
+template<>
+inline void AccessGuard::block<void>()
+{ block_all(); }
+
+template<>
+inline void AccessGuard::unblock<void>()
+{ unblock_all(); }
+
+} // namespace Msp::Game
+
+#endif
index b06dff4d4c0045c49a98840831071e7845030603..4a4c1265e82d12ba897f65f1879b8db8f6b7ac33 100644 (file)
@@ -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<Stage> from the header
 Director::~Director()
index de19185849e121e50e7c112ce59aa68a50e46fa7..1be3e8fa31bbf59c892866c436439ed7ee8c87da 100644 (file)
@@ -2,10 +2,12 @@
 #define MSP_GAME_DIRECTOR_H_
 
 #include <memory>
+#include <optional>
 #include <vector>
 #include <msp/datafile/collection.h>
 #include <msp/time/timedelta.h>
 #include <msp/time/timestamp.h>
+#include "accessguard.h"
 #include "eventbus.h"
 #include "events.h"
 #include "eventsource.h"
@@ -20,6 +22,7 @@ public:
        using EventSource = Game::EventSource<Events::StageCreated, Events::StageActivated, Events::StageDeactivated>;
 
 private:
+       std::optional<AccessGuard> access_guard;
        DataFile::Collection &resources;
        EventBus event_bus;
        EventSource event_source;
index 7159c7d9622a06fd83301b7565dc1d55c0beedcc..fc2acab6ecb02ab0d4079630dae5f5fe5b8fc939 100644 (file)
@@ -2,6 +2,7 @@
 #define MSP_GAME_OWNED_H_
 
 #include <stdexcept>
+#include "accessguard.h"
 #include "events.h"
 #include "handle.h"
 #include "stage.h"
@@ -43,6 +44,10 @@ template<typename T>
 template<typename P, typename... Args>
 Owned<T>::Owned(Handle<P> parent, Args &&... args)
 {
+#ifdef DEBUG
+       AccessGuard::get_instance().check<AccessGuard::Create>();
+#endif
+
        if(!parent)
                throw std::invalid_argument("Owned::Owned");
 
@@ -89,6 +94,10 @@ void Owned<T>::destroy()
        if(!obj)
                return;
 
+#ifdef DEBUG
+       AccessGuard::get_instance().check<AccessGuard::Destroy>();
+#endif
+
        Stage &stage = get_stage(*obj);
 
        if constexpr(std::is_base_of_v<Component, T>)
index 81893e1e1ef8c27ca72db42e456c78462a999452..bdd0ed676d27200fe87c9ee89e4231874b643f69 100644 (file)
@@ -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> 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();
 }