Skip to content

Commit

Permalink
improve: switch ObjectPool to Dynamic Allocation and update condition…
Browse files Browse the repository at this point in the history
… creation

Updated ObjectPool to support dynamic object allocation without a fixed capacity.

Modified the Condition::createCondition function to utilize the new dynamic ObjectPool.

Improved memory management and reduced dependency on predefined pool sizes.

Enhanced flexibility for object allocation and reuse.

improve: add documentation

Code format - (Clang-format)
  • Loading branch information
dudantas committed Jan 14, 2025
1 parent cb1e3d5 commit 6b0388d
Show file tree
Hide file tree
Showing 2 changed files with 65 additions and 52 deletions.
30 changes: 15 additions & 15 deletions src/creatures/combat/condition.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -216,41 +216,41 @@ std::shared_ptr<Condition> Condition::createCondition(ConditionId_t id, Conditio
case CONDITION_DAZZLED:
case CONDITION_CURSED:
case CONDITION_BLEEDING:
return ObjectPool<ConditionDamage, 1024>::allocateShared(id, type, buff, subId);
return ObjectPool<ConditionDamage>::acquireObject(id, type, buff, subId);

case CONDITION_HASTE:
case CONDITION_PARALYZE:
return ObjectPool<ConditionSpeed, 1024>::allocateShared(id, type, ticks, buff, subId, param);
return ObjectPool<ConditionSpeed>::acquireObject(id, type, ticks, buff, subId, param);

case CONDITION_INVISIBLE:
return ObjectPool<ConditionInvisible, 1024>::allocateShared(id, type, ticks, buff, subId);
return ObjectPool<ConditionInvisible>::acquireObject(id, type, ticks, buff, subId);

case CONDITION_OUTFIT:
return ObjectPool<ConditionOutfit, 1024>::allocateShared(id, type, ticks, buff, subId);
return ObjectPool<ConditionOutfit>::acquireObject(id, type, ticks, buff, subId);

case CONDITION_LIGHT:
return ObjectPool<ConditionLight, 1024>::allocateShared(id, type, ticks, buff, subId, param & 0xFF, (param & 0xFF00) >> 8);
return ObjectPool<ConditionLight>::acquireObject(id, type, ticks, buff, subId, param & 0xFF, (param & 0xFF00) >> 8);

case CONDITION_REGENERATION:
return ObjectPool<ConditionRegeneration, 1024>::allocateShared(id, type, ticks, buff, subId);
return ObjectPool<ConditionRegeneration>::acquireObject(id, type, ticks, buff, subId);

case CONDITION_SOUL:
return ObjectPool<ConditionSoul, 1024>::allocateShared(id, type, ticks, buff, subId);
return ObjectPool<ConditionSoul>::acquireObject(id, type, ticks, buff, subId);

case CONDITION_ATTRIBUTES:
return ObjectPool<ConditionAttributes, 1024>::allocateShared(id, type, ticks, buff, subId);
return ObjectPool<ConditionAttributes>::acquireObject(id, type, ticks, buff, subId);

case CONDITION_SPELLCOOLDOWN:
return ObjectPool<ConditionSpellCooldown, 1024>::allocateShared(id, type, ticks, buff, subId);
return ObjectPool<ConditionSpellCooldown>::acquireObject(id, type, ticks, buff, subId);

case CONDITION_SPELLGROUPCOOLDOWN:
return ObjectPool<ConditionSpellGroupCooldown, 1024>::allocateShared(id, type, ticks, buff, subId);
return ObjectPool<ConditionSpellGroupCooldown>::acquireObject(id, type, ticks, buff, subId);

case CONDITION_MANASHIELD:
return ObjectPool<ConditionManaShield, 1024>::allocateShared(id, type, ticks, buff, subId);
return ObjectPool<ConditionManaShield>::acquireObject(id, type, ticks, buff, subId);

case CONDITION_FEARED:
return ObjectPool<ConditionFeared, 1024>::allocateShared(id, type, ticks, buff, subId);
return ObjectPool<ConditionFeared>::acquireObject(id, type, ticks, buff, subId);

case CONDITION_ROOTED:
case CONDITION_INFIGHT:
Expand All @@ -262,13 +262,13 @@ std::shared_ptr<Condition> Condition::createCondition(ConditionId_t id, Conditio
case CONDITION_CHANNELMUTEDTICKS:
case CONDITION_YELLTICKS:
case CONDITION_PACIFIED:
return ObjectPool<ConditionGeneric, 1024>::allocateShared(id, type, ticks, buff, subId);
return ObjectPool<ConditionGeneric>::acquireObject(id, type, ticks, buff, subId);

case CONDITION_BAKRAGORE:
return ObjectPool<ConditionGeneric, 1024>::allocateShared(id, type, ticks, buff, subId, isPersistent);
return ObjectPool<ConditionGeneric>::acquireObject(id, type, ticks, buff, subId, isPersistent);

case CONDITION_GOSHNARTAINT:
return ObjectPool<ConditionGeneric, 1024>::allocateShared(id, type, ticks, buff, subId);
return ObjectPool<ConditionGeneric>::acquireObject(id, type, ticks, buff, subId);

default:
return nullptr;
Expand Down
87 changes: 50 additions & 37 deletions src/utils/object_pool.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,76 +9,89 @@

#pragma once

#include "utils/lockfree.hpp"
#include <memory>
#include <vector>
#include <mutex>

/**
* @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 <typename T, size_t CAPACITY>
template <typename T>
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<T, CAPACITY>;
using Ptr = std::shared_ptr<T>;

/**
* @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 <typename... Args>
static std::shared_ptr<T> allocateShared(Args &&... args) {
T* obj = allocator.allocate(1);
if (obj) {
// Construct the object in place
std::construct_at(obj, std::forward<Args>(args)...);
static Ptr acquireObject(Args &&... args) {
std::lock_guard<std::mutex> lock(mutex_);
if (!pool_.empty()) {
T* obj = pool_.back();
pool_.pop_back();

// Return a shared_ptr with a custom deleter
return std::shared_ptr<T>(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>(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>(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<T, CAPACITY>::preallocate(count);
static void releaseObject(T* obj) {
std::lock_guard<std::mutex> 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<T*> pool_;

/**
* @brief The allocator instance used to allocate and deallocate objects.
*/
static inline std::allocator<T> allocator_;

template <typename T, size_t CAPACITY>
typename ObjectPool<T, CAPACITY>::Allocator ObjectPool<T, CAPACITY>::allocator;
/**
* @brief A mutex to ensure thread safety when accessing the pool.
*/
static inline std::mutex mutex_;
};

0 comments on commit 6b0388d

Please sign in to comment.