Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

improve: ObjectPool to Dynamic Allocation and update condition creation #3242

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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_;
};
Loading