[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:
Abtin Keshavarzian
2026-03-04 00:40:13 -06:00
committed by GitHub
parent e5169ea810
commit 01d75f730f
2 changed files with 148 additions and 41 deletions
+122 -40
View File
@@ -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;
};
/**
+26 -1
View File
@@ -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;
}