diff --git a/src/lib/support/Pool.h b/src/lib/support/Pool.h index f9fe67aaa8d735..c482ecc59306b6 100644 --- a/src/lib/support/Pool.h +++ b/src/lib/support/Pool.h @@ -23,6 +23,8 @@ #pragma once +#include + #include #include #include @@ -30,6 +32,11 @@ #include #include +#if CHIP_SYSTEM_CONFIG_POOL_USE_HEAP +#include +#include +#endif // CHIP_SYSTEM_CONFIG_POOL_USE_HEAP + namespace chip { class StaticAllocatorBase @@ -160,4 +167,107 @@ class BitMapObjectPool : public StaticAllocatorBitmap } mData; }; +#if CHIP_SYSTEM_CONFIG_POOL_USE_HEAP + +/** + * @brief + * A class template used for allocating Object subclass objects from an ObjectArena<> template union. + * + * @tparam T a class to be allocated from the arena. + */ +template +class HeapObjectPool +{ +public: + template + T * CreateObject(Args &&... args) + { + std::lock_guard lock(mutex); + T * object = new T(std::forward(args)...); + if (object == nullptr) + { + return nullptr; + } + + mObjects.insert(object); + return object; + } + + void ReleaseObject(T * object) + { + if (object == nullptr) + return; + + std::lock_guard lock(mutex); + auto iter = mObjects.find(object); + VerifyOrDie(iter != mObjects.end()); + mObjects.erase(iter); + delete object; + } + + template + void ResetObject(T * element, Args &&... args) + { + element->~T(); + new (element) T(std::forward(args)...); + } + + /** + * @brief + * Run a functor for each active object in the pool + * + * @param function The functor of type `bool (*)(T*)`, return false to break the iteration + * @return bool Returns false if broke during iteration + */ + template + bool ForEachActiveObject(Function && function) + { + std::lock_guard lock(mutex); + // Create a new copy of original set, allowing add/remove elements while iterating in the same thread. + for (auto object : std::set(mObjects)) + { + if (!function(object)) + return false; + } + return true; + } + +private: + std::mutex mutex; + std::set mObjects; +}; + +#endif // CHIP_SYSTEM_CONFIG_POOL_USE_HEAP + +#if CHIP_SYSTEM_CONFIG_POOL_USE_HEAP +template +using ObjectPool = HeapObjectPool; +#else // CHIP_SYSTEM_CONFIG_POOL_USE_HEAP +template +using ObjectPool = BitMapObjectPool; +#endif // CHIP_SYSTEM_CONFIG_POOL_USE_HEAP + +enum class ObjectPoolMem +{ + kStatic, +#if CHIP_SYSTEM_CONFIG_POOL_USE_HEAP + kDynamic +#endif // CHIP_SYSTEM_CONFIG_POOL_USE_HEAP +}; + +template +class MemTypeObjectPool; + +template +class MemTypeObjectPool : public BitMapObjectPool +{ +}; + +#if CHIP_SYSTEM_CONFIG_POOL_USE_HEAP +template +class MemTypeObjectPool : public HeapObjectPool +{ +}; +#endif // CHIP_SYSTEM_CONFIG_POOL_USE_HEAP + } // namespace chip diff --git a/src/lib/support/tests/TestPool.cpp b/src/lib/support/tests/TestPool.cpp index 57366ff2b3ce8f..32a2a871fc2314 100644 --- a/src/lib/support/tests/TestPool.cpp +++ b/src/lib/support/tests/TestPool.cpp @@ -129,6 +129,71 @@ void TestCreateReleaseStruct(nlTestSuite * inSuite, void * inContext) } } +#if CHIP_SYSTEM_CONFIG_POOL_USE_HEAP + +void TestReleaseNullDynamic(nlTestSuite * inSuite, void * inContext) +{ + HeapObjectPool pool; + pool.ReleaseObject(nullptr); + // Not crashing is good. +} + +void TestCreateReleaseObjectDynamic(nlTestSuite * inSuite, void * inContext) +{ + constexpr const size_t size = 100; + HeapObjectPool pool; + uint32_t * obj[size]; + for (size_t i = 0; i < size; ++i) + { + obj[i] = pool.CreateObject(); + NL_TEST_ASSERT(inSuite, obj[i] != nullptr); + for (size_t j = 0; j < i; ++j) + { + NL_TEST_ASSERT(inSuite, obj[i] != obj[j]); + } + } + + pool.ReleaseObject(obj[55]); + obj[55] = pool.CreateObject(); + NL_TEST_ASSERT(inSuite, obj[55] != nullptr); + + for (size_t i = 0; i < size; ++i) + { + pool.ReleaseObject(obj[i]); + } +} + +void TestCreateReleaseStructDynamic(nlTestSuite * inSuite, void * inContext) +{ + struct S + { + S(std::set & set) : mSet(set) { mSet.insert(this); } + ~S() { mSet.erase(this); } + std::set & mSet; + }; + + std::set objs1; + + constexpr const size_t size = 100; + HeapObjectPool pool; + S * objs2[size]; + for (size_t i = 0; i < size; ++i) + { + objs2[i] = pool.CreateObject(objs1); + NL_TEST_ASSERT(inSuite, objs2[i] != nullptr); + for (size_t j = 0; j < i; ++j) + { + NL_TEST_ASSERT(inSuite, objs2[i] != objs2[j]); + } + } + for (size_t i = 0; i < size; ++i) + { + pool.ReleaseObject(objs2[i]); + } +} + +#endif // CHIP_SYSTEM_CONFIG_POOL_USE_HEAP + int Setup(void * inContext) { return SUCCESS; @@ -145,8 +210,19 @@ int Teardown(void * inContext) /** * Test Suite. It lists all the test functions. */ -static const nlTest sTests[] = { NL_TEST_DEF_FN(TestReleaseNull), NL_TEST_DEF_FN(TestCreateReleaseObject), - NL_TEST_DEF_FN(TestCreateReleaseStruct), NL_TEST_SENTINEL() }; +static const nlTest sTests[] = { + // clang-format off + NL_TEST_DEF_FN(TestReleaseNull), + NL_TEST_DEF_FN(TestCreateReleaseObject), + NL_TEST_DEF_FN(TestCreateReleaseStruct), +#if CHIP_SYSTEM_CONFIG_POOL_USE_HEAP + NL_TEST_DEF_FN(TestReleaseNullDynamic), + NL_TEST_DEF_FN(TestCreateReleaseObjectDynamic), + NL_TEST_DEF_FN(TestCreateReleaseStructDynamic), +#endif // CHIP_SYSTEM_CONFIG_POOL_USE_HEAP + NL_TEST_SENTINEL() + // clang-format on +}; int TestPool() { diff --git a/src/system/SystemTimer.cpp b/src/system/SystemTimer.cpp index d9a1600802a106..7e4a606099e5ea 100644 --- a/src/system/SystemTimer.cpp +++ b/src/system/SystemTimer.cpp @@ -74,7 +74,7 @@ namespace System { ******************************************************************************* */ -BitMapObjectPool Timer::sPool; +chip::ObjectPool Timer::sPool; Stats::count_t Timer::mNumInUse = 0; Stats::count_t Timer::mHighWatermark = 0; diff --git a/src/system/SystemTimer.h b/src/system/SystemTimer.h index 390307ba97f2d8..91efa2a0cfcdca 100644 --- a/src/system/SystemTimer.h +++ b/src/system/SystemTimer.h @@ -229,7 +229,7 @@ class DLL_EXPORT Timer private: friend class LayerImplLwIP; - static BitMapObjectPool sPool; + static chip::ObjectPool sPool; static Stats::count_t mNumInUse; static Stats::count_t mHighWatermark;