#pragma once #include "spin-mutex.h" #include #include #include #include namespace effil { // Unique handle for all objects spawned from one object. typedef void* GCObjectHandle; static const GCObjectHandle GCNull = nullptr; // All effil objects that owned in lua code have to inherit this class. // This type o object can persist in multiple threads and in multiple lua states. // Child has to care about storing data and concurrent access. class GCObject { public: GCObject() noexcept : refs_(new std::set) {} GCObject(const GCObject& init) = default; GCObject(GCObject&& init) = default; GCObject& operator=(const GCObject& init) = default; virtual ~GCObject() = default; GCObjectHandle handle() const noexcept { return reinterpret_cast(refs_.get()); } size_t instances() const noexcept { return refs_.use_count(); } const std::set& refers() const { return *refs_; } protected: std::shared_ptr> refs_; }; class GC { public: GC(); ~GC() = default; // This method is used to create all managed objects. template ObjectType create(Args&&... args) { if (enabled_ && lastCleanup_.fetch_add(1) == step_) collect(); auto object = std::make_shared(std::forward(args)...); std::lock_guard g(lock_); objects_[object->handle()] = object; return *object; } template ObjectType get(GCObjectHandle handle) { std::lock_guard g(lock_); // TODO: add dynamic cast to check? return *static_cast(findObject(handle)); } bool has(GCObjectHandle handle) const; void collect(); size_t size() const; void pause() { enabled_ = false; }; void resume() { enabled_ = true; }; size_t step() const { return step_; } void step(size_t newStep) { step_ = newStep; } bool enabled() { return enabled_; }; size_t count(); static GC& instance(); static sol::table getLuaApi(sol::state_view& lua); private: mutable std::mutex lock_; bool enabled_; std::atomic lastCleanup_; size_t step_; std::map> objects_; private: GCObject* findObject(GCObjectHandle handle); private: GC(GC&&) = delete; GC(const GC&) = delete; }; } // effil