diff --git a/src/creatures/combat/condition.cpp b/src/creatures/combat/condition.cpp index 4d4796ec2d5..1da6d02f8ad 100644 --- a/src/creatures/combat/condition.cpp +++ b/src/creatures/combat/condition.cpp @@ -216,41 +216,41 @@ std::shared_ptr Condition::createCondition(ConditionId_t id, Conditio case CONDITION_DAZZLED: case CONDITION_CURSED: case CONDITION_BLEEDING: - return ObjectPool::allocateShared(id, type, buff, subId); + return ObjectPool::acquireObject(id, type, buff, subId); case CONDITION_HASTE: case CONDITION_PARALYZE: - return ObjectPool::allocateShared(id, type, ticks, buff, subId, param); + return ObjectPool::acquireObject(id, type, ticks, buff, subId, param); case CONDITION_INVISIBLE: - return ObjectPool::allocateShared(id, type, ticks, buff, subId); + return ObjectPool::acquireObject(id, type, ticks, buff, subId); case CONDITION_OUTFIT: - return ObjectPool::allocateShared(id, type, ticks, buff, subId); + return ObjectPool::acquireObject(id, type, ticks, buff, subId); case CONDITION_LIGHT: - return ObjectPool::allocateShared(id, type, ticks, buff, subId, param & 0xFF, (param & 0xFF00) >> 8); + return ObjectPool::acquireObject(id, type, ticks, buff, subId, param & 0xFF, (param & 0xFF00) >> 8); case CONDITION_REGENERATION: - return ObjectPool::allocateShared(id, type, ticks, buff, subId); + return ObjectPool::acquireObject(id, type, ticks, buff, subId); case CONDITION_SOUL: - return ObjectPool::allocateShared(id, type, ticks, buff, subId); + return ObjectPool::acquireObject(id, type, ticks, buff, subId); case CONDITION_ATTRIBUTES: - return ObjectPool::allocateShared(id, type, ticks, buff, subId); + return ObjectPool::acquireObject(id, type, ticks, buff, subId); case CONDITION_SPELLCOOLDOWN: - return ObjectPool::allocateShared(id, type, ticks, buff, subId); + return ObjectPool::acquireObject(id, type, ticks, buff, subId); case CONDITION_SPELLGROUPCOOLDOWN: - return ObjectPool::allocateShared(id, type, ticks, buff, subId); + return ObjectPool::acquireObject(id, type, ticks, buff, subId); case CONDITION_MANASHIELD: - return ObjectPool::allocateShared(id, type, ticks, buff, subId); + return ObjectPool::acquireObject(id, type, ticks, buff, subId); case CONDITION_FEARED: - return ObjectPool::allocateShared(id, type, ticks, buff, subId); + return ObjectPool::acquireObject(id, type, ticks, buff, subId); case CONDITION_ROOTED: case CONDITION_INFIGHT: @@ -262,13 +262,13 @@ std::shared_ptr Condition::createCondition(ConditionId_t id, Conditio case CONDITION_CHANNELMUTEDTICKS: case CONDITION_YELLTICKS: case CONDITION_PACIFIED: - return ObjectPool::allocateShared(id, type, ticks, buff, subId); + return ObjectPool::acquireObject(id, type, ticks, buff, subId); case CONDITION_BAKRAGORE: - return ObjectPool::allocateShared(id, type, ticks, buff, subId, isPersistent); + return ObjectPool::acquireObject(id, type, ticks, buff, subId, isPersistent); case CONDITION_GOSHNARTAINT: - return ObjectPool::allocateShared(id, type, ticks, buff, subId); + return ObjectPool::acquireObject(id, type, ticks, buff, subId); default: return nullptr; diff --git a/src/utils/object_pool.hpp b/src/utils/object_pool.hpp index c37d1d2efeb..4ba9c264fe2 100644 --- a/src/utils/object_pool.hpp +++ b/src/utils/object_pool.hpp @@ -9,76 +9,89 @@ #pragma once -#include "utils/lockfree.hpp" +#include +#include +#include /** - * @brief A lock-free object pool for efficient memory allocation and reuse. + * @brief A thread-safe object pool for efficient memory allocation and reuse. * * This class provides an efficient mechanism for managing the allocation * and deallocation of objects, reducing the overhead associated with - * frequent memory operations. It uses a lock-free structure to ensure - * thread safety and high performance in multithreaded environments. + * frequent memory operations. The pool dynamically grows as needed and + * reuses objects to minimize heap allocations. * * @tparam T The type of objects managed by the pool. - * @tparam CAPACITY The maximum number of objects that can be held in the pool. */ -template +template class ObjectPool { public: /** - * @brief The allocator type used for managing object memory. + * @brief Alias for the smart pointer type used by the pool. */ - using Allocator = LockfreePoolingAllocator; + using Ptr = std::shared_ptr; /** - * @brief Allocates an object from the pool and returns it as a `std::shared_ptr`. + * @brief Acquires an object from the pool or creates a new one if the pool is empty. * * The object is constructed in place using the provided arguments. * The `std::shared_ptr` includes a custom deleter that returns the object * to the pool when it is no longer needed. * - * @tparam Args The types of the arguments used to construct the object.* @param args The arguments forwarded to the constructor of the object. - * @return A `std::shared_ptr` managing the allocated object, or `nullptr` if the pool is empty. + * @tparam Args The types of the arguments used to construct the object. + * @param args The arguments forwarded to the constructor of the object. + * @return A `std::shared_ptr` managing the allocated object. */ template - static std::shared_ptr allocateShared(Args &&... args) { - T* obj = allocator.allocate(1); - if (obj) { - // Construct the object in place - std::construct_at(obj, std::forward(args)...); + static Ptr acquireObject(Args &&... args) { + std::lock_guard lock(mutex_); + if (!pool_.empty()) { + T* obj = pool_.back(); + pool_.pop_back(); - // Return a shared_ptr with a custom deleter - return std::shared_ptr(obj, [](T* ptr) { - std::destroy_at(ptr); // Destroy the object - allocator.deallocate(ptr, 1); // Return to the pool + // Construct the object in place with new arguments + new (obj) T(std::forward(args)...); + return Ptr(obj, [](T* ptr) { + ptr->~T(); // Destroy the object + releaseObject(ptr); // Return to the pool }); } - // Return nullptr if the pool is empty - return nullptr; - } - static void clear() { - allocator.clear(); + // Allocate a new object if the pool is empty + T* rawPtr = allocator_.allocate(1); + new (rawPtr) T(std::forward(args)...); + return Ptr(rawPtr, [](T* ptr) { + ptr->~T(); // Destroy the object + releaseObject(ptr); // Return to the pool + }); } +private: /** - * @brief Preallocates a specified number of objects in the pool. + * @brief Returns an object to the pool for future reuse. * - * This method allows you to populate the pool with preallocated objects - * to improve performance by reducing the need for dynamic allocations at runtime. + * This method is called automatically by the custom deleter when the + * `std::shared_ptr` is destroyed. * - * @param count The number of objects to preallocate. + * @param obj The object to be returned to the pool. */ - static void preallocate(size_t count) { - LockfreeFreeList::preallocate(count); + static void releaseObject(T* obj) { + std::lock_guard lock(mutex_); + pool_.push_back(obj); } -private: /** - * @brief The allocator instance used to manage object memory. + * @brief The internal pool of objects available for reuse. */ - static Allocator allocator; -}; + static inline std::vector pool_; + + /** + * @brief The allocator instance used to allocate and deallocate objects. + */ + static inline std::allocator allocator_; -template -typename ObjectPool::Allocator ObjectPool::allocator; + /** + * @brief A mutex to ensure thread safety when accessing the pool. + */ + static inline std::mutex mutex_; +};