#include "iwa/object.hpp" #if IWA_OBJECTPTR_TRACKING #include #endif #include #include #include namespace iwa::impl { namespace { std::atomic gNextObjectId; std::unordered_map gAllObjects; std::shared_mutex gAllObjectMutex; std::unordered_map gDestructionHandlers; std::shared_mutex gDestructionHandlersMutex; #if IWA_OBJECTPTR_TRACKING std::list gObjectPtrAllocations; std::mutex gObjectPtrAllocationsMutex; struct ObjectPtrAllocationsChecker { ~ObjectPtrAllocationsChecker() { const std::unique_lock lock(gObjectPtrAllocationsMutex); if (!gObjectPtrAllocations.empty()) { std::cerr << "ObjectPtrs pending deletion:\n"; for (const ObjectPtrAllocation& allocation : gObjectPtrAllocations) { std::cerr << typeid(*allocation.object).name() /* << "@" << allocation.stacktrace */ <<"\n"; #if IWA_OBJECTPTR_TRACKING > 1 if (allocation.stacktrace.isSuccess()) { std::cerr << *allocation.stacktrace << "\n"; } #endif } // did you call instance->requestQuit() and then instance->getMainTaskLoop().runUntilDone()? std::cerr.flush(); MIJIN_TRAP(); } } } gObjectPtrAllocationsChecker; #endif // IWA_OBJECTPTR_TRACKING } #if IWA_OBJECTPTR_TRACKING #if IWA_OBJECTPTR_TRACKING > 1 objectptr_allocation_handle_t trackObjectPtr(BaseObject* object, mijin::Result&& stacktrace) noexcept { const std::unique_lock lock(gObjectPtrAllocationsMutex); gObjectPtrAllocations.emplace_back(object, std::move(stacktrace)); return std::prev(gObjectPtrAllocations.end()); } #else objectptr_allocation_handle_t trackObjectPtr(BaseObject* object) noexcept { const std::unique_lock lock(gObjectPtrAllocationsMutex); gObjectPtrAllocations.emplace_back(object); return std::prev(gObjectPtrAllocations.end()); } #endif void untrackObjectPtr(objectptr_allocation_handle_t handle) noexcept { const std::unique_lock lock(gObjectPtrAllocationsMutex); gObjectPtrAllocations.erase(*handle); // NOLINT(bugprone-unchecked-optional-access) } #endif // IWA_OBJECTPTR_TRACKING object_id_t nextObjectId() noexcept { return ++gNextObjectId; } void registerObject(BaseObject* object) noexcept { const std::unique_lock lock(gAllObjectMutex); MIJIN_ASSERT(gAllObjects.find(object->getId()) == gAllObjects.end(), "Duplicate object id!"); gAllObjects.emplace(object->getId(), object); } void unregisterObject(BaseObject* object) noexcept { { const std::unique_lock lock(gAllObjectMutex); gAllObjects.erase(object->getId()); } std::unordered_map::iterator itHandler; { const std::shared_lock readLock(gDestructionHandlersMutex); itHandler = gDestructionHandlers.find(object->getId()); } if (itHandler != gDestructionHandlers.end()) { object_destruction_handler_t handler; { const std::unique_lock writeLock(gDestructionHandlersMutex); handler = std::move(itHandler->second); gDestructionHandlers.erase(itHandler); } handler(); } } ObjectPtr getRegisteredObject(object_id_t objectId) noexcept { const std::shared_lock lock(gAllObjectMutex); auto it = gAllObjects.find(objectId); if (it != gAllObjects.end()) { return it->second->getPointer(); } return {}; } } // namespace iwa::impl namespace iwa { void registerObjectDestructionHandler(const BaseObject& object, object_destruction_handler_t handler) noexcept { const std::unique_lock writeLock(impl::gDestructionHandlersMutex); impl::gDestructionHandlers.emplace(object.getId(), std::move(handler)); } // namespace iwa }