mirror of
https://github.com/espressif/openthread.git
synced 2026-06-05 21:14:49 +00:00
[bbr] simplify MulticastListenersTable implementation (#13036)
This commit simplifies the `MulticastListenersTable` by replacing the custom heap-based sorting logic with a standard `Array` and integrating an internal `TimerMilli` (`mTimer`) to handle entry expirations. Previously, the table maintained a min-heap based on expiration times (`FixHeap`, `SiftHeapElemDown`, `SiftHeapElemUp`) and required external calls to `Expire()` every second. The new implementation uses `mListeners.FindMatching()` and `mListeners.RemoveAllMatching()`, significantly reducing code complexity and maintenance overhead. The unit tests in `test_multicast_listeners_table.cpp` are also updated to reflect the simplified model
This commit is contained in:
committed by
GitHub
parent
4c12431b68
commit
3f82c1dfa5
@@ -110,10 +110,6 @@ void Manager::HandleNotifierEvents(Events aEvents)
|
||||
|
||||
void Manager::HandleTimer(void)
|
||||
{
|
||||
#if OPENTHREAD_CONFIG_BACKBONE_ROUTER_MULTICAST_ROUTING_ENABLE
|
||||
mMulticastListenersTable.Expire();
|
||||
#endif
|
||||
|
||||
#if OPENTHREAD_CONFIG_BACKBONE_ROUTER_DUA_NDPROXYING_ENABLE
|
||||
mNdProxyTable.HandleTimer();
|
||||
#endif
|
||||
|
||||
@@ -43,108 +43,95 @@ namespace BackboneRouter {
|
||||
|
||||
RegisterLogModule("BbrMlt");
|
||||
|
||||
MulticastListenersTable::MulticastListenersTable(Instance &aInstance)
|
||||
: InstanceLocator(aInstance)
|
||||
, mTimer(aInstance)
|
||||
{
|
||||
}
|
||||
|
||||
Error MulticastListenersTable::Add(const Ip6::Address &aAddress, Time aExpireTime)
|
||||
{
|
||||
Error error = kErrorNone;
|
||||
Error error = kErrorNone;
|
||||
bool isNew = false;
|
||||
Listener *entry;
|
||||
|
||||
VerifyOrExit(aAddress.IsMulticastLargerThanRealmLocal(), error = kErrorInvalidArgs);
|
||||
|
||||
for (uint16_t i = 0; i < mNumValidListeners; i++)
|
||||
{
|
||||
Listener &listener = mListeners[i];
|
||||
entry = mListeners.FindMatching(aAddress);
|
||||
|
||||
if (listener.GetAddress() == aAddress)
|
||||
{
|
||||
listener.SetExpireTime(aExpireTime);
|
||||
FixHeap(i);
|
||||
ExitNow();
|
||||
}
|
||||
if (entry == nullptr)
|
||||
{
|
||||
entry = mListeners.PushBack();
|
||||
isNew = true;
|
||||
}
|
||||
|
||||
VerifyOrExit(mNumValidListeners < GetArrayLength(mListeners), error = kErrorNoBufs);
|
||||
VerifyOrExit(entry != nullptr, error = kErrorNoBufs);
|
||||
|
||||
mListeners[mNumValidListeners].SetAddress(aAddress);
|
||||
mListeners[mNumValidListeners].SetExpireTime(aExpireTime);
|
||||
mNumValidListeners++;
|
||||
entry->mAddress = aAddress;
|
||||
entry->mExpireTime = aExpireTime;
|
||||
|
||||
FixHeap(mNumValidListeners - 1);
|
||||
mTimer.FireAtIfEarlier(aExpireTime);
|
||||
|
||||
mCallback.InvokeIfSet(MapEnum(Listener::kEventAdded), &aAddress);
|
||||
if (isNew)
|
||||
{
|
||||
InvokeCallback(kEventAdded, aAddress);
|
||||
}
|
||||
|
||||
exit:
|
||||
Log(kAdd, aAddress, aExpireTime, error);
|
||||
CheckInvariants();
|
||||
return error;
|
||||
}
|
||||
|
||||
void MulticastListenersTable::Remove(const Ip6::Address &aAddress)
|
||||
{
|
||||
Error error = kErrorNotFound;
|
||||
Error error = kErrorNone;
|
||||
Listener *entry;
|
||||
|
||||
for (uint16_t i = 0; i < mNumValidListeners; i++)
|
||||
{
|
||||
Listener &listener = mListeners[i];
|
||||
entry = mListeners.FindMatching(aAddress);
|
||||
VerifyOrExit(entry != nullptr, error = kErrorNotFound);
|
||||
|
||||
if (listener.GetAddress() == aAddress)
|
||||
{
|
||||
mNumValidListeners--;
|
||||
mListeners.Remove(*entry);
|
||||
|
||||
if (i != mNumValidListeners)
|
||||
{
|
||||
listener = mListeners[mNumValidListeners];
|
||||
FixHeap(i);
|
||||
}
|
||||
|
||||
mCallback.InvokeIfSet(MapEnum(Listener::kEventRemoved), &aAddress);
|
||||
|
||||
ExitNow(error = kErrorNone);
|
||||
}
|
||||
}
|
||||
InvokeCallback(kEventRemoved, aAddress);
|
||||
|
||||
exit:
|
||||
Log(kRemove, aAddress, TimeMilli(0), error);
|
||||
CheckInvariants();
|
||||
}
|
||||
|
||||
void MulticastListenersTable::Expire(void)
|
||||
void MulticastListenersTable::HandleTimer(void)
|
||||
{
|
||||
TimeMilli now = TimerMilli::GetNow();
|
||||
Ip6::Address address;
|
||||
NextFireTime nextTime(now);
|
||||
|
||||
while (mNumValidListeners > 0 && now >= mListeners[0].GetExpireTime())
|
||||
for (uint16_t i = 0; i < mListeners.GetLength();)
|
||||
{
|
||||
Log(kExpire, mListeners[0].GetAddress(), mListeners[0].GetExpireTime(), kErrorNone);
|
||||
address = mListeners[0].GetAddress();
|
||||
|
||||
mNumValidListeners--;
|
||||
|
||||
if (mNumValidListeners > 0)
|
||||
if (mListeners[i].mExpireTime <= now)
|
||||
{
|
||||
mListeners[0] = mListeners[mNumValidListeners];
|
||||
FixHeap(0);
|
||||
}
|
||||
Ip6::Address address = mListeners[i].mAddress;
|
||||
|
||||
mCallback.InvokeIfSet(MapEnum(Listener::kEventRemoved), &address);
|
||||
Log(kExpire, address, mListeners[i].mExpireTime, kErrorNone);
|
||||
mListeners.Remove(mListeners[i]);
|
||||
InvokeCallback(kEventRemoved, address);
|
||||
|
||||
// When the entry is removed from the array it is replaced
|
||||
// with the last element. In this case, we do not
|
||||
// increment `i`.
|
||||
}
|
||||
else
|
||||
{
|
||||
nextTime.UpdateIfEarlier(mListeners[i].mExpireTime);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
CheckInvariants();
|
||||
mTimer.FireAtIfEarlier(nextTime);
|
||||
}
|
||||
|
||||
bool MulticastListenersTable::Has(const Ip6::Address &aAddress) const
|
||||
bool MulticastListenersTable::Has(const Ip6::Address &aAddress) const { return mListeners.ContainsMatching(aAddress); }
|
||||
|
||||
void MulticastListenersTable::InvokeCallback(Event aEvent, const Ip6::Address &aAddress) const
|
||||
{
|
||||
bool has = false;
|
||||
|
||||
for (uint16_t i = 0; i < mNumValidListeners; i++)
|
||||
{
|
||||
if (mListeners[i].GetAddress() == aAddress)
|
||||
{
|
||||
has = true;
|
||||
ExitNow();
|
||||
}
|
||||
}
|
||||
|
||||
exit:
|
||||
return has;
|
||||
mCallback.InvokeIfSet(aEvent, &aAddress);
|
||||
}
|
||||
|
||||
#if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_DEBG)
|
||||
@@ -167,146 +154,43 @@ void MulticastListenersTable::Log(Action aAction,
|
||||
void MulticastListenersTable::Log(Action, const Ip6::Address &, TimeMilli, Error) const {}
|
||||
#endif
|
||||
|
||||
void MulticastListenersTable::FixHeap(uint16_t aIndex)
|
||||
{
|
||||
if (!SiftHeapElemDown(aIndex))
|
||||
{
|
||||
SiftHeapElemUp(aIndex);
|
||||
}
|
||||
}
|
||||
|
||||
void MulticastListenersTable::CheckInvariants(void) const
|
||||
{
|
||||
#if OPENTHREAD_EXAMPLES_SIMULATION && OPENTHREAD_CONFIG_ASSERT_ENABLE
|
||||
for (uint16_t child = 1; child < mNumValidListeners; ++child)
|
||||
{
|
||||
uint16_t parent = (child - 1) / 2;
|
||||
|
||||
OT_ASSERT(!(mListeners[child] < mListeners[parent]));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
bool MulticastListenersTable::SiftHeapElemDown(uint16_t aIndex)
|
||||
{
|
||||
uint16_t index = aIndex;
|
||||
Listener saveElem;
|
||||
|
||||
OT_ASSERT(aIndex < mNumValidListeners);
|
||||
|
||||
saveElem = mListeners[aIndex];
|
||||
|
||||
for (;;)
|
||||
{
|
||||
uint16_t child = 2 * index + 1;
|
||||
|
||||
if (child >= mNumValidListeners || child <= index) // child <= index after int overflow
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if (child + 1 < mNumValidListeners && mListeners[child + 1] < mListeners[child])
|
||||
{
|
||||
child++;
|
||||
}
|
||||
|
||||
if (!(mListeners[child] < saveElem))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
mListeners[index] = mListeners[child];
|
||||
|
||||
index = child;
|
||||
}
|
||||
|
||||
if (index > aIndex)
|
||||
{
|
||||
mListeners[index] = saveElem;
|
||||
}
|
||||
|
||||
return index > aIndex;
|
||||
}
|
||||
|
||||
void MulticastListenersTable::SiftHeapElemUp(uint16_t aIndex)
|
||||
{
|
||||
uint16_t index = aIndex;
|
||||
Listener saveElem;
|
||||
|
||||
OT_ASSERT(aIndex < mNumValidListeners);
|
||||
|
||||
saveElem = mListeners[aIndex];
|
||||
|
||||
for (;;)
|
||||
{
|
||||
uint16_t parent = (index - 1) / 2;
|
||||
|
||||
if (index == 0 || !(saveElem < mListeners[parent]))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
mListeners[index] = mListeners[parent];
|
||||
|
||||
index = parent;
|
||||
}
|
||||
|
||||
if (index < aIndex)
|
||||
{
|
||||
mListeners[index] = saveElem;
|
||||
}
|
||||
}
|
||||
|
||||
MulticastListenersTable::Listener *MulticastListenersTable::IteratorBuilder::begin(void)
|
||||
{
|
||||
return &Get<MulticastListenersTable>().mListeners[0];
|
||||
}
|
||||
|
||||
MulticastListenersTable::Listener *MulticastListenersTable::IteratorBuilder::end(void)
|
||||
{
|
||||
return &Get<MulticastListenersTable>().mListeners[Get<MulticastListenersTable>().mNumValidListeners];
|
||||
}
|
||||
|
||||
void MulticastListenersTable::Clear(void)
|
||||
{
|
||||
if (mCallback.IsSet())
|
||||
while (!mListeners.IsEmpty())
|
||||
{
|
||||
for (uint16_t i = 0; i < mNumValidListeners; i++)
|
||||
{
|
||||
mCallback.Invoke(MapEnum(Listener::kEventRemoved), &mListeners[i].GetAddress());
|
||||
}
|
||||
Ip6::Address address = mListeners.Back()->mAddress;
|
||||
|
||||
mListeners.PopBack();
|
||||
InvokeCallback(kEventRemoved, address);
|
||||
}
|
||||
|
||||
mNumValidListeners = 0;
|
||||
|
||||
CheckInvariants();
|
||||
mTimer.Stop();
|
||||
}
|
||||
|
||||
void MulticastListenersTable::SetCallback(Listener::Callback aCallback, void *aContext)
|
||||
void MulticastListenersTable::SetCallback(ListenerCallback aCallback, void *aContext)
|
||||
{
|
||||
mCallback.Set(aCallback, aContext);
|
||||
|
||||
if (mCallback.IsSet())
|
||||
{
|
||||
for (uint16_t i = 0; i < mNumValidListeners; i++)
|
||||
for (const Listener &listener : mListeners)
|
||||
{
|
||||
mCallback.Invoke(MapEnum(Listener::kEventAdded), &mListeners[i].GetAddress());
|
||||
InvokeCallback(kEventAdded, listener.mAddress);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Error MulticastListenersTable::GetNext(Listener::Iterator &aIterator, Listener::Info &aInfo)
|
||||
Error MulticastListenersTable::GetNext(ListenerIterator &aIterator, ListenerInfo &aInfo)
|
||||
{
|
||||
Error error = kErrorNone;
|
||||
TimeMilli now;
|
||||
|
||||
VerifyOrExit(aIterator < mNumValidListeners, error = kErrorNotFound);
|
||||
VerifyOrExit(aIterator < mListeners.GetLength(), error = kErrorNotFound);
|
||||
|
||||
now = TimerMilli::GetNow();
|
||||
|
||||
aInfo.mAddress = mListeners[aIterator].mAddress;
|
||||
aInfo.mTimeout =
|
||||
Time::MsecToSec(mListeners[aIterator].mExpireTime > now ? mListeners[aIterator].mExpireTime - now : 0);
|
||||
aInfo.mTimeout = Time::MsecToSec(mListeners[aIterator].mExpireTime.DetermineRemainingDurationFrom(now));
|
||||
|
||||
aIterator++;
|
||||
|
||||
|
||||
@@ -40,11 +40,13 @@
|
||||
|
||||
#include <openthread/backbone_router_ftd.h>
|
||||
|
||||
#include "common/array.hpp"
|
||||
#include "common/as_core_type.hpp"
|
||||
#include "common/callback.hpp"
|
||||
#include "common/non_copyable.hpp"
|
||||
#include "common/notifier.hpp"
|
||||
#include "common/time.hpp"
|
||||
#include "common/timer.hpp"
|
||||
#include "net/ip6_address.hpp"
|
||||
|
||||
namespace ot {
|
||||
@@ -56,66 +58,17 @@ namespace BackboneRouter {
|
||||
*/
|
||||
class MulticastListenersTable : public InstanceLocator, private NonCopyable
|
||||
{
|
||||
class IteratorBuilder;
|
||||
|
||||
public:
|
||||
/**
|
||||
* Represents a Multicast Listener entry.
|
||||
*/
|
||||
class Listener : public Clearable<Listener>
|
||||
{
|
||||
friend class MulticastListenersTable;
|
||||
|
||||
public:
|
||||
typedef otBackboneRouterMulticastListenerCallback Callback; ///< Listener callback.
|
||||
typedef otBackboneRouterMulticastListenerIterator Iterator; ///< Iterator to go over Listener entries.
|
||||
typedef otBackboneRouterMulticastListenerInfo Info; ///< Listener info.
|
||||
|
||||
enum Event : uint8_t ///< Listener Event
|
||||
{
|
||||
kEventAdded = OT_BACKBONE_ROUTER_MULTICAST_LISTENER_ADDED, ///< Listener was added.
|
||||
kEventRemoved = OT_BACKBONE_ROUTER_MULTICAST_LISTENER_REMOVED, ///< Listener was removed.
|
||||
};
|
||||
|
||||
/**
|
||||
* Initializes the `Listener` object.
|
||||
*/
|
||||
Listener(void) { Clear(); }
|
||||
|
||||
/**
|
||||
* Returns the Multicast Listener address.
|
||||
*
|
||||
* @returns The Multicast Listener address.
|
||||
*/
|
||||
const Ip6::Address &GetAddress(void) const { return mAddress; }
|
||||
|
||||
/**
|
||||
* Returns the expire time of the Multicast Listener.
|
||||
*
|
||||
* @returns The Multicast Listener expire time.
|
||||
*/
|
||||
const TimeMilli GetExpireTime(void) const { return mExpireTime; }
|
||||
|
||||
private:
|
||||
void SetAddress(const Ip6::Address &aAddress) { mAddress = aAddress; }
|
||||
void SetExpireTime(TimeMilli aExpireTime) { mExpireTime = aExpireTime; }
|
||||
|
||||
bool operator<(const Listener &aOther) const { return GetExpireTime() < aOther.GetExpireTime(); }
|
||||
|
||||
Ip6::Address mAddress;
|
||||
TimeMilli mExpireTime;
|
||||
};
|
||||
typedef otBackboneRouterMulticastListenerCallback ListenerCallback; ///< Listener Callback.
|
||||
typedef otBackboneRouterMulticastListenerInfo ListenerInfo; ///< Listener info.
|
||||
typedef otBackboneRouterMulticastListenerIterator ListenerIterator; ///< Iterator to go over entries.
|
||||
|
||||
/**
|
||||
* Initializes the Multicast Listeners Table.
|
||||
*
|
||||
* @param[in] aInstance A reference to the OpenThread instance.
|
||||
*/
|
||||
explicit MulticastListenersTable(Instance &aInstance)
|
||||
: InstanceLocator(aInstance)
|
||||
, mNumValidListeners(0)
|
||||
{
|
||||
}
|
||||
explicit MulticastListenersTable(Instance &aInstance);
|
||||
|
||||
/**
|
||||
* Adds a Multicast Listener with given address and expire time.
|
||||
@@ -136,17 +89,12 @@ public:
|
||||
*/
|
||||
void Remove(const Ip6::Address &aAddress);
|
||||
|
||||
/**
|
||||
* Removes expired Multicast Listeners.
|
||||
*/
|
||||
void Expire(void);
|
||||
|
||||
/**
|
||||
* Counts the number of valid Multicast Listeners.
|
||||
*
|
||||
* @returns The number of valid Multicast Listeners.
|
||||
*/
|
||||
uint16_t Count(void) const { return mNumValidListeners; }
|
||||
uint16_t Count(void) const { return mListeners.GetLength(); }
|
||||
|
||||
/**
|
||||
* Indicates whether or not the Multicast Listeners Table contains the given address.
|
||||
@@ -158,17 +106,6 @@ public:
|
||||
*/
|
||||
bool Has(const Ip6::Address &aAddress) const;
|
||||
|
||||
/**
|
||||
* Enables range-based `for` loop iteration over all Multicast Listeners.
|
||||
*
|
||||
* Should be used as follows:
|
||||
*
|
||||
* for (MulticastListenersTable::Listener &listener : Get<MulticastListenersTable>().Iterate())
|
||||
*
|
||||
* @returns An IteratorBuilder instance.
|
||||
*/
|
||||
IteratorBuilder Iterate(void) { return IteratorBuilder(GetInstance()); }
|
||||
|
||||
/**
|
||||
* Removes all the Multicast Listeners.
|
||||
*/
|
||||
@@ -180,7 +117,7 @@ public:
|
||||
* @param[in] aCallback The callback function.
|
||||
* @param[in] aContext A user context pointer.
|
||||
*/
|
||||
void SetCallback(Listener::Callback aCallback, void *aContext);
|
||||
void SetCallback(ListenerCallback aCallback, void *aContext);
|
||||
|
||||
/**
|
||||
* Gets the next Multicast Listener.
|
||||
@@ -191,24 +128,17 @@ public:
|
||||
* @retval kErrorNone Successfully found the next Multicast Listener info.
|
||||
* @retval kErrorNotFound No subsequent Multicast Listener was found.
|
||||
*/
|
||||
Error GetNext(Listener::Iterator &aIterator, Listener::Info &aInfo);
|
||||
Error GetNext(ListenerIterator &aIterator, ListenerInfo &aInfo);
|
||||
|
||||
private:
|
||||
static constexpr uint16_t kTableSize = OPENTHREAD_CONFIG_MAX_MULTICAST_LISTENERS;
|
||||
|
||||
static_assert(kTableSize >= 75, "Thread 1.2 Conformance requires table size of at least 75 listeners.");
|
||||
|
||||
class IteratorBuilder : InstanceLocator
|
||||
{
|
||||
public:
|
||||
explicit IteratorBuilder(Instance &aInstance)
|
||||
: InstanceLocator(aInstance)
|
||||
{
|
||||
}
|
||||
typedef otBackboneRouterMulticastListenerEvent Event;
|
||||
|
||||
Listener *begin(void);
|
||||
Listener *end(void);
|
||||
};
|
||||
static constexpr Event kEventAdded = OT_BACKBONE_ROUTER_MULTICAST_LISTENER_ADDED;
|
||||
static constexpr Event kEventRemoved = OT_BACKBONE_ROUTER_MULTICAST_LISTENER_REMOVED;
|
||||
|
||||
enum Action : uint8_t
|
||||
{
|
||||
@@ -217,22 +147,28 @@ private:
|
||||
kExpire,
|
||||
};
|
||||
|
||||
struct Listener
|
||||
{
|
||||
bool Matches(const Ip6::Address &aAddress) const { return mAddress == aAddress; }
|
||||
|
||||
Ip6::Address mAddress;
|
||||
TimeMilli mExpireTime;
|
||||
};
|
||||
|
||||
void InvokeCallback(Event aEvent, const Ip6::Address &aAddress) const;
|
||||
void HandleTimer(void);
|
||||
void Log(Action aAction, const Ip6::Address &aAddress, TimeMilli aExpireTime, Error aError) const;
|
||||
|
||||
void FixHeap(uint16_t aIndex);
|
||||
bool SiftHeapElemDown(uint16_t aIndex);
|
||||
void SiftHeapElemUp(uint16_t aIndex);
|
||||
void CheckInvariants(void) const;
|
||||
using ListenerArray = Array<Listener, kTableSize, uint16_t>;
|
||||
using ExpireTimer = TimerMilliIn<MulticastListenersTable, &MulticastListenersTable::HandleTimer>;
|
||||
|
||||
Listener mListeners[kTableSize];
|
||||
uint16_t mNumValidListeners;
|
||||
Callback<Listener::Callback> mCallback;
|
||||
ListenerArray mListeners;
|
||||
ExpireTimer mTimer;
|
||||
Callback<ListenerCallback> mCallback;
|
||||
};
|
||||
|
||||
} // namespace BackboneRouter
|
||||
|
||||
DefineMapEnum(otBackboneRouterMulticastListenerEvent, BackboneRouter::MulticastListenersTable::Listener::Event);
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
||||
@@ -42,150 +42,86 @@
|
||||
|
||||
namespace ot {
|
||||
|
||||
static Instance *sInstance;
|
||||
|
||||
using namespace ot::BackboneRouter;
|
||||
|
||||
static const otIp6Address MA201 = {
|
||||
{{0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01}}};
|
||||
static const otIp6Address MA301 = {
|
||||
{{0xff, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01}}};
|
||||
static const otIp6Address MA401 = {
|
||||
{{0xff, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01}}};
|
||||
static const otIp6Address MA501 = {
|
||||
{{0xff, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01}}};
|
||||
|
||||
uint32_t sNow;
|
||||
|
||||
extern "C" uint32_t otPlatAlarmMilliGetNow(void) { return sNow; }
|
||||
|
||||
void testMulticastListenersTableAPIs(Instance *aInstance);
|
||||
|
||||
void TestMulticastListenersTable(void)
|
||||
{
|
||||
sInstance = testInitInstance();
|
||||
VerifyOrQuit(sInstance != nullptr);
|
||||
static constexpr uint16_t kMaxSize = OPENTHREAD_CONFIG_MAX_MULTICAST_LISTENERS;
|
||||
|
||||
MulticastListenersTable &table = sInstance->Get<MulticastListenersTable>();
|
||||
MulticastListenersTable *table;
|
||||
Instance *instance;
|
||||
Ip6::Address kMa201;
|
||||
Ip6::Address kMa301;
|
||||
Ip6::Address kMa401;
|
||||
Ip6::Address kMa501;
|
||||
TimeMilli now(0);
|
||||
|
||||
for (MulticastListenersTable::Listener &listener : table.Iterate())
|
||||
{
|
||||
VerifyOrQuit(false, "MulticastListenersTable should be empty when created");
|
||||
}
|
||||
SuccessOrQuit(kMa201.FromString("ff02::01"));
|
||||
SuccessOrQuit(kMa301.FromString("ff03::01"));
|
||||
SuccessOrQuit(kMa401.FromString("ff04::01"));
|
||||
SuccessOrQuit(kMa501.FromString("ff05::01"));
|
||||
|
||||
// Removing from empty table should be OK
|
||||
table.Remove(static_cast<const Ip6::Address &>(MA401));
|
||||
instance = testInitInstance();
|
||||
VerifyOrQuit(instance != nullptr);
|
||||
|
||||
sNow = 1;
|
||||
// Add valid MAs should succeed
|
||||
SuccessOrQuit(table.Add(static_cast<const Ip6::Address &>(MA401), TimerMilli::GetNow()));
|
||||
SuccessOrQuit(table.Add(static_cast<const Ip6::Address &>(MA501), TimerMilli::GetNow()));
|
||||
VerifyOrQuit(table.Count() == 2, "Table count is wrong");
|
||||
table = &instance->Get<MulticastListenersTable>();
|
||||
|
||||
VerifyOrQuit(table->Count() == 0);
|
||||
|
||||
table->Remove(kMa201);
|
||||
|
||||
VerifyOrQuit(table->Count() == 0);
|
||||
|
||||
SuccessOrQuit(table->Add(kMa401, now));
|
||||
SuccessOrQuit(table->Add(kMa501, now));
|
||||
VerifyOrQuit(table->Count() == 2);
|
||||
|
||||
// Add invalid MAs should fail with kErrorInvalidArgs
|
||||
VerifyOrQuit(table.Add(static_cast<const Ip6::Address &>(MA201), TimerMilli::GetNow()) == kErrorInvalidArgs,
|
||||
"failed to detect bad arg");
|
||||
VerifyOrQuit(table.Add(static_cast<const Ip6::Address &>(MA301), TimerMilli::GetNow()) == kErrorInvalidArgs,
|
||||
"failed to detect bad arg");
|
||||
VerifyOrQuit(table->Add(kMa201, now) == kErrorInvalidArgs);
|
||||
VerifyOrQuit(table->Add(kMa301, now) == kErrorInvalidArgs);
|
||||
VerifyOrQuit(table->Count() == 2);
|
||||
|
||||
// Expire should expire outdated Listeners
|
||||
sNow = 2;
|
||||
table.Expire();
|
||||
VerifyOrQuit(table.Count() == 0, "Table count is wrong");
|
||||
// Re-add an existing entry
|
||||
SuccessOrQuit(table->Add(kMa501, now));
|
||||
VerifyOrQuit(table->Count() == 2);
|
||||
|
||||
// Add different addresses until the table is full
|
||||
sNow = 10;
|
||||
for (uint16_t i = 0; i < OPENTHREAD_CONFIG_MAX_MULTICAST_LISTENERS; i++)
|
||||
VerifyOrQuit(table->Has(kMa401));
|
||||
VerifyOrQuit(table->Has(kMa501));
|
||||
VerifyOrQuit(!table->Has(kMa201));
|
||||
VerifyOrQuit(!table->Has(kMa301));
|
||||
|
||||
table->Clear();
|
||||
VerifyOrQuit(table->Count() == 0);
|
||||
|
||||
for (uint16_t i = 0; i < kMaxSize; i++)
|
||||
{
|
||||
Ip6::Address address;
|
||||
|
||||
address = static_cast<const Ip6::Address &>(MA401);
|
||||
address = kMa401;
|
||||
address.mFields.m16[7] = BigEndian::HostSwap16(i);
|
||||
|
||||
SuccessOrQuit(table.Add(address, TimerMilli::GetNow() + i));
|
||||
VerifyOrQuit(table.Count() == i + 1, "Table count is wrong");
|
||||
SuccessOrQuit(table->Add(address, now));
|
||||
|
||||
VerifyOrQuit(table->Count() == i + 1);
|
||||
VerifyOrQuit(table->Has(address));
|
||||
}
|
||||
|
||||
// Now the table is full, we can't add more addresses
|
||||
VerifyOrQuit(table.Add(static_cast<const Ip6::Address &>(MA501), TimerMilli::GetNow()) == kErrorNoBufs,
|
||||
"succeeded when table is full");
|
||||
// Now the table is full, adding more entries should fail
|
||||
VerifyOrQuit(table->Add(kMa501, now) == kErrorNoBufs);
|
||||
|
||||
// Expire one Listener at a time
|
||||
for (uint16_t i = 0; i < OPENTHREAD_CONFIG_MAX_MULTICAST_LISTENERS; i++)
|
||||
{
|
||||
table.Expire();
|
||||
VerifyOrQuit(table.Count() == OPENTHREAD_CONFIG_MAX_MULTICAST_LISTENERS - i - 1, "Table count is wrong");
|
||||
VerifyOrQuit(table->Count() == kMaxSize);
|
||||
|
||||
sNow += 1;
|
||||
}
|
||||
|
||||
// Now the table should be empty
|
||||
VerifyOrQuit(table.Count() == 0, "Table count is wrong");
|
||||
|
||||
// Now test the APIs
|
||||
testMulticastListenersTableAPIs(sInstance);
|
||||
|
||||
// Do some fuzzy test
|
||||
for (uint16_t i = 0; i < 10000; i++)
|
||||
for (uint16_t i = 0; i < kMaxSize; i++)
|
||||
{
|
||||
Ip6::Address address;
|
||||
sNow += 10;
|
||||
|
||||
table.Expire();
|
||||
address = kMa401;
|
||||
address.mFields.m16[7] = BigEndian::HostSwap16(i);
|
||||
|
||||
for (MulticastListenersTable::Listener &listener : table.Iterate())
|
||||
{
|
||||
OT_ASSERT(listener.GetAddress().IsMulticastLargerThanRealmLocal());
|
||||
OT_ASSERT(listener.GetExpireTime() > TimerMilli::GetNow());
|
||||
}
|
||||
|
||||
address = static_cast<const Ip6::Address &>(MA401);
|
||||
|
||||
address.mFields.m16[7] = Random::NonCrypto::GetUint16InRange(1, 1000);
|
||||
IgnoreError(table.Add(address, TimerMilli::GetNow() + Random::NonCrypto::GetUint32InRange(1, 100)));
|
||||
|
||||
address.mFields.m16[7] = Random::NonCrypto::GetUint16InRange(1, 1000);
|
||||
if (Random::NonCrypto::GetUint16InRange(0, 2) == 0)
|
||||
{
|
||||
table.Remove(address);
|
||||
}
|
||||
VerifyOrQuit(table->Has(address));
|
||||
}
|
||||
}
|
||||
|
||||
void testMulticastListenersTableAPIs(Instance *aInstance)
|
||||
{
|
||||
#if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE
|
||||
otBackboneRouterMulticastListenerIterator iter = OT_BACKBONE_ROUTER_MULTICAST_LISTENER_ITERATOR_INIT;
|
||||
otBackboneRouterMulticastListenerInfo info;
|
||||
size_t table_size = 0;
|
||||
|
||||
while (otBackboneRouterMulticastListenerGetNext(aInstance, &iter, &info) == kErrorNone)
|
||||
{
|
||||
VerifyOrQuit(false, "Table should be empty");
|
||||
}
|
||||
|
||||
SuccessOrQuit(otBackboneRouterMulticastListenerAdd(aInstance, &MA401, 30));
|
||||
|
||||
table_size = 0, iter = OT_BACKBONE_ROUTER_MULTICAST_LISTENER_ITERATOR_INIT;
|
||||
while (otBackboneRouterMulticastListenerGetNext(aInstance, &iter, &info) == kErrorNone)
|
||||
{
|
||||
table_size++;
|
||||
|
||||
VerifyOrQuit(memcmp(&info.mAddress, &MA401, sizeof(otIp6Address)) == 0, "bad address");
|
||||
VerifyOrQuit(info.mTimeout == 30, "bad timeout");
|
||||
}
|
||||
VerifyOrQuit(table_size == 1, "Table size is wrong");
|
||||
|
||||
otBackboneRouterMulticastListenerClear(aInstance);
|
||||
|
||||
iter = OT_BACKBONE_ROUTER_MULTICAST_LISTENER_ITERATOR_INIT;
|
||||
while (otBackboneRouterMulticastListenerGetNext(aInstance, &iter, &info) == kErrorNone)
|
||||
{
|
||||
VerifyOrQuit(false, "Table should be empty");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace ot
|
||||
|
||||
int main(void)
|
||||
|
||||
Reference in New Issue
Block a user