From a7f41650bbd9079907d7c57096cacf702bb3990c Mon Sep 17 00:00:00 2001 From: Mathias Kraus Date: Sun, 22 Oct 2023 21:08:30 +0200 Subject: [PATCH] iox-#66 Remove 'ObjectPool' --- .../release-notes/iceoryx-unreleased.md | 1 + iceoryx_dust/README.md | 1 - .../include/iceoryx_dust/cxx/objectpool.hpp | 454 --------- .../test/moduletests/test_cxx_objectpool.cpp | 950 ------------------ .../include/iceoryx_posh/gateway/channel.hpp | 6 +- .../iceoryx_posh/internal/gateway/channel.inl | 28 +- .../test/moduletests/test_gw_channel.cpp | 2 +- 7 files changed, 18 insertions(+), 1424 deletions(-) delete mode 100644 iceoryx_dust/include/iceoryx_dust/cxx/objectpool.hpp delete mode 100644 iceoryx_dust/test/moduletests/test_cxx_objectpool.cpp diff --git a/doc/website/release-notes/iceoryx-unreleased.md b/doc/website/release-notes/iceoryx-unreleased.md index 015a9cdb31..a4e00d7b82 100644 --- a/doc/website/release-notes/iceoryx-unreleased.md +++ b/doc/website/release-notes/iceoryx-unreleased.md @@ -165,6 +165,7 @@ - Roudi Environment independent from Googletest [#1533](https://github.com/eclipse-iceoryx/iceoryx/issues/1533) - Move test class for ctor and assignment operators to hoofs testing [#2041](https://github.com/eclipse-iceoryx/iceoryx/issues/2041) - Refactor `FixdePositionContainer` and move to `dust` [#2044](https://github.com/eclipse-iceoryx/iceoryx/issues/2044) +- Cleanup or Remove ObjectPool [#66](https://github.com/eclipse-iceoryx/iceoryx/issues/66) **Workflow:** diff --git a/iceoryx_dust/README.md b/iceoryx_dust/README.md index a374afe728..7614d09b0d 100644 --- a/iceoryx_dust/README.md +++ b/iceoryx_dust/README.md @@ -10,7 +10,6 @@ grouped together in categories or namespace, depending on where or how they are | class/file | description | |:---------------------:|:----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| |`forward_list` | Heap and exception free, relocatable implementation of `std::forward_list` | -|`ObjectPool` | Container which stores raw objects without calling the ctor of the objects. | |`FileReader` | Wrapper for opening files and reading them. | |`FixedPositionContainer` | A fixed-position container is similar to a list but is optimized for iterating over its elements without the back-and-forth jumping that can occur during iteration in a list. | |`MessageQueue` | Interface for Message Queues, see [ManPage mq_overview](https://www.man7.org/linux/man-pages/man7/mq_overview.7.html). | diff --git a/iceoryx_dust/include/iceoryx_dust/cxx/objectpool.hpp b/iceoryx_dust/include/iceoryx_dust/cxx/objectpool.hpp deleted file mode 100644 index 2e61d61571..0000000000 --- a/iceoryx_dust/include/iceoryx_dust/cxx/objectpool.hpp +++ /dev/null @@ -1,454 +0,0 @@ -// Copyright (c) 2019 by Robert Bosch GmbH. All rights reserved. -// Copyright (c) 2021 by Apex.AI Inc. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// SPDX-License-Identifier: Apache-2.0 -#ifndef IOX_DUST_OBJECTPOOL_OBJECTPOOL_HPP -#define IOX_DUST_OBJECTPOOL_OBJECTPOOL_HPP - -#include //for size_t -#include -#include //for std::forward - -namespace iox -{ -namespace cxx -{ -// @todo iox-#66 finalize API and add doxygen comments -// the API will be safer and more concise -// furthermore the free position computation will be improved to gain performance - -// note: as in e.g. std::vector, no index bounds checking for efficiency (using illegal indices leads to undefined -// behaviour) -template // @todo iox-#66 use sensible and compatible type for this and NO_INDEX -class ObjectPool -{ - public: - using Index_t = int; // @todo iox-#66 choose Index_t types correctly wrt. size, conversion compatibility - static constexpr int NO_INDEX = -1; - - private: - static constexpr size_t CHUNKSIZE = sizeof(T); - using Chunk = char[CHUNKSIZE]; - using Container = Chunk[CAPACITY]; // we need uninitalized memory without calling any constructors - // we cannot use typed C arrays for this reason, since it would call T() (maybe - // we could use a one element as CHUNK) - - - Index_t m_freeIndex{0}; - size_t m_size{0u}; - - // @todo iox-#66 maybe this metainformation can be combined, e.g. the data pointer == nullptr to indicate that - // the data is invalid - struct CellInfo - { - bool isValid{false}; // @todo iox-#66 rename into isUsed? - bool wasConstructed{false}; // we want to use this to determine whether T destruction should occur in destructor - T* data{nullptr}; - }; - - alignas(T) Container m_values; - CellInfo m_cellInfo[CAPACITY]; - char* m_first; - char* m_last; - - public: - class Iterator - { - private: - Index_t index; - ObjectPool* pool; - - public: - Iterator(Index_t index, ObjectPool& pool) - : index(index) - , pool(&pool) - { - } - - T& operator*() - { - return *(pool->m_cellInfo[index].data); - } - - // in contrast to operator* we do checking in operator-> - // for operator* this is not as straightforward, since we need - // to return a reference (what should happen if iterator is end()?) - T* operator->() - { - if (index >= CAPACITY) - { - return nullptr; - } - if ((pool->m_cellInfo[index]).isValid) - { - return pool->m_cellInfo[index].data; - } - return nullptr; - } - - // pre-increment - Iterator& operator++() - { - for (Index_t i = index + 1; i < CAPACITY; ++i) - { - if (pool->m_cellInfo[i].isValid == true) - { - index = i; - return *this; - } - } - index = CAPACITY; - return *this; - } - - // post increment - Iterator operator++(int) - { - auto ret = Iterator(index, *pool); - for (Index_t i = index + 1; i < CAPACITY; ++i) - { - if (pool->m_cellInfo[i].isValid == true) - { - index = i; - return ret; - } - } - index = CAPACITY; - return ret; - } - - bool operator!=(const Iterator& other) const - { - return (this->index != other.index || this->pool != other.pool); - } - - bool operator==(const Iterator& other) const - { - return (this->index == other.index && this->pool == other.pool); - } - }; - - Iterator begin() - { - for (Index_t i = 0; i < CAPACITY; ++i) - { - if (m_cellInfo[i].isValid == true) - { - return Iterator(i, *this); - } - } - return Iterator(CAPACITY, *this); - } - - Iterator end() - { - return Iterator(CAPACITY, *this); - } - - ObjectPool() - : m_first(reinterpret_cast(&(m_values[0]))) - , m_last(reinterpret_cast(&(m_values[CAPACITY - 1]))) - { - } - - - ~ObjectPool() - { - // destroy objects if they where constructed by the pool - for (Index_t i = 0; i < CAPACITY; ++i) - { - if (m_cellInfo[i].isValid && m_cellInfo[i].wasConstructed) - { - m_cellInfo[i].data->~T(); - } - } - } - - //***********************************index API *********************************************************** - - Index_t reserve() - { - auto index = nextFree(); - - if (index >= 0) - { - m_freeIndex = index; - m_cellInfo[m_freeIndex].isValid = true; - m_cellInfo[m_freeIndex].wasConstructed = false; - ++m_size; - } - - return index; - } - - Index_t construct() - { - auto index = nextFree(); - - if (index >= 0) - { - m_freeIndex = index; - m_cellInfo[index].data = new (&m_values[index]) T; // use placement new - m_cellInfo[m_freeIndex].isValid = true; - m_cellInfo[m_freeIndex].wasConstructed = true; - ++m_size; - } - - return index; - } - - template - Index_t construct(Args&&... args) - { - auto index = nextFree(); - - if (index >= 0) - { - m_freeIndex = index; - m_cellInfo[index].data = new (&m_values[index]) T(std::forward(args)...); // use placement new - m_cellInfo[m_freeIndex].isValid = true; - m_cellInfo[m_freeIndex].wasConstructed = true; - ++m_size; - } - - return index; - } - - Index_t add(const T& element) - { - auto index = nextFree(); - - if (index >= 0) - { - m_freeIndex = index; - auto& cellInfo = m_cellInfo[m_freeIndex]; - cellInfo.data = new (m_values[m_freeIndex]) T(element); - cellInfo.isValid = true; - cellInfo.wasConstructed = true; - ++m_size; - } - - return index; - } - - void remove(Index_t index, bool destruct = false) - { - if (m_cellInfo[index].isValid) - { - if (destruct) - { - m_cellInfo[index].data->~T(); - } - m_cellInfo[index].isValid = false; - --m_size; - } - } - - // unsafe by design (as std::vector), remove ...? - T& operator[](Index_t index) - { - return *(m_cellInfo[index].data); - } - - Iterator iterator(Index_t index) - { - if (m_cellInfo[index].isValid) - { - return Iterator(index, *this); - } - return end(); - } - - size_t size() const - { - return m_size; - } - - size_t capacity() const - { - return CAPACITY; - } - - //**************************** pointer API ******************************** - - // get raw memory for object T - T* allocate() - { - auto index = reserve(); - if (index == NO_INDEX) - { - return nullptr; - } - // access to raw memory where nothing was constructed yet - // cannot be avoided easily with current logic - // since the idea is that the user could also use this memory pointer - // to place objects of type T in (should probably not be supported in the future - // because it is dangerous if used wrongly) - - return reinterpret_cast(m_values[index]); - } - - // default construct object T - T* create() - { - auto index = construct(); - if (index == NO_INDEX) - { - return nullptr; - } - return get(index); - } - - // construct object T with constructors taking arguments - template - T* create(Args&&... args) - { - auto index = construct(std::forward(args)...); - if (index == NO_INDEX) - { - return nullptr; - } - return get(index); - } - - // free the cell associated with ptr and call destructor if destruct is true - void free(T* ptr, bool destruct) - { - auto index = pointerToIndex(ptr); - if (index >= 0) - { - remove(index, destruct); - } - } - - // free cell associated with ptr - // call object destructor if and only if it was constructed by the pool - // note: this can be dangerous if the user also destroyed the objects via pointer - void free(T* ptr) - { - auto index = pointerToIndex(ptr); - if (index >= 0) - { - remove(index, m_cellInfo[index].wasConstructed); - } - } - - T* insert(const T& element) - { - auto index = add(element); - - if (index >= 0) - { - get(index); - } - else - { - return nullptr; - } - - return get(index); - } - - T* get(Index_t index) - { - return (m_cellInfo[index].isValid) ? m_cellInfo[index].data : nullptr; - } - - T* get(T* ptr) - { - auto index = pointerToIndex(ptr); - if (index != NO_INDEX) - { - return (m_cellInfo[index].isValid) ? m_cellInfo[index].data : nullptr; - } - return nullptr; - } - - Iterator iterator(T* ptr) - { - auto index = pointerToIndex(ptr); - if (index >= 0) - { - return iterator(index); - } - return end(); - } - - Index_t pointerToIndex(T* ptr) - { - /// @todo iox-#66 not critical, but refactor for pointer arithmetic later - char* p = reinterpret_cast(ptr); - if (p < m_first || p > m_last) - { - return NO_INDEX; - } - auto delta = p - m_first; - if (static_cast(delta) % sizeof(T) != 0) - { - return NO_INDEX; - } - - // if the cell is valid and contains data we expect the pointer to equal data (alignment has to match) - auto index = static_cast(static_cast(delta) / sizeof(T)); - if (m_cellInfo[index].isValid && m_cellInfo[index].data) - { - if (m_cellInfo[index].data == ptr) - { - return index; - } - else - { - // pointer is not aligned to object in cell - return NO_INDEX; - } - } - - return index; - } - - T* indexToPointer(Index_t index) - { - return m_cellInfo[index].data; - } - - protected: - // protected for unit tests - // @todo iox-#66 use fifo/index set for efficient search of free elements - Index_t nextFree() - { - if (m_size >= CAPACITY) - return NO_INDEX; // container is full - - for (; m_cellInfo[m_freeIndex].isValid; m_freeIndex = (m_freeIndex + 1) % CAPACITY) - ; - - return m_freeIndex; - } - - // private member accessors for unit tests - char* getFirstPtr() const - { - return m_first; - } - - char* getLastPtr() const - { - return m_last; - } -}; -} // namespace cxx -} // namespace iox - -#endif // IOX_DUST_OBJECTPOOL_OBJECTPOOL_HPP diff --git a/iceoryx_dust/test/moduletests/test_cxx_objectpool.cpp b/iceoryx_dust/test/moduletests/test_cxx_objectpool.cpp deleted file mode 100644 index 895f75b5e9..0000000000 --- a/iceoryx_dust/test/moduletests/test_cxx_objectpool.cpp +++ /dev/null @@ -1,950 +0,0 @@ -// Copyright (c) 2019 by Robert Bosch GmbH. All rights reserved. -// Copyright (c) 2021 by Apex.AI Inc. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// SPDX-License-Identifier: Apache-2.0 - -#include "iceoryx_dust/cxx/objectpool.hpp" - -#include - -#include "test.hpp" - -namespace -{ -using namespace ::testing; - -constexpr int INVALID = -1; - - -// non primitive type for pool -class Foo -{ - public: - Foo() - { - ++constructionCounter; - } - - Foo(int& data) - : m_data(&data) - { - *m_data = ++constructionCounter; - } - - ~Foo() - { - if (m_data) - { - *m_data = INVALID; // invalidated by destructor, visible outside to check destructor call by Pool - } - ++destructionCounter; - } - - int* m_data{nullptr}; - - static void resetConstructionCounter() - { - constructionCounter = 0; - } - - static int getConstructionCounter() - { - return constructionCounter; - } - - static void resetDestructionCounter() - { - destructionCounter = 0; - } - - static int getDestructionCounter() - { - return destructionCounter; - } - - static int constructionCounter; // count the number of Foo objects constructed - static int destructionCounter; // count the number of Foo objects constructed -}; - -int Foo::constructionCounter{0}; -int Foo::destructionCounter{0}; - -// needed signed and unsigned to avoid some sign comparison issues -constexpr int CAPACITY = 3; -constexpr size_t CAPACITY_UNSIGNED = static_cast(CAPACITY); - -using FooPool = iox::cxx::ObjectPool; -using Index_t = FooPool::Index_t; -constexpr int NO_INDEX = FooPool::NO_INDEX; - -struct FooPoolWithPrivateMembersAccess : FooPool -{ - using FooPool::getFirstPtr; - using FooPool::getLastPtr; - using FooPool::nextFree; -}; - -class ObjectPool_test : public Test -{ - public: - void SetUp() override - { - Foo::resetConstructionCounter(); // reset Foo constructionCounter in each test - Foo::resetDestructionCounter(); // reset Foo destructionCounter in each test - } - void TearDown() override - { - } - - int data; - int data1; - int data2; - int data3; - FooPool pool; - FooPoolWithPrivateMembersAccess poolExposed; -}; - -// check whether the constructed pool objects (of type Foo) have the intended data and construction/destruction -// behaviour many other tests depend on this behaviour to track construction/destruction and associated data -TEST_F(ObjectPool_test, poolObjectBehaviour) -{ - ::testing::Test::RecordProperty("TEST_ID", "f8381e4b-b775-47f1-aaac-3c3ac7484df6"); - // check that Foo objects behave correctly (i.e. static constructionCounter works and destructor effects data) - int data = 73; // databuffer of foo - { - Foo foo(data); - EXPECT_THAT(data, Eq(1)); - EXPECT_THAT(Foo::getConstructionCounter(), Eq(data)); - // Foo destructor called -> data invalid - } - EXPECT_THAT(Foo::getDestructionCounter(), Eq(1)); - EXPECT_THAT(data, Eq(INVALID)); - - { - Foo foo(data); - EXPECT_THAT(data, Eq(2)); - EXPECT_THAT(Foo::getConstructionCounter(), Eq(data)); - } - EXPECT_THAT(Foo::getDestructionCounter(), Eq(2)); - EXPECT_THAT(data, Eq(INVALID)); - - Foo::resetConstructionCounter(); - EXPECT_THAT(Foo::getConstructionCounter(), Eq(0)); - - Foo::resetDestructionCounter(); - EXPECT_THAT(Foo::getDestructionCounter(), Eq(0)); -} - - -TEST_F(ObjectPool_test, construction) -{ - ::testing::Test::RecordProperty("TEST_ID", "7b8a278a-7a3c-4b32-8e81-f0072d97e5c2"); - // pool initialized correctly, check size and capacity - EXPECT_THAT(pool.size(), Eq(0U)); - EXPECT_THAT(pool.capacity(), Eq(CAPACITY_UNSIGNED)); - EXPECT_THAT(Foo::getConstructionCounter(), Eq(0)); // no Foo objects constructed by pool -} - - -TEST_F(ObjectPool_test, reserve) -{ - ::testing::Test::RecordProperty("TEST_ID", "a91fc2f1-9d6d-4e85-9a3d-4620e31478ef"); - EXPECT_THAT(pool.size(), Eq(0U)); - EXPECT_THAT(pool.capacity(), Eq(CAPACITY_UNSIGNED)); - EXPECT_THAT(Foo::getConstructionCounter(), Eq(0)); // no Foo objects constructed by pool - - for (int i = 1; i <= CAPACITY; ++i) - { - auto index = pool.reserve(); - EXPECT_THAT(index, Ne(NO_INDEX)); - } - EXPECT_THAT(pool.size(), Eq(CAPACITY_UNSIGNED)); - - // pool is full - auto index = pool.reserve(); - EXPECT_THAT(index, Eq(NO_INDEX)); - EXPECT_THAT(pool.size(), Eq(CAPACITY_UNSIGNED)); -} - - -TEST_F(ObjectPool_test, default_construct) -{ - ::testing::Test::RecordProperty("TEST_ID", "50fad76a-5eec-4812-b509-908f09ed71ac"); - EXPECT_THAT(pool.size(), Eq(0U)); - EXPECT_THAT(pool.capacity(), Eq(CAPACITY_UNSIGNED)); - EXPECT_THAT(Foo::getConstructionCounter(), Eq(0)); // no Foo objects constructed by pool - - for (int i = 1; i <= CAPACITY; ++i) - { - auto index = pool.construct(); - EXPECT_THAT(index, Ne(NO_INDEX)); - EXPECT_THAT(Foo::getConstructionCounter(), Eq(i)); - } - EXPECT_THAT(pool.size(), Eq(CAPACITY_UNSIGNED)); - - // pool is full, nothing constructed - auto index = pool.construct(); - EXPECT_THAT(index, Eq(NO_INDEX)); - EXPECT_THAT(pool.size(), Eq(CAPACITY_UNSIGNED)); -} - -TEST_F(ObjectPool_test, parameter_construct) -{ - ::testing::Test::RecordProperty("TEST_ID", "5b0af6d2-3baf-4620-92e1-fe14770b03fd"); - EXPECT_THAT(pool.size(), Eq(0U)); - EXPECT_THAT(pool.capacity(), Eq(CAPACITY_UNSIGNED)); - EXPECT_THAT(Foo::getConstructionCounter(), Eq(0)); // no Foo objects constructed by pool - - data = 0; - for (int i = 1; i <= CAPACITY; ++i) - { - auto index = pool.construct(data); - EXPECT_THAT(index, Ne(NO_INDEX)); - EXPECT_THAT(data, Eq(i)); - EXPECT_THAT(Foo::getConstructionCounter(), Eq(i)); - } - EXPECT_THAT(pool.size(), Eq(CAPACITY_UNSIGNED)); - - // pool is full, nothing constructed - auto index = pool.construct(data); - EXPECT_THAT(index, Eq(NO_INDEX)); - EXPECT_THAT(Foo::getConstructionCounter(), Eq(CAPACITY)); - EXPECT_THAT(data, Eq(CAPACITY)); - EXPECT_THAT(pool.size(), Eq(CAPACITY_UNSIGNED)); -} - -TEST_F(ObjectPool_test, add) -{ - ::testing::Test::RecordProperty("TEST_ID", "e5c09e18-a3dc-46e5-a8d8-fac27c3c8c47"); - EXPECT_THAT(pool.size(), Eq(0U)); - EXPECT_THAT(pool.capacity(), Eq(CAPACITY_UNSIGNED)); - EXPECT_THAT(Foo::getConstructionCounter(), Eq(0)); // no Foo objects constructed by pool - - data = 0; - for (int i = 1; i <= CAPACITY; ++i) - { - Foo foo(data); - EXPECT_THAT(data, Eq(i)); - EXPECT_THAT(Foo::getConstructionCounter(), Eq(i)); - - auto index = pool.add(foo); - EXPECT_THAT(index, Ne(NO_INDEX)); - } - EXPECT_THAT(pool.size(), Eq(CAPACITY_UNSIGNED)); - - // pool is full, cannot add - Foo foo(data); - EXPECT_THAT(Foo::getConstructionCounter(), Eq(CAPACITY + 1)); - EXPECT_THAT(data, Eq(CAPACITY + 1)); - auto index = pool.add(foo); - EXPECT_THAT(index, Eq(NO_INDEX)); - EXPECT_THAT(pool.size(), Eq(CAPACITY_UNSIGNED)); -} - -TEST_F(ObjectPool_test, size_and_remove) -{ - ::testing::Test::RecordProperty("TEST_ID", "696ba57c-d761-4c8a-bb5f-60a519172a69"); - EXPECT_THAT(pool.size(), Eq(0U)); - EXPECT_THAT(pool.capacity(), Eq(CAPACITY_UNSIGNED)); - EXPECT_THAT(Foo::getConstructionCounter(), Eq(0)); // no Foo objects constructed by pool - EXPECT_THAT(pool.size(), Eq(0U)); - - data1 = 0; - auto index1 = pool.construct(data1); - EXPECT_THAT(data1, Eq(1)); - EXPECT_THAT(Foo::getConstructionCounter(), Eq(1)); - EXPECT_THAT(Foo::getDestructionCounter(), Eq(0)); - EXPECT_THAT(pool.size(), Eq(1U)); - - data2 = 0; - Foo foo(data2); - auto index2 = pool.add(foo); - EXPECT_THAT(data2, Eq(2)); - EXPECT_THAT(Foo::getConstructionCounter(), Eq(2)); - EXPECT_THAT(Foo::getDestructionCounter(), Eq(0)); - EXPECT_THAT(pool.size(), Eq(2U)); - ASSERT_THAT(pool.get(index1), Ne(nullptr)); - - pool.remove(index1); - EXPECT_THAT(pool.get(index1), Eq(nullptr)); - EXPECT_THAT(Foo::getDestructionCounter(), Eq(0)); - EXPECT_THAT(pool.size(), Eq(1U)); - - ASSERT_THAT(pool.get(index2), Ne(nullptr)); - pool.remove(index2, true); - EXPECT_THAT(data2, Eq(INVALID)); - EXPECT_THAT(pool.get(index2), Eq(nullptr)); - EXPECT_THAT(Foo::getDestructionCounter(), Eq(1)); - EXPECT_THAT(pool.size(), Eq(0U)); -} - -TEST_F(ObjectPool_test, bracket_operator) -{ - ::testing::Test::RecordProperty("TEST_ID", "79f4e1f0-cfeb-474c-a2f3-7b7b92cad27f"); - EXPECT_THAT(pool.size(), Eq(0U)); - EXPECT_THAT(pool.capacity(), Eq(CAPACITY_UNSIGNED)); - EXPECT_THAT(Foo::getConstructionCounter(), Eq(0)); // no Foo objects constructed by pool - EXPECT_THAT(pool.size(), Eq(0U)); - - data1 = 0; - auto index1 = pool.construct(data1); - EXPECT_THAT(data1, Eq(1)); - EXPECT_THAT(Foo::getConstructionCounter(), Eq(1)); - EXPECT_THAT(Foo::getDestructionCounter(), Eq(0)); - EXPECT_THAT(pool.size(), Eq(1U)); - ASSERT_THAT(pool.get(index1), Ne(nullptr)); - - data2 = 0; - Foo foo(data2); - auto index2 = pool.add(foo); - EXPECT_THAT(data2, Eq(2)); - EXPECT_THAT(Foo::getConstructionCounter(), Eq(2)); - EXPECT_THAT(Foo::getDestructionCounter(), Eq(0)); - EXPECT_THAT(pool.size(), Eq(2U)); - ASSERT_THAT(pool.get(index2), Ne(nullptr)); - - Foo& ret1 = pool[index1]; // check whether we get the reference to the correct object (associated with data1) - EXPECT_THAT(ret1.m_data, Eq(&data1)); - - Foo& ret2 = pool[index2]; // check whether we get the reference to the correct object (associated with data2) - EXPECT_THAT(ret2.m_data, Eq(&data2)); -} - -TEST_F(ObjectPool_test, allocate) -{ - ::testing::Test::RecordProperty("TEST_ID", "89ed7083-42be-4f71-bf31-9ce3ea5db6cf"); - EXPECT_THAT(pool.size(), Eq(0U)); - EXPECT_THAT(pool.capacity(), Eq(CAPACITY_UNSIGNED)); - EXPECT_THAT(Foo::getConstructionCounter(), Eq(0)); // no Foo objects constructed by pool - - for (int i = 1; i <= CAPACITY; ++i) - { - auto ptr = pool.allocate(); - EXPECT_THAT(ptr, Ne(nullptr)); - } - EXPECT_THAT(pool.size(), Eq(CAPACITY_UNSIGNED)); - - // pool is full - auto ptr = pool.allocate(); - EXPECT_THAT(ptr, Eq(nullptr)); - EXPECT_THAT(pool.size(), Eq(CAPACITY_UNSIGNED)); -} - -TEST_F(ObjectPool_test, default_create) -{ - ::testing::Test::RecordProperty("TEST_ID", "874342ce-0680-4634-b651-f72c2dee24c4"); - EXPECT_THAT(pool.size(), Eq(0U)); - EXPECT_THAT(pool.capacity(), Eq(CAPACITY_UNSIGNED)); - EXPECT_THAT(Foo::getConstructionCounter(), Eq(0)); // no Foo objects constructed by pool - - for (int i = 1; i <= CAPACITY; ++i) - { - auto ptr = pool.create(); - EXPECT_THAT(ptr, Ne(nullptr)); - EXPECT_THAT(Foo::getConstructionCounter(), Eq(i)); - } - EXPECT_THAT(pool.size(), Eq(CAPACITY_UNSIGNED)); - - // pool is full, nothing constructed - auto ptr = pool.create(); - EXPECT_THAT(ptr, Eq(nullptr)); - EXPECT_THAT(pool.size(), Eq(CAPACITY_UNSIGNED)); -} - -TEST_F(ObjectPool_test, parameter_create) -{ - ::testing::Test::RecordProperty("TEST_ID", "b08141d9-23b9-4a36-a694-236dce2ceaf7"); - EXPECT_THAT(pool.size(), Eq(0U)); - EXPECT_THAT(pool.capacity(), Eq(CAPACITY_UNSIGNED)); - EXPECT_THAT(Foo::getConstructionCounter(), Eq(0)); // no Foo objects constructed by pool - - data = 0; - for (int i = 1; i <= CAPACITY; ++i) - { - auto ptr = pool.create(data); - EXPECT_THAT(ptr, Ne(nullptr)); - EXPECT_THAT(data, Eq(i)); - EXPECT_THAT(Foo::getConstructionCounter(), Eq(i)); - } - EXPECT_THAT(pool.size(), Eq(CAPACITY_UNSIGNED)); - - // pool is full, nothing constructed - auto ptr = pool.create(data); - EXPECT_THAT(ptr, Eq(nullptr)); - EXPECT_THAT(Foo::getConstructionCounter(), Eq(CAPACITY)); - EXPECT_THAT(data, Eq(CAPACITY)); - EXPECT_THAT(pool.size(), Eq(CAPACITY_UNSIGNED)); -} - -TEST_F(ObjectPool_test, destruct_free) -{ - ::testing::Test::RecordProperty("TEST_ID", "efd63af2-3c59-491f-b92b-4d9faa548e9b"); - EXPECT_THAT(pool.size(), Eq(0U)); - EXPECT_THAT(pool.capacity(), Eq(CAPACITY_UNSIGNED)); - EXPECT_THAT(Foo::getConstructionCounter(), Eq(0)); // no Foo objects constructed by pool - EXPECT_THAT(pool.size(), Eq(0U)); - - data1 = 0; - auto ptr1 = pool.create(data1); - EXPECT_THAT(data1, Eq(1)); - EXPECT_THAT(Foo::getConstructionCounter(), Eq(1)); - EXPECT_THAT(Foo::getDestructionCounter(), Eq(0)); - EXPECT_THAT(pool.size(), Eq(1U)); - - data2 = 0; - Foo foo(data2); - auto ptr2 = pool.insert(foo); - EXPECT_THAT(data2, Eq(2)); - EXPECT_THAT(Foo::getConstructionCounter(), Eq(2)); - EXPECT_THAT(Foo::getDestructionCounter(), Eq(0)); - EXPECT_THAT(pool.size(), Eq(2U)); - - ASSERT_THAT(ptr1, Ne(nullptr)); - pool.free(ptr1, false); - EXPECT_THAT(pool.get(ptr1), Eq(nullptr)); - EXPECT_THAT(Foo::getDestructionCounter(), Eq(0)); - EXPECT_THAT(pool.size(), Eq(1U)); - - ASSERT_THAT(ptr2, Ne(nullptr)); - pool.free(ptr2, true); - EXPECT_THAT(data2, Eq(INVALID)); - EXPECT_THAT(pool.get(ptr2), Eq(nullptr)); - EXPECT_THAT(Foo::getDestructionCounter(), Eq(1)); - EXPECT_THAT(pool.size(), Eq(0U)); -} - -TEST_F(ObjectPool_test, default_free) -{ - ::testing::Test::RecordProperty("TEST_ID", "55b755e5-8195-4fe4-8336-044416e644cd"); - EXPECT_THAT(pool.size(), Eq(0U)); - EXPECT_THAT(pool.capacity(), Eq(CAPACITY_UNSIGNED)); - EXPECT_THAT(Foo::getConstructionCounter(), Eq(0)); // no Foo objects constructed by pool - EXPECT_THAT(pool.size(), Eq(0U)); - - data1 = 0; - auto ptr1 = pool.create(data1); - EXPECT_THAT(data1, Eq(1)); - EXPECT_THAT(Foo::getConstructionCounter(), Eq(1)); - EXPECT_THAT(Foo::getDestructionCounter(), Eq(0)); - EXPECT_THAT(pool.size(), Eq(1U)); - - data2 = 0; - Foo foo(data2); - auto ptr2 = pool.insert(foo); - EXPECT_THAT(data2, Eq(2)); - EXPECT_THAT(Foo::getConstructionCounter(), Eq(2)); - EXPECT_THAT(Foo::getDestructionCounter(), Eq(0)); - EXPECT_THAT(pool.size(), Eq(2U)); - - auto ptr3 = pool.allocate(); - EXPECT_THAT(Foo::getConstructionCounter(), Eq(2)); - EXPECT_THAT(Foo::getDestructionCounter(), Eq(0)); - EXPECT_THAT(pool.size(), Eq(3U)); - - ASSERT_THAT(ptr1, Ne(nullptr)); - pool.free(ptr1); - EXPECT_THAT(pool.get(ptr1), Eq(nullptr)); - EXPECT_THAT(Foo::getDestructionCounter(), Eq(1)); - EXPECT_THAT(pool.size(), Eq(2U)); - - ASSERT_THAT(ptr2, Ne(nullptr)); - pool.free(ptr2); - EXPECT_THAT(data2, Eq(INVALID)); - EXPECT_THAT(pool.get(ptr2), Eq(nullptr)); - EXPECT_THAT(Foo::getDestructionCounter(), Eq(2)); - EXPECT_THAT(pool.size(), Eq(1U)); - - ASSERT_THAT(ptr3, Ne(nullptr)); - pool.free(ptr3); - EXPECT_THAT(pool.get(ptr3), Eq(nullptr)); - EXPECT_THAT(Foo::getDestructionCounter(), Eq(2)); - EXPECT_THAT(pool.size(), Eq(0U)); -} - -TEST_F(ObjectPool_test, insert) -{ - ::testing::Test::RecordProperty("TEST_ID", "d362be33-4ac1-420d-8502-c4dad69a66ff"); - EXPECT_THAT(pool.size(), Eq(0U)); - EXPECT_THAT(pool.capacity(), Eq(CAPACITY_UNSIGNED)); - EXPECT_THAT(Foo::getConstructionCounter(), Eq(0)); // no Foo objects constructed by pool - - data = 0; - for (int i = 1; i <= CAPACITY; ++i) - { - Foo foo(data); - EXPECT_THAT(data, Eq(i)); - EXPECT_THAT(Foo::getConstructionCounter(), Eq(i)); - auto ptr = pool.insert(foo); - EXPECT_THAT(ptr, Ne(nullptr)); - } - EXPECT_THAT(pool.size(), Eq(CAPACITY_UNSIGNED)); - - // pool is full, cannot add - Foo foo(data); - EXPECT_THAT(Foo::getConstructionCounter(), Eq(CAPACITY + 1)); - EXPECT_THAT(data, Eq(CAPACITY + 1)); - auto ptr = pool.insert(foo); - EXPECT_THAT(ptr, Eq(nullptr)); - EXPECT_THAT(pool.size(), Eq(CAPACITY_UNSIGNED)); -} - -TEST_F(ObjectPool_test, get) -{ - ::testing::Test::RecordProperty("TEST_ID", "9328ee17-c705-48ac-bcc6-e6a13d73829c"); - EXPECT_THAT(pool.size(), Eq(0U)); - EXPECT_THAT(pool.capacity(), Eq(CAPACITY_UNSIGNED)); - EXPECT_THAT(Foo::getConstructionCounter(), Eq(0)); // no Foo objects constructed by pool - EXPECT_THAT(pool.size(), Eq(0U)); - - data1 = 0; - auto index1 = pool.construct(data1); - EXPECT_THAT(data1, Eq(1)); - EXPECT_THAT(Foo::getConstructionCounter(), Eq(1)); - EXPECT_THAT(Foo::getDestructionCounter(), Eq(0)); - EXPECT_THAT(pool.size(), Eq(1U)); - - data2 = 0; - Foo foo(data2); - auto ptr2 = pool.insert(foo); - EXPECT_THAT(data2, Eq(2)); - EXPECT_THAT(Foo::getConstructionCounter(), Eq(2)); - EXPECT_THAT(Foo::getDestructionCounter(), Eq(0)); - EXPECT_THAT(pool.size(), Eq(2U)); - - ASSERT_THAT(index1, Ne(NO_INDEX)); - ASSERT_THAT(ptr2, Ne(nullptr)); - - auto ptr1 = pool.get(index1); - ASSERT_THAT(ptr1, Ne(nullptr)); - - auto index2 = pool.get(ptr2); - auto ptr = pool.get(index2); - ASSERT_THAT(ptr, Ne(nullptr)); - - // check whether indicies/pointers are associated with the correct values - EXPECT_THAT(ptr1->m_data, Eq(&data1)); - EXPECT_THAT(ptr2->m_data, Eq(&data2)); - EXPECT_THAT(ptr->m_data, Eq(&data2)); - - // remove element and check whether we get a nullptr - pool.free(ptr1, false); - EXPECT_THAT(pool.get(ptr1), Eq(nullptr)); - EXPECT_THAT(pool.get(index1), Eq(nullptr)); - EXPECT_THAT(Foo::getDestructionCounter(), Eq(0)); - EXPECT_THAT(pool.size(), Eq(1U)); -} - -TEST_F(ObjectPool_test, pointerToIndexConversion) -{ - ::testing::Test::RecordProperty("TEST_ID", "69ece67d-0d9e-44a3-bb16-956bdc591ee3"); - EXPECT_THAT(pool.size(), Eq(0U)); - EXPECT_THAT(pool.capacity(), Eq(CAPACITY_UNSIGNED)); - EXPECT_THAT(Foo::getConstructionCounter(), Eq(0)); // no Foo objects constructed by pool - EXPECT_THAT(pool.size(), Eq(0U)); - - data1 = 0; - auto index1 = pool.construct(data1); - EXPECT_THAT(data1, Eq(1)); - EXPECT_THAT(Foo::getConstructionCounter(), Eq(1)); - EXPECT_THAT(Foo::getDestructionCounter(), Eq(0)); - EXPECT_THAT(pool.size(), Eq(1U)); - ASSERT_THAT(index1, Ne(NO_INDEX)); - - auto ptr1 = pool.get(index1); - ASSERT_THAT(ptr1, Ne(nullptr)); - - // check whether indicies/pointers are associated with the correct values - EXPECT_THAT(ptr1->m_data, Eq(&data1)); - - EXPECT_THAT(pool.pointerToIndex(ptr1), Eq(index1)); - EXPECT_THAT(pool.indexToPointer(index1), Eq(ptr1)); - - // remove element and check whether we get a nullptr - pool.free(ptr1); - EXPECT_THAT(pool.get(ptr1), Eq(nullptr)); - EXPECT_THAT(pool.get(index1), Eq(nullptr)); - EXPECT_THAT(Foo::getDestructionCounter(), Eq(1)); - EXPECT_THAT(pool.size(), Eq(0U)); - - // conversion does not care about valid data, index1 still corresponds to ptr1 - EXPECT_THAT(pool.pointerToIndex(ptr1), Eq(index1)); - EXPECT_THAT(pool.indexToPointer(index1), Eq(ptr1)); -} - -TEST_F(ObjectPool_test, pointerToIndexLegalPointerConversion) -{ - ::testing::Test::RecordProperty("TEST_ID", "0e03caaa-1d47-4c19-823a-5456b732bff6"); - data = 0; - EXPECT_THAT(poolExposed.construct(data), Ne(NO_INDEX)); - - auto first = reinterpret_cast(poolExposed.getFirstPtr()); - auto last = reinterpret_cast(poolExposed.getLastPtr()); - auto alignedPtr = reinterpret_cast(poolExposed.getFirstPtr() + sizeof(Foo)); - - EXPECT_THAT(poolExposed.pointerToIndex(first), Eq(0)); - EXPECT_THAT(poolExposed.pointerToIndex(last), Eq(CAPACITY - 1)); - EXPECT_THAT(poolExposed.pointerToIndex(alignedPtr), Eq(1)); -} - -TEST_F(ObjectPool_test, pointerToIndexIllegalPointerConversion) -{ - ::testing::Test::RecordProperty("TEST_ID", "9f127dc6-944d-4723-8e99-cf45ed75e4ff"); - data = 0; - EXPECT_THAT(poolExposed.construct(data), Ne(NO_INDEX)); - - auto lowOutOfMemoryPtr = reinterpret_cast(poolExposed.getFirstPtr() - 1); - auto highOutOfMemoryPtr = reinterpret_cast(poolExposed.getLastPtr() + 1); - - constexpr size_t ONE_BYTE{1}; - ASSERT_THAT(sizeof(Foo), Gt(ONE_BYTE)); - auto nonAlignedPtr = reinterpret_cast(poolExposed.getFirstPtr() + sizeof(Foo) + ONE_BYTE); - - EXPECT_THAT(poolExposed.pointerToIndex(lowOutOfMemoryPtr), Eq(NO_INDEX)); - EXPECT_THAT(poolExposed.pointerToIndex(highOutOfMemoryPtr), Eq(NO_INDEX)); - EXPECT_THAT(poolExposed.pointerToIndex(nonAlignedPtr), Eq(NO_INDEX)); -} - -// private API, important for correct behaviour of all other functions -// also test whether finding the next free cell (if it exists) works correctly -TEST_F(ObjectPool_test, nextFree) -{ - ::testing::Test::RecordProperty("TEST_ID", "d554c604-186f-4372-8ce7-633df8aeddcc"); - EXPECT_THAT(poolExposed.size(), Eq(0U)); - EXPECT_THAT(poolExposed.capacity(), Eq(CAPACITY_UNSIGNED)); - EXPECT_THAT(Foo::getConstructionCounter(), Eq(0)); // no Foo objects constructed by poolExposed - - for (int i = 1; i <= CAPACITY; ++i) - { - // changes object state but does not matter, if there is some free cell it has to be found - EXPECT_THAT(poolExposed.nextFree(), Ne(NO_INDEX)); - - // populate poolExposed - auto index = poolExposed.reserve(); - EXPECT_THAT(index, Ne(NO_INDEX)); - } - EXPECT_THAT(poolExposed.size(), Eq(CAPACITY_UNSIGNED)); - - // poolExposed is full - auto index = poolExposed.reserve(); - EXPECT_THAT(index, Eq(NO_INDEX)); - EXPECT_THAT(poolExposed.size(), Eq(CAPACITY_UNSIGNED)); - - EXPECT_THAT(poolExposed.nextFree(), Eq(NO_INDEX)); -} - -TEST_F(ObjectPool_test, destructor) -{ - ::testing::Test::RecordProperty("TEST_ID", "138bcd54-54e8-47ef-a0d0-dbf464789e41"); - // allocate objects without construction - { - FooPool localPool; // local pool, to check destruction of objects - - EXPECT_THAT(localPool.size(), Eq(0U)); - EXPECT_THAT(localPool.capacity(), Eq(CAPACITY_UNSIGNED)); - EXPECT_THAT(Foo::getDestructionCounter(), Eq(0)); - EXPECT_THAT(Foo::getConstructionCounter(), Eq(0)); // no Foo objects constructed by pool - - for (int i = 1; i <= CAPACITY; ++i) - { - // populate pool - auto index = localPool.reserve(); - EXPECT_THAT(index, Ne(NO_INDEX)); - } - EXPECT_THAT(localPool.size(), Eq(CAPACITY_UNSIGNED)); - - // pool is full - auto index = localPool.reserve(); - EXPECT_THAT(index, Eq(NO_INDEX)); - EXPECT_THAT(localPool.size(), Eq(CAPACITY_UNSIGNED)); - EXPECT_THAT(Foo::getDestructionCounter(), Eq(0)); - } - // localPool destructor was called, but since the objects of the pool - // were not construced by the pool (merely allocated) no Foo destructors are called - EXPECT_THAT(Foo::getDestructionCounter(), Eq(0)); - - // default construction of Foo objects by pool - { - FooPool localPool; // local pool, to check destruction of objects - - EXPECT_THAT(localPool.size(), Eq(0U)); - EXPECT_THAT(localPool.capacity(), Eq(CAPACITY_UNSIGNED)); - EXPECT_THAT(Foo::getDestructionCounter(), Eq(0)); - EXPECT_THAT(Foo::getConstructionCounter(), Eq(0)); // no Foo objects constructed by pool - - for (int i = 1; i <= CAPACITY; ++i) - { - // populate pool - auto index = localPool.construct(); - EXPECT_THAT(index, Ne(NO_INDEX)); - } - EXPECT_THAT(localPool.size(), Eq(CAPACITY_UNSIGNED)); - - // pool is full - auto index = localPool.reserve(); - EXPECT_THAT(index, Eq(NO_INDEX)); - EXPECT_THAT(localPool.size(), Eq(CAPACITY_UNSIGNED)); - EXPECT_THAT(Foo::getDestructionCounter(), Eq(0)); - } - // localPool destructor was called, and since the objects were constructed by the pool - // Foo destructors are called CAPACITY times - EXPECT_THAT(Foo::getDestructionCounter(), Eq(CAPACITY)); - - Foo::resetConstructionCounter(); - Foo::resetDestructionCounter(); - data = 0; - - // default construction of Foo objects by pool - { - FooPool localPool; // local pool, to check destruction of objects - - EXPECT_THAT(localPool.size(), Eq(0U)); - EXPECT_THAT(localPool.capacity(), Eq(CAPACITY_UNSIGNED)); - EXPECT_THAT(Foo::getDestructionCounter(), Eq(0)); - EXPECT_THAT(Foo::getConstructionCounter(), Eq(0)); // no Foo objects constructed by pool - - for (int i = 1; i <= CAPACITY; ++i) - { - // populate pool - auto index = localPool.construct(data); - EXPECT_THAT(index, Ne(NO_INDEX)); - } - EXPECT_THAT(localPool.size(), Eq(CAPACITY_UNSIGNED)); - - // pool is full - auto index = localPool.reserve(); - EXPECT_THAT(index, Eq(NO_INDEX)); - EXPECT_THAT(localPool.size(), Eq(CAPACITY_UNSIGNED)); - EXPECT_THAT(Foo::getDestructionCounter(), Eq(0)); - } - // localPool destructor was called, and since the objects were constructed by the pool - // Foo destructors are called CAPACITY times - EXPECT_THAT(Foo::getDestructionCounter(), Eq(CAPACITY)); - EXPECT_THAT(data, Eq(INVALID)); -} - -// test all iterator functions in this test since they are closely related -TEST_F(ObjectPool_test, iterator) -{ - ::testing::Test::RecordProperty("TEST_ID", "2a8ffc21-1ec3-4319-ac2e-89db82f80c98"); - EXPECT_THAT(pool.size(), Eq(0U)); - EXPECT_THAT(pool.capacity(), Eq(CAPACITY_UNSIGNED)); - EXPECT_THAT(Foo::getConstructionCounter(), Eq(0)); // no Foo objects constructed by pool - EXPECT_THAT(pool.size(), Eq(0U)); - - data1 = 0; - auto index1 = pool.construct(data1); - EXPECT_THAT(index1, Ne(NO_INDEX)); - EXPECT_THAT(data1, Eq(1)); - EXPECT_THAT(Foo::getConstructionCounter(), Eq(1)); - EXPECT_THAT(Foo::getDestructionCounter(), Eq(0)); - EXPECT_THAT(pool.size(), Eq(1U)); - - data2 = 0; - auto index2 = pool.construct(data2); - EXPECT_THAT(index2, Ne(NO_INDEX)); - EXPECT_THAT(data2, Eq(2)); - EXPECT_THAT(Foo::getConstructionCounter(), Eq(2)); - EXPECT_THAT(Foo::getDestructionCounter(), Eq(0)); - EXPECT_THAT(pool.size(), Eq(2U)); - - data3 = 0; - auto index3 = pool.construct(data3); - EXPECT_THAT(index3, Ne(NO_INDEX)); - EXPECT_THAT(data3, Eq(3)); - EXPECT_THAT(Foo::getConstructionCounter(), Eq(3)); - EXPECT_THAT(Foo::getDestructionCounter(), Eq(0)); - EXPECT_THAT(pool.size(), Eq(3U)); - - // container is full - - // construct iterators - - auto iter1 = pool.iterator(index1); - auto iter2 = pool.iterator(index2); - auto iter3 = pool.iterator(index3); - auto iterBegin = pool.begin(); - auto iterEnd = pool.end(); - - - // comparison test (operator!=) - EXPECT_THAT(iterBegin, Ne(iterEnd)); - EXPECT_THAT(iter1, Ne(iterEnd)); - EXPECT_THAT(iter2, Ne(iterEnd)); - EXPECT_THAT(iter3, Ne(iterEnd)); - - // pairwise not equal - EXPECT_THAT(iter1, Ne(iter2)); - EXPECT_THAT(iter1, Ne(iter3)); - EXPECT_THAT(iter2, Ne(iter3)); - - EXPECT_THAT(iter1, Eq(iter1)); - EXPECT_THAT(iter2, Eq(iter2)); - EXPECT_THAT(iter3, Eq(iter3)); - EXPECT_THAT(iterBegin, Eq(iterBegin)); - EXPECT_THAT(iterEnd, Eq(iterEnd)); - - // operator++ - - // post increment - auto iter = iter1; // we need the copy since operator++ changes the iterator - EXPECT_THAT(iter++, Eq(iter1)); - - iter = iter2; - EXPECT_THAT(iter++, Eq(iter2)); - - iter = iter3; - EXPECT_THAT(iter++, Eq(iter3)); - - iter = iterBegin; - EXPECT_THAT(iter++, Eq(iterBegin)); - - iter = iterEnd; - EXPECT_THAT(iter++, Eq(iterEnd)); - - - // pre increment - iter = iter1; - EXPECT_THAT(++iter, Ne(iter1)); - - iter = iter2; - EXPECT_THAT(++iter, Ne(iter2)); - - iter = iter3; - EXPECT_THAT(++iter, Ne(iter3)); - - iter = iterBegin; - EXPECT_THAT(++iter, Ne(iterBegin)); - - iter = iterEnd; - EXPECT_THAT(++iter, Eq(iterEnd)); - - // operator* - EXPECT_THAT(((*iter1).m_data), Eq(&data1)); - EXPECT_THAT(((*iter2).m_data), Eq(&data2)); - EXPECT_THAT(((*iter3).m_data), Eq(&data3)); - - // operator-> - EXPECT_THAT(iter1->m_data, Eq(&data1)); - EXPECT_THAT(iter2->m_data, Eq(&data2)); - EXPECT_THAT(iter3->m_data, Eq(&data3)); - EXPECT_THAT(iterEnd.operator->(), Eq(nullptr)); - - // check that after CAPACITY increments we have reached end - //(to reduce potential for unbounded loops) - auto iterPre = pool.begin(); - auto iterPost = pool.begin(); - - for (size_t i = 0; i < pool.size(); ++i) - { - ++iterPre; - iterPost++; - } - ASSERT_THAT(iterPre, Eq(iterEnd)); - ASSERT_THAT(iterPost, Eq(iterEnd)); - - // we now know that the iterator increment does not lead to unbounded loops... - // test range based loop which relies on iterators internally - - std::vector count(4, 0); - int numElements = 0; - for (auto& foo : pool) - { - auto value = *foo.m_data; - if (value >= 1 && value <= 3) - { - count[static_cast(value)]++; - } - ++numElements; - } - - EXPECT_THAT(numElements, Eq(3)); - for (uint64_t i = 1U; i <= 3U; ++i) - { - EXPECT_THAT(count[i], Eq(1)); // expect each value exactly once - } - - // remove an element and iterate over pool - - ASSERT_THAT(pool.get(index2), Ne(nullptr)); - pool.remove(index2); - EXPECT_THAT(pool.size(), Eq(2U)); - - // only 1 and 3 remain in the pool - numElements = 0; - for (auto& foo : pool) - { - auto value = *foo.m_data; - if (value >= 1 && value <= 3) - { - count[static_cast(value)]++; - } - ++numElements; - } - - EXPECT_THAT(numElements, Eq(2)); - EXPECT_THAT(count[1], Eq(2)); - EXPECT_THAT(count[2], Eq(1)); // count unchanged because element 2 was removed - EXPECT_THAT(count[3], Eq(2)); - - // remove remaining elements - - ASSERT_THAT(pool.get(index1), Ne(nullptr)); - pool.remove(index1); - EXPECT_THAT(pool.size(), Eq(1U)); - - - // only 3 remains in the pool - numElements = 0; - for (auto& foo : pool) - { - auto value = *foo.m_data; - if (value >= 1 && value <= 3) - { - count[static_cast(value)]++; - } - ++numElements; - } - - EXPECT_THAT(numElements, Eq(1)); - EXPECT_THAT(count[1], Eq(2)); // count unchanged because element 1 was removed - EXPECT_THAT(count[2], Eq(1)); // count unchanged because element 2 was removed - EXPECT_THAT(count[3], Eq(3)); - - ASSERT_THAT(pool.get(index3), Ne(nullptr)); - pool.remove(index3); - EXPECT_THAT(pool.size(), Eq(0U)); - - // pool is empty - numElements = 0; - for (auto& foo : pool) - { - auto value = *foo.m_data; - if (value >= 1 && value <= 3) - { - count[static_cast(value)]++; - } - ++numElements; - } - - EXPECT_THAT(numElements, Eq(0)); - // all counts unchanged - EXPECT_THAT(count[1], Eq(2)); - EXPECT_THAT(count[2], Eq(1)); - EXPECT_THAT(count[3], Eq(3)); - - // empty pool, begin equals end - EXPECT_THAT(pool.begin(), Eq(pool.end())); -} -} // namespace diff --git a/iceoryx_posh/include/iceoryx_posh/gateway/channel.hpp b/iceoryx_posh/include/iceoryx_posh/gateway/channel.hpp index 2f5681aa9e..bbc2283f5c 100644 --- a/iceoryx_posh/include/iceoryx_posh/gateway/channel.hpp +++ b/iceoryx_posh/include/iceoryx_posh/gateway/channel.hpp @@ -18,10 +18,10 @@ #ifndef IOX_POSH_GW_CHANNEL_HPP #define IOX_POSH_GW_CHANNEL_HPP -#include "iceoryx_dust/cxx/objectpool.hpp" #include "iceoryx_posh/capro/service_description.hpp" #include "iceoryx_posh/iceoryx_posh_types.hpp" #include "iox/expected.hpp" +#include "iox/fixed_position_container.hpp" #include "iox/optional.hpp" #include @@ -56,9 +56,9 @@ template class Channel { using IceoryxTerminalPtr = std::shared_ptr; - using IceoryxTerminalPool = cxx::ObjectPool; + using IceoryxTerminalPool = FixedPositionContainer; using ExternalTerminalPtr = std::shared_ptr; - using ExternalTerminalPool = cxx::ObjectPool; + using ExternalTerminalPool = FixedPositionContainer; public: constexpr Channel(const capro::ServiceDescription& service, diff --git a/iceoryx_posh/include/iceoryx_posh/internal/gateway/channel.inl b/iceoryx_posh/include/iceoryx_posh/internal/gateway/channel.inl index b1ade5d139..9730564c6b 100644 --- a/iceoryx_posh/include/iceoryx_posh/internal/gateway/channel.inl +++ b/iceoryx_posh/include/iceoryx_posh/internal/gateway/channel.inl @@ -25,17 +25,15 @@ namespace gw { // Typedefs template -using IceoryxTerminalPool = cxx::ObjectPool; +using IceoryxTerminalPool = FixedPositionContainer; template -using ExternalTerminalPool = cxx::ObjectPool; +using ExternalTerminalPool = FixedPositionContainer; // Statics template -IceoryxTerminalPool Channel::s_iceoryxTerminals = - IceoryxTerminalPool(); +IceoryxTerminalPool Channel::s_iceoryxTerminals{}; template -ExternalTerminalPool Channel::s_externalTerminals = - ExternalTerminalPool(); +ExternalTerminalPool Channel::s_externalTerminals{}; template inline constexpr Channel::Channel( @@ -62,24 +60,24 @@ Channel::create(const capro::ServiceDescripti const IceoryxPubSubOptions& options) noexcept { // Create objects in the pool. - auto rawIceoryxTerminalPtr = s_iceoryxTerminals.create(std::forward(service), - std::forward(options)); - if (rawIceoryxTerminalPtr == nullptr) + auto rawIceoryxTerminal = s_iceoryxTerminals.emplace(std::forward(service), + std::forward(options)); + if (rawIceoryxTerminal == s_iceoryxTerminals.end()) { return err(ChannelError::OBJECT_POOL_FULL); } - auto rawExternalTerminalPtr = s_externalTerminals.create( + auto rawExternalTerminal = s_externalTerminals.emplace( service.getServiceIDString(), service.getInstanceIDString(), service.getEventIDString()); - if (rawExternalTerminalPtr == nullptr) + if (rawExternalTerminal == s_externalTerminals.end()) { return err(ChannelError::OBJECT_POOL_FULL); } // Wrap in smart pointer with custom deleter to ensure automatic cleanup. - auto iceoryxTerminalPtr = - IceoryxTerminalPtr(rawIceoryxTerminalPtr, [](IceoryxTerminal* const p) { s_iceoryxTerminals.free(p); }); - auto externalTerminalPtr = - ExternalTerminalPtr(rawExternalTerminalPtr, [](ExternalTerminal* const p) { s_externalTerminals.free(p); }); + auto iceoryxTerminalPtr = IceoryxTerminalPtr( + rawIceoryxTerminal.to_ptr(), [](IceoryxTerminal* const p) { Channel::s_iceoryxTerminals.erase(p); }); + auto externalTerminalPtr = ExternalTerminalPtr( + rawExternalTerminal.to_ptr(), [](ExternalTerminal* const p) { Channel::s_externalTerminals.erase(p); }); return ok(Channel(service, iceoryxTerminalPtr, externalTerminalPtr)); } diff --git a/iceoryx_posh/test/moduletests/test_gw_channel.cpp b/iceoryx_posh/test/moduletests/test_gw_channel.cpp index acc9e21f6c..69f42440ee 100644 --- a/iceoryx_posh/test/moduletests/test_gw_channel.cpp +++ b/iceoryx_posh/test/moduletests/test_gw_channel.cpp @@ -54,7 +54,7 @@ class ChannelTest : public Test }; // ======================================== Tests ======================================== // -TEST_F(ChannelTest, ReturnsEmptyOptionalIfObjectPoolExhausted) +TEST_F(ChannelTest, ReturnsErrorIfPoolExhausted) { ::testing::Test::RecordProperty("TEST_ID", "c4444a37-2044-4bba-aa22-9fabc0a7b5f4"); auto channel = iox::gw::Channel::create(