diff --git a/src/core/Makefile.am b/src/core/Makefile.am index 7e5fe3da9..c956ba360 100644 --- a/src/core/Makefile.am +++ b/src/core/Makefile.am @@ -336,6 +336,7 @@ HEADERS_COMMON = \ common/new.hpp \ common/non_copyable.hpp \ common/notifier.hpp \ + common/pool.hpp \ common/random.hpp \ common/random_manager.hpp \ common/settings.hpp \ diff --git a/src/core/common/message.cpp b/src/core/common/message.cpp index caf7e8c46..0accf3cd9 100644 --- a/src/core/common/message.cpp +++ b/src/core/common/message.cpp @@ -46,17 +46,10 @@ MessagePool::MessagePool(Instance &aInstance) : InstanceLocator(aInstance) #if !OPENTHREAD_CONFIG_PLATFORM_MESSAGE_MANAGEMENT , mNumFreeBuffers(kNumBuffers) - , mFreeBuffers() + , mBufferPool() #endif { -#if !OPENTHREAD_CONFIG_PLATFORM_MESSAGE_MANAGEMENT - memset(mBuffers, 0, sizeof(mBuffers)); - - for (Buffer *cur = &mBuffers[0]; cur < OT_ARRAY_END(mBuffers); cur++) - { - mFreeBuffers.Push(*cur); - } -#else +#if OPENTHREAD_CONFIG_PLATFORM_MESSAGE_MANAGEMENT otPlatMessagePoolInit(&GetInstance(), kNumBuffers, sizeof(Buffer)); #endif } @@ -118,22 +111,20 @@ Buffer *MessagePool::NewBuffer(Message::Priority aPriority) #else - buffer = mFreeBuffers.Pop(); + buffer = mBufferPool.Allocate(); + VerifyOrExit(buffer != nullptr, OT_NOOP); - if (buffer != nullptr) - { - buffer->SetNextBuffer(nullptr); - mNumFreeBuffers--; - } + mNumFreeBuffers--; + buffer->SetNextBuffer(nullptr); #endif +exit: if (buffer == nullptr) { otLogInfoMem("No available message buffer"); } -exit: return buffer; } @@ -145,7 +136,7 @@ void MessagePool::FreeBuffers(Buffer *aBuffer) #if OPENTHREAD_CONFIG_PLATFORM_MESSAGE_MANAGEMENT otPlatMessagePoolFree(&GetInstance(), aBuffer); #else - mFreeBuffers.Push(*aBuffer); + mBufferPool.Free(*aBuffer); mNumFreeBuffers++; #endif aBuffer = next; diff --git a/src/core/common/message.hpp b/src/core/common/message.hpp index 00bd5aa51..c863b750a 100644 --- a/src/core/common/message.hpp +++ b/src/core/common/message.hpp @@ -46,6 +46,7 @@ #include "common/linked_list.hpp" #include "common/locator.hpp" #include "common/non_copyable.hpp" +#include "common/pool.hpp" #include "mac/mac_types.hpp" #include "thread/link_quality.hpp" @@ -1254,9 +1255,8 @@ private: otError ReclaimBuffers(int aNumBuffers, Message::Priority aPriority); #if OPENTHREAD_CONFIG_PLATFORM_MESSAGE_MANAGEMENT == 0 - uint16_t mNumFreeBuffers; - Buffer mBuffers[kNumBuffers]; - LinkedList mFreeBuffers; + uint16_t mNumFreeBuffers; + Pool mBufferPool; #endif }; diff --git a/src/core/common/pool.hpp b/src/core/common/pool.hpp new file mode 100644 index 000000000..e2c4007a9 --- /dev/null +++ b/src/core/common/pool.hpp @@ -0,0 +1,185 @@ +/* + * Copyright (c) 2020, The OpenThread Authors. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @file + * This file includes definitions for a generic object pool. + */ + +#ifndef POOL_HPP_ +#define POOL_HPP_ + +#include "openthread-core-config.h" + +#include "common/linked_list.hpp" +#include "common/non_copyable.hpp" + +namespace ot { + +class Instance; + +/** + * @addtogroup core-pool + * + * @brief + * This module includes definitions for OpenThread object pool. + * + * @{ + * + */ + +/** + * This template class 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 Pool : private NonCopyable +{ +public: + /** + * This constructor initializes the pool. + * + */ + Pool(void) + : mFreeList() + { + for (Type &entry : mPool) + { + mFreeList.Push(entry); + } + } + + /** + * This constructor initializes the pool. + * + * This constructor 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 `InstaceLocatorInit()`. + * + * @param[in] aInstance A reference to the OpenThread instance. + * + */ + Pool(Instance &aInstance) + : mFreeList() + { + for (Type &entry : mPool) + { + entry.Init(aInstance); + mFreeList.Push(entry); + } + } + + /** + * This method allocates a new object from the pool. + * + * @returns A pointer to the newly allocated object, or nullptr if all entries from the pool are already allocated. + * + */ + Type *Allocate(void) { return mFreeList.Pop(); } + + /** + * This method frees a previously allocated object. + * + * The @p aEntry MUST be an entry from the pool previously allocated using `Allocate()` method and not yet freed. + * An already freed entry MUST not be freed again. + * + * @param[in] aEntry The pool object entry to free. + * + */ + void Free(Type &aEntry) { mFreeList.Push(aEntry); } + + /** + * This method returns the pool size. + * + * @returns The pool size (maximum number of objects in the pool). + * + */ + uint16_t GetSize(void) const { return kPoolSize; } + + /** + * This method 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 (&mPool[0] <= &aObject) && (&aObject < OT_ARRAY_END(mPool)); } + + /** + * This method returns the associated index of a given entry from the pool. + * + * The @p aEntry MUST be from the pool, otherwise the behavior of this method is undefined. + * + * @param[in] aEntry A reference to an entry from the pool. + * + * @returns The associated index of @p aEntry. + * + */ + uint16_t GetIndexOf(const Type &aEntry) const { return static_cast(&aEntry - mPool); } + + /** + * This method retrieves a pool entry at a given index. + * + * The @p aIndex MUST be from an earlier call to `GetIndexOf()`. + * + * @param[in] aIndex An index. + * + * @returns A reference to entry at index @p aIndex. + * + */ + Type &GetEntryAt(uint16_t aIndex) { return mPool[aIndex]; } + + /** + * This method retrieves a pool entry at a given index. + * + * The @p aIndex MUST be from an earlier call to `GetIndexOf()`. + * + * @param[in] aIndex An index. + * + * @returns A reference to entry at index @p aIndex. + * + */ + const Type &GetEntryAt(uint16_t aIndex) const { return mPool[aIndex]; } + +private: + LinkedList mFreeList; + Type mPool[kPoolSize]; +}; + +/** + * @} + * + */ + +} // namespace ot + +#endif // POOL_HPP_ diff --git a/src/core/net/netif.cpp b/src/core/net/netif.cpp index 93de21faa..a639fcd31 100644 --- a/src/core/net/netif.cpp +++ b/src/core/net/netif.cpp @@ -86,16 +86,9 @@ Netif::Netif(Instance &aInstance) , mMulticastPromiscuous(false) , mAddressCallback(nullptr) , mAddressCallbackContext(nullptr) + , mExtUnicastAddressPool() + , mExtMulticastAddressPool() { - for (NetifUnicastAddress &entry : mExtUnicastAddresses) - { - entry.MarkAsNotInUse(); - } - - for (NetifMulticastAddress &entry : mExtMulticastAddresses) - { - entry.MarkAsNotInUse(); - } } bool Netif::IsMulticastSubscribed(const Address &aAddress) const @@ -290,6 +283,11 @@ exit: return; } +bool Netif::IsMulticastAddressExternal(const NetifMulticastAddress &aAddress) const +{ + return mExtMulticastAddressPool.IsPoolEntry(aAddress); +} + void Netif::SubscribeMulticast(NetifMulticastAddress &aAddress) { SuccessOrExit(mMulticastAddresses.Add(aAddress)); @@ -321,6 +319,7 @@ otError Netif::SubscribeExternalMulticast(const Address &aAddress) otError error = OT_ERROR_NONE; NetifMulticastAddress &linkLocalAllRoutersAddress = static_cast( const_cast(kLinkLocalAllRoutersMulticastAddress)); + NetifMulticastAddress *entry; VerifyOrExit(!IsMulticastSubscribed(aAddress), error = OT_ERROR_ALREADY); @@ -333,18 +332,12 @@ otError Netif::SubscribeExternalMulticast(const Address &aAddress) VerifyOrExit(cur->GetAddress() != aAddress, error = OT_ERROR_INVALID_ARGS); } - for (NetifMulticastAddress &entry : mExtMulticastAddresses) - { - if (!entry.IsInUse()) - { - entry.mAddress = aAddress; - mMulticastAddresses.Push(entry); - Get().Signal(kEventIp6MulticastSubscribed); - ExitNow(); - } - } + entry = mExtMulticastAddressPool.Allocate(); + VerifyOrExit(entry != nullptr, error = OT_ERROR_NO_BUFS); - error = OT_ERROR_NO_BUFS; + entry->mAddress = aAddress; + mMulticastAddresses.Push(*entry); + Get().Signal(kEventIp6MulticastSubscribed); exit: return error; @@ -363,7 +356,7 @@ otError Netif::UnsubscribeExternalMulticast(const Address &aAddress) mMulticastAddresses.PopAfter(prev); - entry->MarkAsNotInUse(); + mExtMulticastAddressPool.Free(*entry); Get().Signal(kEventIp6MulticastUnsubscribed); @@ -373,11 +366,15 @@ exit: void Netif::UnsubscribeAllExternalMulticastAddresses(void) { - for (NetifMulticastAddress &entry : mExtMulticastAddresses) + NetifMulticastAddress *next; + + for (NetifMulticastAddress *entry = mMulticastAddresses.GetHead(); entry != nullptr; entry = next) { - if (entry.IsInUse()) + next = entry->GetNext(); + + if (IsMulticastAddressExternal(*entry)) { - IgnoreError(UnsubscribeExternalMulticast(entry.GetAddress())); + IgnoreError(UnsubscribeExternalMulticast(entry->GetAddress())); } } } @@ -417,36 +414,30 @@ exit: otError Netif::AddExternalUnicastAddress(const NetifUnicastAddress &aAddress) { otError error = OT_ERROR_NONE; - NetifUnicastAddress *existingEntry; + NetifUnicastAddress *entry; NetifUnicastAddress *prev; - existingEntry = mUnicastAddresses.FindMatching(aAddress.GetAddress(), prev); + entry = mUnicastAddresses.FindMatching(aAddress.GetAddress(), prev); - if (existingEntry != nullptr) + if (entry != nullptr) { - VerifyOrExit(IsUnicastAddressExternal(*existingEntry), error = OT_ERROR_ALREADY); + VerifyOrExit(IsUnicastAddressExternal(*entry), error = OT_ERROR_ALREADY); - existingEntry->mPrefixLength = aAddress.mPrefixLength; - existingEntry->mAddressOrigin = aAddress.mAddressOrigin; - existingEntry->mPreferred = aAddress.mPreferred; - existingEntry->mValid = aAddress.mValid; + entry->mPrefixLength = aAddress.mPrefixLength; + entry->mAddressOrigin = aAddress.mAddressOrigin; + entry->mPreferred = aAddress.mPreferred; + entry->mValid = aAddress.mValid; ExitNow(); } VerifyOrExit(!aAddress.GetAddress().IsLinkLocal(), error = OT_ERROR_INVALID_ARGS); - for (NetifUnicastAddress &entry : mExtUnicastAddresses) - { - if (!entry.IsInUse()) - { - entry = aAddress; - mUnicastAddresses.Push(entry); - Get().Signal(kEventIp6AddressAdded); - ExitNow(); - } - } + entry = mExtUnicastAddressPool.Allocate(); + VerifyOrExit(entry != nullptr, error = OT_ERROR_NO_BUFS); - error = OT_ERROR_NO_BUFS; + *entry = aAddress; + mUnicastAddresses.Push(*entry); + Get().Signal(kEventIp6AddressAdded); exit: return error; @@ -464,8 +455,7 @@ otError Netif::RemoveExternalUnicastAddress(const Address &aAddress) VerifyOrExit(IsUnicastAddressExternal(*entry), error = OT_ERROR_INVALID_ARGS); mUnicastAddresses.PopAfter(prev); - - entry->MarkAsNotInUse(); + mExtUnicastAddressPool.Free(*entry); Get().Signal(kEventIp6AddressRemoved); exit: @@ -474,11 +464,15 @@ exit: void Netif::RemoveAllExternalUnicastAddresses(void) { - for (NetifUnicastAddress &entry : mExtUnicastAddresses) + NetifUnicastAddress *next; + + for (NetifUnicastAddress *entry = mUnicastAddresses.GetHead(); entry != nullptr; entry = next) { - if (entry.IsInUse()) + next = entry->GetNext(); + + if (IsUnicastAddressExternal(*entry)) { - IgnoreError(RemoveExternalUnicastAddress(entry.GetAddress())); + IgnoreError(RemoveExternalUnicastAddress(entry->GetAddress())); } } } @@ -490,5 +484,10 @@ bool Netif::HasUnicastAddress(const Address &aAddress) const return mUnicastAddresses.FindMatching(aAddress, prev) != nullptr; } +bool Netif::IsUnicastAddressExternal(const NetifUnicastAddress &aAddress) const +{ + return mExtUnicastAddressPool.IsPoolEntry(aAddress); +} + } // namespace Ip6 } // namespace ot diff --git a/src/core/net/netif.hpp b/src/core/net/netif.hpp index 7df7312d9..fa2d81cfa 100644 --- a/src/core/net/netif.hpp +++ b/src/core/net/netif.hpp @@ -103,11 +103,6 @@ public: private: bool Matches(const Address &aAddress) const { return GetAddress() == aAddress; } - - // In an unused/available entry (i.e., entry not present in a linked - // list), the next pointer is set to point back to the entry itself. - bool IsInUse(void) const { return GetNext() != this; } - void MarkAsNotInUse(void) { SetNext(this); } }; /** @@ -159,11 +154,6 @@ public: private: bool Matches(const Address &aAddress) const { return GetAddress() == aAddress; } - - // In an unused/available entry (i.e., entry not present in a linked - // list), the next pointer is set to point back to the entry itself. - bool IsInUse(void) const { return GetNext() != this; } - void MarkAsNotInUse(void) { mNext = this; } }; /** @@ -254,10 +244,7 @@ public: * @retval FALSE The address is not an external address (it is an OpenThread internal address). * */ - bool IsUnicastAddressExternal(const NetifUnicastAddress &aAddress) const - { - return (&mExtUnicastAddresses[0] <= &aAddress) && (&aAddress < OT_ARRAY_END(mExtUnicastAddresses)); - } + bool IsUnicastAddressExternal(const NetifUnicastAddress &aAddress) const; /** * This method adds an external (to OpenThread) unicast address to the network interface. @@ -336,10 +323,7 @@ public: * @retval FALSE The address is not an external address (it is an OpenThread internal address). * */ - bool IsMulticastAddressExternal(const NetifMulticastAddress &aAddress) const - { - return (&mExtMulticastAddresses[0] <= &aAddress) && (&aAddress < OT_ARRAY_END(mExtMulticastAddresses)); - } + bool IsMulticastAddressExternal(const NetifMulticastAddress &aAddress) const; /** * This method subscribes the network interface to a multicast address. @@ -446,8 +430,8 @@ private: otIp6AddressCallback mAddressCallback; void * mAddressCallbackContext; - NetifUnicastAddress mExtUnicastAddresses[OPENTHREAD_CONFIG_IP6_MAX_EXT_UCAST_ADDRS]; - NetifMulticastAddress mExtMulticastAddresses[OPENTHREAD_CONFIG_IP6_MAX_EXT_MCAST_ADDRS]; + Pool mExtUnicastAddressPool; + Pool mExtMulticastAddressPool; static const otNetifMulticastAddress kRealmLocalAllMplForwardersMulticastAddress; static const otNetifMulticastAddress kLinkLocalAllNodesMulticastAddress; diff --git a/src/core/thread/address_resolver.cpp b/src/core/thread/address_resolver.cpp index 6a796ca88..baa74bcfd 100644 --- a/src/core/thread/address_resolver.cpp +++ b/src/core/thread/address_resolver.cpp @@ -57,20 +57,14 @@ AddressResolver::AddressResolver(Instance &aInstance) , mAddressError(OT_URI_PATH_ADDRESS_ERROR, &AddressResolver::HandleAddressError, this) , mAddressQuery(OT_URI_PATH_ADDRESS_QUERY, &AddressResolver::HandleAddressQuery, this) , mAddressNotification(OT_URI_PATH_ADDRESS_NOTIFY, &AddressResolver::HandleAddressNotification, this) + , mCacheEntryPool(aInstance) , mCachedList() , mSnoopedList() , mQueryList() , mQueryRetryList() - , mUnusedList() , mIcmpHandler(&AddressResolver::HandleIcmpReceive, this) , mTimer(aInstance, AddressResolver::HandleTimer, this) { - for (CacheEntry *entry = &mCacheEntries[0]; entry < OT_ARRAY_END(mCacheEntries); entry++) - { - entry->Init(GetInstance()); - mUnusedList.Push(*entry); - } - Get().AddResource(mAddressError); Get().AddResource(mAddressQuery); Get().AddResource(mAddressNotification); @@ -94,7 +88,7 @@ void AddressResolver::Clear(void) Get().HandleResolved(entry->GetTarget(), OT_ERROR_DROP); } - mUnusedList.Push(*entry); + mCacheEntryPool.Free(*entry); } } } @@ -210,7 +204,7 @@ void AddressResolver::Remove(Mac::ShortAddress aRloc16, bool aMatchRouterId) (!aMatchRouterId && (entry->GetRloc16() == aRloc16))) { RemoveCacheEntry(*entry, *list, prev, aMatchRouterId ? kReasonRemovingRouterId : kReasonRemovingRloc16); - mUnusedList.Push(*entry); + mCacheEntryPool.Free(*entry); // If the entry is removed from list, we keep the same // `prev` pointer. @@ -256,7 +250,7 @@ void AddressResolver::Remove(const Ip6::Address &aEid, Reason aReason) VerifyOrExit(entry != nullptr, OT_NOOP); RemoveCacheEntry(*entry, *list, prev, aReason); - mUnusedList.Push(*entry); + mCacheEntryPool.Free(*entry); exit: return; @@ -269,7 +263,7 @@ AddressResolver::CacheEntry *AddressResolver::NewCacheEntry(bool aSnoopedEntry) CacheEntryList *lists[] = {&mSnoopedList, &mQueryRetryList, &mQueryList, &mCachedList}; // The following order is used when trying to allocate a new cache - // entry: First the unused list is checked, followed by the list + // entry: First the cache pool is checked, followed by the list // of snooped entries, then query-retry list (entries in delay // retry timeout wait due to a prior query failing to get a // response), then the query list (entries actively querying and @@ -279,7 +273,7 @@ AddressResolver::CacheEntry *AddressResolver::NewCacheEntry(bool aSnoopedEntry) // can be evicted (e.g., first time query entries can not be // evicted till timeout). - newEntry = mUnusedList.Pop(); + newEntry = mCacheEntryPool.Allocate(); VerifyOrExit(newEntry == nullptr, OT_NOOP); for (size_t index = 0; index < OT_ARRAY_LENGTH(lists); index++) @@ -515,7 +509,7 @@ otError AddressResolver::Resolve(const Ip6::Address &aEid, uint16_t &aRloc16) entry->SetTimeout(kAddressQueryTimeout); error = SendAddressQuery(aEid); - VerifyOrExit(error == OT_ERROR_NONE, mUnusedList.Push(*entry)); + VerifyOrExit(error == OT_ERROR_NONE, mCacheEntryPool.Free(*entry)); if (list == nullptr) { @@ -1059,18 +1053,18 @@ void AddressResolver::CacheEntry::Init(Instance &aInstance) AddressResolver::CacheEntry *AddressResolver::CacheEntry::GetNext(void) { - return (mNextIndex == kNoNextIndex) ? nullptr : &Get().mCacheEntries[mNextIndex]; + return (mNextIndex == kNoNextIndex) ? nullptr : &Get().GetCacheEntryPool().GetEntryAt(mNextIndex); } const AddressResolver::CacheEntry *AddressResolver::CacheEntry::GetNext(void) const { - return (mNextIndex == kNoNextIndex) ? nullptr : &Get().mCacheEntries[mNextIndex]; + return (mNextIndex == kNoNextIndex) ? nullptr : &Get().GetCacheEntryPool().GetEntryAt(mNextIndex); } void AddressResolver::CacheEntry::SetNext(CacheEntry *aEntry) { VerifyOrExit(aEntry != nullptr, mNextIndex = kNoNextIndex); - mNextIndex = static_cast(aEntry - Get().mCacheEntries); + mNextIndex = Get().GetCacheEntryPool().GetIndexOf(*aEntry); exit: return; diff --git a/src/core/thread/address_resolver.hpp b/src/core/thread/address_resolver.hpp index c86b7cea5..f2fa91c1c 100644 --- a/src/core/thread/address_resolver.hpp +++ b/src/core/thread/address_resolver.hpp @@ -250,7 +250,8 @@ private: } mInfo; }; - typedef LinkedList CacheEntryList; + typedef Pool CacheEntryPool; + typedef LinkedList CacheEntryList; enum EntryChange { @@ -271,6 +272,8 @@ private: kReasonRemovingEid, }; + CacheEntryPool &GetCacheEntryPool(void) { return mCacheEntryPool; } + void Remove(Mac::ShortAddress aRloc16, bool aMatchRouterId); void Remove(const Ip6::Address &aEid, Reason aReason); CacheEntry *FindCacheEntry(const Ip6::Address &aEid, CacheEntryList *&aList, CacheEntry *&aPrevEntry); @@ -317,12 +320,11 @@ private: Coap::Resource mAddressQuery; Coap::Resource mAddressNotification; - CacheEntry mCacheEntries[kCacheEntries]; + CacheEntryPool mCacheEntryPool; CacheEntryList mCachedList; CacheEntryList mSnoopedList; CacheEntryList mQueryList; CacheEntryList mQueryRetryList; - CacheEntryList mUnusedList; Ip6::IcmpHandler mIcmpHandler; TimerMilli mTimer; diff --git a/tests/unit/CMakeLists.txt b/tests/unit/CMakeLists.txt index 03cc44c57..d10f0aefc 100644 --- a/tests/unit/CMakeLists.txt +++ b/tests/unit/CMakeLists.txt @@ -381,6 +381,28 @@ target_link_libraries(test-network-data add_test(NAME test-network-data COMMAND test-network-data) +add_executable(test-pool + ${COMMON_SOURCES} + test_pool.cpp +) + +target_include_directories(test-pool + PRIVATE + ${COMMON_INCLUDES} +) + +target_compile_options(test-pool + PRIVATE + ${COMMON_COMPILE_OPTIONS} +) + +target_link_libraries(test-pool + PRIVATE + ${COMMON_LIBS} +) + +add_test(NAME test-pool COMMAND test-pool) + add_executable(test-priority-queue ${COMMON_SOURCES} test_priority_queue.cpp @@ -492,7 +514,7 @@ target_link_libraries(test-timer add_test(NAME test-timer COMMAND test-timer) set_target_properties( - test-aes test-child test-child-table test-flash test-heap test-hmac-sha256 test-ip6-address test-link-quality test-linked-list test-lowpan test-mac-frame test-message test-message-queue test-netif test-network-data test-priority-queue test-pskc test-steering-data test-string test-timer + test-aes test-child test-child-table test-flash test-heap test-hmac-sha256 test-ip6-address test-link-quality test-linked-list test-lowpan test-mac-frame test-message test-message-queue test-netif test-network-data test-pool test-priority-queue test-pskc test-steering-data test-string test-timer PROPERTIES C_STANDARD 99 CXX_STANDARD 11 diff --git a/tests/unit/Makefile.am b/tests/unit/Makefile.am index 0e3e9b1b9..7aac4bfb5 100644 --- a/tests/unit/Makefile.am +++ b/tests/unit/Makefile.am @@ -121,6 +121,7 @@ check_PROGRAMS += \ test-message-queue \ test-netif \ test-network-data \ + test-pool \ test-priority-queue \ test-pskc \ test-steering-data \ @@ -217,6 +218,9 @@ test_netif_SOURCES = $(COMMON_SOURCES) test_netif.cpp test_network_data_LDADD = $(COMMON_LDADD) test_network_data_SOURCES = $(COMMON_SOURCES) test_network_data.cpp +test_pool_LDADD = $(COMMON_LDADD) +test_pool_SOURCES = $(COMMON_SOURCES) test_pool.cpp + test_priority_queue_LDADD = $(COMMON_LDADD) test_priority_queue_SOURCES = $(COMMON_SOURCES) test_priority_queue.cpp diff --git a/tests/unit/test_pool.cpp b/tests/unit/test_pool.cpp new file mode 100644 index 000000000..6ab037f01 --- /dev/null +++ b/tests/unit/test_pool.cpp @@ -0,0 +1,136 @@ +/* + * Copyright (c) 2020, The OpenThread Authors. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "test_platform.h" + +#include + +#include "common/instance.hpp" +#include "common/pool.hpp" + +#include "test_util.h" + +struct EntryBase +{ + EntryBase *mNext; +}; + +struct Entry : public EntryBase, ot::LinkedListEntry +{ +public: + Entry(void) + : mInitWithInstance(false) + { + } + + void Init(ot::Instance &) { mInitWithInstance = true; } + + bool IsInitializedWithInstance(void) const { return mInitWithInstance; } + +private: + bool mInitWithInstance; +}; + +enum : uint16_t +{ + kPoolSize = 11, +}; + +typedef ot::Pool EntryPool; + +static Entry sNonPoolEntry; + +void VerifyEntry(EntryPool &aPool, const Entry &aEntry, bool aInitWithInstance) +{ + uint16_t index; + const EntryPool &constPool = const_cast(aPool); + + VerifyOrQuit(aPool.IsPoolEntry(aEntry), "Pool::IsPoolEntry() failed"); + VerifyOrQuit(!aPool.IsPoolEntry(sNonPoolEntry), "Pool::IsPoolEntry() succeeded for non-pool entry"); + + index = aPool.GetIndexOf(aEntry); + VerifyOrQuit(&aPool.GetEntryAt(index) == &aEntry, "Pool::GetEntryAt() failed"); + VerifyOrQuit(&constPool.GetEntryAt(index) == &aEntry, "Pool::GetEntryAt() failed"); + + VerifyOrQuit(aEntry.IsInitializedWithInstance() == aInitWithInstance, "Pool did not correctly Init() entry"); +} + +void TestPool(EntryPool &aPool, bool aInitWithInstance) +{ + Entry *entries[kPoolSize]; + + VerifyOrQuit(aPool.GetSize() == kPoolSize, "Pool::GetSize() failed"); + + for (uint16_t i = 0; i < kPoolSize; i++) + { + entries[i] = aPool.Allocate(); + VerifyOrQuit(entries[i] != nullptr, "Pool::Allocate() failed"); + + VerifyEntry(aPool, *entries[i], aInitWithInstance); + } + + for (uint16_t numEntriesToFree = 1; numEntriesToFree <= kPoolSize; numEntriesToFree++) + { + VerifyOrQuit(aPool.Allocate() == nullptr, "Pool::Allocate() did not fail when all pool entries were allocated"); + + for (uint16_t i = 0; i < numEntriesToFree; i++) + { + VerifyEntry(aPool, *entries[i], aInitWithInstance); + aPool.Free(*entries[i]); + } + + for (uint16_t i = 0; i < numEntriesToFree; i++) + { + entries[i] = aPool.Allocate(); + VerifyOrQuit(entries[i] != nullptr, "Pool::Allocate() failed"); + + VerifyEntry(aPool, *entries[i], aInitWithInstance); + } + } + + VerifyOrQuit(aPool.Allocate() == nullptr, "Pool::Allocate() did not fail when all pool entries were allocated"); +} + +void TestPool(void) +{ + ot::Instance *instance = testInitInstance(); + EntryPool pool1; + EntryPool pool2(*instance); + + TestPool(pool1, /* aInitWithInstance */ false); + TestPool(pool2, /* aInitWithInstance */ true); + + testFreeInstance(instance); +} + +int main(void) +{ + TestPool(); + printf("All tests passed\n"); + return 0; +}