[pool] introducing a generic object pool class (#5193)

This commit adds a template `Pool<Type, kPoolSize>` class representing
an object pool. `Message`, `Netif`, and `AddressResolver` classes are
respectively updated to use `Pool` for managing their buffer pool,
external unicast/multicast address pool, and address cache entry pool.
This commit also adds a unit test for the `Pool` class.
This commit is contained in:
Abtin Keshavarzian
2020-07-07 17:59:25 -07:00
committed by GitHub
parent f7e16fec24
commit e29143b089
11 changed files with 426 additions and 108 deletions
+1
View File
@@ -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 \
+8 -17
View File
@@ -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;
+3 -3
View File
@@ -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<Buffer> mFreeBuffers;
uint16_t mNumFreeBuffers;
Pool<Buffer, kNumBuffers> mBufferPool;
#endif
};
+185
View File
@@ -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 Type, uint16_t kPoolSize> 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<uint16_t>(&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<Type> mFreeList;
Type mPool[kPoolSize];
};
/**
* @}
*
*/
} // namespace ot
#endif // POOL_HPP_
+47 -48
View File
@@ -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<NetifMulticastAddress &>(
const_cast<otNetifMulticastAddress &>(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<Notifier>().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<Notifier>().Signal(kEventIp6MulticastSubscribed);
exit:
return error;
@@ -363,7 +356,7 @@ otError Netif::UnsubscribeExternalMulticast(const Address &aAddress)
mMulticastAddresses.PopAfter(prev);
entry->MarkAsNotInUse();
mExtMulticastAddressPool.Free(*entry);
Get<Notifier>().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<Notifier>().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<Notifier>().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<Notifier>().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
+4 -20
View File
@@ -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<NetifUnicastAddress, OPENTHREAD_CONFIG_IP6_MAX_EXT_UCAST_ADDRS> mExtUnicastAddressPool;
Pool<NetifMulticastAddress, OPENTHREAD_CONFIG_IP6_MAX_EXT_MCAST_ADDRS> mExtMulticastAddressPool;
static const otNetifMulticastAddress kRealmLocalAllMplForwardersMulticastAddress;
static const otNetifMulticastAddress kLinkLocalAllNodesMulticastAddress;
+10 -16
View File
@@ -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<Coap::Coap>().AddResource(mAddressError);
Get<Coap::Coap>().AddResource(mAddressQuery);
Get<Coap::Coap>().AddResource(mAddressNotification);
@@ -94,7 +88,7 @@ void AddressResolver::Clear(void)
Get<MeshForwarder>().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<AddressResolver>().mCacheEntries[mNextIndex];
return (mNextIndex == kNoNextIndex) ? nullptr : &Get<AddressResolver>().GetCacheEntryPool().GetEntryAt(mNextIndex);
}
const AddressResolver::CacheEntry *AddressResolver::CacheEntry::GetNext(void) const
{
return (mNextIndex == kNoNextIndex) ? nullptr : &Get<AddressResolver>().mCacheEntries[mNextIndex];
return (mNextIndex == kNoNextIndex) ? nullptr : &Get<AddressResolver>().GetCacheEntryPool().GetEntryAt(mNextIndex);
}
void AddressResolver::CacheEntry::SetNext(CacheEntry *aEntry)
{
VerifyOrExit(aEntry != nullptr, mNextIndex = kNoNextIndex);
mNextIndex = static_cast<uint16_t>(aEntry - Get<AddressResolver>().mCacheEntries);
mNextIndex = Get<AddressResolver>().GetCacheEntryPool().GetIndexOf(*aEntry);
exit:
return;
+5 -3
View File
@@ -250,7 +250,8 @@ private:
} mInfo;
};
typedef LinkedList<CacheEntry> CacheEntryList;
typedef Pool<CacheEntry, kCacheEntries> CacheEntryPool;
typedef LinkedList<CacheEntry> 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;
+23 -1
View File
@@ -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
+4
View File
@@ -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
+136
View File
@@ -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 <openthread/config.h>
#include "common/instance.hpp"
#include "common/pool.hpp"
#include "test_util.h"
struct EntryBase
{
EntryBase *mNext;
};
struct Entry : public EntryBase, ot::LinkedListEntry<Entry>
{
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<Entry, kPoolSize> EntryPool;
static Entry sNonPoolEntry;
void VerifyEntry(EntryPool &aPool, const Entry &aEntry, bool aInitWithInstance)
{
uint16_t index;
const EntryPool &constPool = const_cast<const EntryPool &>(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;
}