mirror of
https://github.com/espressif/openthread.git
synced 2026-08-13 06:07:48 +00:00
[pool] extract PoolBase and add ConfigPool (#12596)
This commit updates the object pool implementation to support dynamically configured pool sizes without penalizing the memory footprint of existing static usages. A new `PoolBase` class is introduced to encapsulate the core linked-list management logic (`Allocate()` and `Free()`), relying on a shared `mFreeList`. The existing `Pool` class is updated to inherit from `PoolBase`. It retains its statically allocated `mPool` array, ensuring zero code-size or RAM penalty for current users of fixed-size pools. A new `ConfigPool` class is added, also inheriting from `PoolBase`. This class allows a pool to be initialized at run-time with an externally provided memory buffer (`mEntryArray`) and size (`mNumEntries`). This enables future use cases where memory allocation for pools can be provided dynamically by the system integrator. This commit also includes unit tests for `ConfigPool` to verify its allocation, deallocation, and initialization behavior.
This commit is contained in:
+122
-40
@@ -54,45 +54,14 @@ class Instance;
|
||||
*/
|
||||
|
||||
/**
|
||||
* Represents an object pool.
|
||||
* Represents a base class for an object pool.
|
||||
*
|
||||
* @tparam Type The object type. Type should provide `GetNext() and `SetNext()` so that it can be added to a
|
||||
* @tparam Type The object type. Type should provide `GetNext()` and `SetNext()` so that it can be added to a
|
||||
* linked list.
|
||||
* @tparam kPoolSize Specifies the pool size (maximum number of objects in the pool).
|
||||
*/
|
||||
template <class Type, uint16_t kPoolSize> class Pool : private NonCopyable
|
||||
template <class Type> class PoolBase : private NonCopyable
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Initializes the pool.
|
||||
*/
|
||||
Pool(void)
|
||||
: mFreeList()
|
||||
{
|
||||
for (Type &entry : mPool)
|
||||
{
|
||||
mFreeList.Push(entry);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the pool.
|
||||
*
|
||||
* Version requires the `Type` class to provide method `void Init(Instance &)` to initialize
|
||||
* each `Type` entry object. This can be realized by the `Type` class inheriting from `InstanceLocatorInit()`.
|
||||
*
|
||||
* @param[in] aInstance A reference to the OpenThread instance.
|
||||
*/
|
||||
explicit Pool(Instance &aInstance)
|
||||
: mFreeList()
|
||||
{
|
||||
for (Type &entry : mPool)
|
||||
{
|
||||
entry.Init(aInstance);
|
||||
mFreeList.Push(entry);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Allocates a new object from the pool.
|
||||
*
|
||||
@@ -111,16 +80,60 @@ public:
|
||||
*/
|
||||
void Free(Type &aEntry) { mFreeList.Push(aEntry); }
|
||||
|
||||
protected:
|
||||
PoolBase(void) = default;
|
||||
|
||||
LinkedList<Type> mFreeList;
|
||||
};
|
||||
|
||||
/**
|
||||
* Represents an object pool.
|
||||
*
|
||||
* @tparam Type The object type. Type should provide `GetNext() and `SetNext()` so that it can be added to a
|
||||
* linked list.
|
||||
* @tparam kPoolSize Specifies the pool size (maximum number of objects in the pool).
|
||||
*/
|
||||
template <class Type, uint16_t kPoolSize> class Pool : public PoolBase<Type>
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Initializes the pool.
|
||||
*/
|
||||
Pool(void)
|
||||
{
|
||||
for (Type &entry : mPool)
|
||||
{
|
||||
PoolBase<Type>::Free(entry);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the pool.
|
||||
*
|
||||
* Version requires the `Type` class to provide method `void Init(Instance &)` to initialize
|
||||
* each `Type` entry object. This can be realized by the `Type` class inheriting from `InstanceLocatorInit()`.
|
||||
*
|
||||
* @param[in] aInstance A reference to the OpenThread instance.
|
||||
*/
|
||||
explicit Pool(Instance &aInstance)
|
||||
{
|
||||
for (Type &entry : mPool)
|
||||
{
|
||||
entry.Init(aInstance);
|
||||
PoolBase<Type>::Free(entry);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Frees all previously allocated objects.
|
||||
*/
|
||||
void FreeAll(void)
|
||||
{
|
||||
mFreeList.Clear();
|
||||
PoolBase<Type>::mFreeList.Clear();
|
||||
|
||||
for (Type &entry : mPool)
|
||||
{
|
||||
mFreeList.Push(entry);
|
||||
PoolBase<Type>::Free(entry);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -136,8 +149,8 @@ public:
|
||||
*
|
||||
* @param[in] aObject A reference to a `Type` object.
|
||||
*
|
||||
* @retval TRUE if @p aObject is from the pool.
|
||||
* @retval FALSE if @p aObject is not from the pool.
|
||||
* @retval TRUE If @p aObject is from the pool.
|
||||
* @retval FALSE If @p aObject is not from the pool.
|
||||
*/
|
||||
bool IsPoolEntry(const Type &aObject) const { return (&mPool[0] <= &aObject) && (&aObject < GetArrayEnd(mPool)); }
|
||||
|
||||
@@ -175,8 +188,77 @@ public:
|
||||
const Type &GetEntryAt(uint16_t aIndex) const { return mPool[aIndex]; }
|
||||
|
||||
private:
|
||||
LinkedList<Type> mFreeList;
|
||||
Type mPool[kPoolSize];
|
||||
Type mPool[kPoolSize];
|
||||
};
|
||||
|
||||
/**
|
||||
* Represents an object pool with run-time configurable storage.
|
||||
*
|
||||
* @tparam Type The object type. Type should provide `GetNext()` and `SetNext()` so that it can be added to a
|
||||
* linked list.
|
||||
*/
|
||||
template <class Type> class ConfigPool : public PoolBase<Type>
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Initializes the pool as empty and unlinked to any buffer.
|
||||
*/
|
||||
ConfigPool(void)
|
||||
: mEntryArray(nullptr)
|
||||
, mNumEntries(0)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the pool with a given array of entries.
|
||||
*
|
||||
* @param[in] aEntryArray A pointer to an array of entries.
|
||||
* @param[in] aNumEntries The number of entries in the array.
|
||||
*
|
||||
* @retval kErrorNone Successfully initialized the pool.
|
||||
* @retval kErrorAlready The pool is already initialized.
|
||||
*/
|
||||
Error Init(Type *aEntryArray, uint16_t aNumEntries)
|
||||
{
|
||||
Error error = kErrorNone;
|
||||
|
||||
VerifyOrExit(mEntryArray == nullptr, error = kErrorAlready);
|
||||
|
||||
mEntryArray = aEntryArray;
|
||||
mNumEntries = aNumEntries;
|
||||
|
||||
for (uint16_t i = 0; i < aNumEntries; i++)
|
||||
{
|
||||
PoolBase<Type>::Free(mEntryArray[i]);
|
||||
}
|
||||
|
||||
exit:
|
||||
return error;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the pool size.
|
||||
*
|
||||
* @returns The pool size (maximum number of objects in the pool).
|
||||
*/
|
||||
uint16_t GetSize(void) const { return mNumEntries; }
|
||||
|
||||
/**
|
||||
* Indicates whether or not a given `Type` object is from the pool.
|
||||
*
|
||||
* @param[in] aObject A reference to a `Type` object.
|
||||
*
|
||||
* @retval TRUE If @p aObject is from the pool.
|
||||
* @retval FALSE If @p aObject is not from the pool.
|
||||
*/
|
||||
bool IsPoolEntry(const Type &aObject) const
|
||||
{
|
||||
return (mNumEntries > 0) && (mEntryArray <= &aObject) && (&aObject < &mEntryArray[mNumEntries]);
|
||||
}
|
||||
|
||||
private:
|
||||
Type *mEntryArray;
|
||||
uint16_t mNumEntries;
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -61,6 +61,7 @@ private:
|
||||
constexpr uint16_t kPoolSize = 11;
|
||||
|
||||
typedef Pool<Entry, kPoolSize> EntryPool;
|
||||
typedef ConfigPool<Entry> ConfigEntryPool;
|
||||
|
||||
static Entry sNonPoolEntry;
|
||||
|
||||
@@ -79,7 +80,15 @@ void VerifyEntry(EntryPool &aPool, const Entry &aEntry, bool aInitWithInstance)
|
||||
VerifyOrQuit(aEntry.IsInitializedWithInstance() == aInitWithInstance, "Pool did not correctly Init() entry");
|
||||
}
|
||||
|
||||
void TestPool(EntryPool &aPool, bool aInitWithInstance)
|
||||
void VerifyEntry(ConfigEntryPool &aPool, const Entry &aEntry, bool aInitWithInstance)
|
||||
{
|
||||
OT_UNUSED_VARIABLE(aInitWithInstance);
|
||||
|
||||
VerifyOrQuit(aPool.IsPoolEntry(aEntry));
|
||||
VerifyOrQuit(!aPool.IsPoolEntry(sNonPoolEntry), "Pool::IsPoolEntry() succeeded for non-pool entry");
|
||||
}
|
||||
|
||||
template <typename PoolType> void TestPool(PoolType &aPool, bool aInitWithInstance = false)
|
||||
{
|
||||
Entry *entries[kPoolSize];
|
||||
|
||||
@@ -121,17 +130,33 @@ void TestPool(void)
|
||||
EntryPool pool1;
|
||||
EntryPool pool2(*instance);
|
||||
|
||||
printf("TestPool(/* aInitWithInstance */ false)\n");
|
||||
TestPool(pool1, /* aInitWithInstance */ false);
|
||||
|
||||
printf("TestPool(/* aInitWithInstance */ true)\n");
|
||||
TestPool(pool2, /* aInitWithInstance */ true);
|
||||
|
||||
testFreeInstance(instance);
|
||||
}
|
||||
|
||||
void TestConfigPool(void)
|
||||
{
|
||||
ConfigEntryPool pool;
|
||||
Entry entries[kPoolSize];
|
||||
|
||||
printf("TestConfigPool()\n");
|
||||
SuccessOrQuit(pool.Init(entries, kPoolSize));
|
||||
TestPool(pool);
|
||||
|
||||
VerifyOrQuit(pool.Init(entries, kPoolSize) != kErrorNone);
|
||||
}
|
||||
|
||||
} // namespace ot
|
||||
|
||||
int main(void)
|
||||
{
|
||||
ot::TestPool();
|
||||
ot::TestConfigPool();
|
||||
printf("All tests passed\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user