[message] change queues to use non-circular linked list (#11630)

This commit updates the `MessageQueue` and `PriorityQueue`
implementations to use non-circular doubly linked lists instead of
the previous circular ones. Using a non-circular list requires the
queue to track the head element, but it simplifies common operations
like getting the head and iterating over the messages.

Particularly, `Message::GetNext()` now simply returns the `mNext`
pointer. Previously, `Message` had to store a pointer to its `mQueue`
within its `Metadata` to identify the tail of the queue and stop the
iteration correctly.

This commit also updates the unit tests. In particular,
`test_priority_queue` is significantly enhanced to cover many
scenarios, such as multiple messages with the same priority, and
messages with different priorities being added and removed in various
orders.
This commit is contained in:
Abtin Keshavarzian
2025-06-25 13:03:43 -07:00
committed by GitHub
parent 5d1d380012
commit c55098af5e
7 changed files with 694 additions and 268 deletions
+1 -1
View File
@@ -52,7 +52,7 @@ extern "C" {
* *
* @note This number versions both OpenThread platform and user APIs. * @note This number versions both OpenThread platform and user APIs.
*/ */
#define OPENTHREAD_API_VERSION (514) #define OPENTHREAD_API_VERSION (515)
/** /**
* @addtogroup api-instance * @addtogroup api-instance
+2 -1
View File
@@ -400,7 +400,8 @@ int otMessageWrite(otMessage *aMessage, uint16_t aOffset, const void *aBuf, uint
*/ */
typedef struct typedef struct
{ {
void *mData; ///< Opaque data used by the implementation. void *mData; ///< Opaque data used by the implementation.
void *mData2; ///< Opaque data used by the implementation.
} otMessageQueue; } otMessageQueue;
/** /**
+3 -8
View File
@@ -122,12 +122,7 @@ int otMessageWrite(otMessage *aMessage, uint16_t aOffset, const void *aBuf, uint
return aLength; return aLength;
} }
void otMessageQueueInit(otMessageQueue *aQueue) void otMessageQueueInit(otMessageQueue *aQueue) { AsCoreType(aQueue).Clear(); }
{
AssertPointerIsNotNull(aQueue);
aQueue->mData = nullptr;
}
void otMessageQueueEnqueue(otMessageQueue *aQueue, otMessage *aMessage) void otMessageQueueEnqueue(otMessageQueue *aQueue, otMessage *aMessage)
{ {
@@ -148,11 +143,11 @@ otMessage *otMessageQueueGetHead(otMessageQueue *aQueue) { return AsCoreType(aQu
otMessage *otMessageQueueGetNext(otMessageQueue *aQueue, const otMessage *aMessage) otMessage *otMessageQueueGetNext(otMessageQueue *aQueue, const otMessage *aMessage)
{ {
OT_UNUSED_VARIABLE(aQueue);
Message *next; Message *next;
VerifyOrExit(aMessage != nullptr, next = nullptr); VerifyOrExit(aMessage != nullptr, next = nullptr);
VerifyOrExit(AsCoreType(aMessage).GetMessageQueue() == aQueue, next = nullptr);
next = AsCoreType(aMessage).GetNext(); next = AsCoreType(aMessage).GetNext();
exit: exit:
+135 -116
View File
@@ -75,6 +75,7 @@ Message *MessagePool::Allocate(Message::Type aType, uint16_t aReserveHeader, con
message->SetLinkSecurityEnabled(aSettings.IsLinkSecurityEnabled()); message->SetLinkSecurityEnabled(aSettings.IsLinkSecurityEnabled());
message->SetLoopbackToHostAllowed(OPENTHREAD_CONFIG_IP6_ALLOW_LOOP_BACK_HOST_DATAGRAMS); message->SetLoopbackToHostAllowed(OPENTHREAD_CONFIG_IP6_ALLOW_LOOP_BACK_HOST_DATAGRAMS);
message->SetOrigin(Message::kOriginHostTrusted); message->SetOrigin(Message::kOriginHostTrusted);
message->MarkAsNotInAQueue();
SuccessOrExit(error = message->SetPriority(aSettings.GetPriority())); SuccessOrExit(error = message->SetPriority(aSettings.GetPriority()));
SuccessOrExit(error = message->SetLength(0)); SuccessOrExit(error = message->SetLength(0));
@@ -98,7 +99,7 @@ Message *MessagePool::Allocate(Message::Type aType, uint16_t aReserveHeader)
void MessagePool::Free(Message *aMessage) void MessagePool::Free(Message *aMessage)
{ {
OT_ASSERT(aMessage->Next() == nullptr && aMessage->Prev() == nullptr); OT_ASSERT(!aMessage->IsInAQueue());
FreeBuffers(static_cast<Buffer *>(aMessage)); FreeBuffers(static_cast<Buffer *>(aMessage));
} }
@@ -260,30 +261,6 @@ void Message::Free(void)
Get<MessagePool>().Free(this); Get<MessagePool>().Free(this);
} }
Message *Message::GetNext(void) const
{
Message *next;
Message *tail;
if (GetMetadata().mInPriorityQ)
{
PriorityQueue *priorityQueue = GetPriorityQueue();
VerifyOrExit(priorityQueue != nullptr, next = nullptr);
tail = priorityQueue->GetTail();
}
else
{
MessageQueue *messageQueue = GetMessageQueue();
VerifyOrExit(messageQueue != nullptr, next = nullptr);
tail = messageQueue->GetTail();
}
next = (this == tail) ? nullptr : Next();
exit:
return next;
}
Error Message::SetLength(uint16_t aLength) Error Message::SetLength(uint16_t aLength)
{ {
Error error = kErrorNone; Error error = kErrorNone;
@@ -871,16 +848,18 @@ bool Message::IsTimeSync(void) const
#endif #endif
} }
void Message::SetMessageQueue(MessageQueue *aMessageQueue) void Message::MarkAsNotInAQueue(void)
{ {
GetMetadata().mQueue = aMessageQueue; // To indicate that a message is no longer in a queue, we set
GetMetadata().mInPriorityQ = false; // its 'mPrev' pointer to point back to itself. This state is
} // unique and won't occur if the message is part of a queue. We
// also set 'mNext' to 'nullptr' so that 'GetNext()' correctly
// returns 'nullptr' for a dequeued message.
void Message::SetPriorityQueue(PriorityQueue *aPriorityQueue) Next() = nullptr;
{ Prev() = this;
GetMetadata().mQueue = aPriorityQueue;
GetMetadata().mInPriorityQ = aPriorityQueue != nullptr; GetMetadata().mInPriorityQ = false;
} }
//--------------------------------------------------------------------------------------------------------------------- //---------------------------------------------------------------------------------------------------------------------
@@ -889,56 +868,61 @@ void Message::SetPriorityQueue(PriorityQueue *aPriorityQueue)
void MessageQueue::Enqueue(Message &aMessage, QueuePosition aPosition) void MessageQueue::Enqueue(Message &aMessage, QueuePosition aPosition)
{ {
OT_ASSERT(!aMessage.IsInAQueue()); OT_ASSERT(!aMessage.IsInAQueue());
OT_ASSERT((aMessage.Next() == nullptr) && (aMessage.Prev() == nullptr));
aMessage.SetMessageQueue(this); aMessage.GetMetadata().mInPriorityQ = false;
if (GetTail() == nullptr) if (GetHead() == nullptr)
{ {
aMessage.Next() = &aMessage; aMessage.Next() = nullptr;
aMessage.Prev() = &aMessage; aMessage.Prev() = nullptr;
SetHead(&aMessage);
SetTail(&aMessage); SetTail(&aMessage);
} }
else else
{ {
Message *head = GetTail()->Next(); switch (aPosition)
aMessage.Next() = head;
aMessage.Prev() = GetTail();
head->Prev() = &aMessage;
GetTail()->Next() = &aMessage;
if (aPosition == kQueuePositionTail)
{ {
case kQueuePositionHead:
aMessage.Next() = GetHead();
aMessage.Prev() = nullptr;
GetHead()->Prev() = &aMessage;
SetHead(&aMessage);
break;
case kQueuePositionTail:
aMessage.Next() = nullptr;
aMessage.Prev() = GetTail();
GetTail()->Next() = &aMessage;
SetTail(&aMessage); SetTail(&aMessage);
break;
} }
} }
} }
void MessageQueue::Dequeue(Message &aMessage) void MessageQueue::Dequeue(Message &aMessage)
{ {
OT_ASSERT(aMessage.GetMessageQueue() == this); if (&aMessage == GetHead())
OT_ASSERT((aMessage.Next() != nullptr) && (aMessage.Prev() != nullptr)); {
SetHead(aMessage.Next());
}
if (&aMessage == GetTail()) if (&aMessage == GetTail())
{ {
SetTail(GetTail()->Prev()); SetTail(aMessage.Prev());
if (&aMessage == GetTail())
{
SetTail(nullptr);
}
} }
aMessage.Prev()->Next() = aMessage.Next(); if (aMessage.Prev() != nullptr)
aMessage.Next()->Prev() = aMessage.Prev(); {
aMessage.Prev()->Next() = aMessage.Next();
}
aMessage.Prev() = nullptr; if (aMessage.Next() != nullptr)
aMessage.Next() = nullptr; {
aMessage.Next()->Prev() = aMessage.Prev();
}
aMessage.SetMessageQueue(nullptr); aMessage.MarkAsNotInAQueue();
} }
void MessageQueue::DequeueAndFree(Message &aMessage) void MessageQueue::DequeueAndFree(Message &aMessage)
@@ -983,86 +967,109 @@ void MessageQueue::AddQueueInfos(Info &aInfo, const Info &aOther)
//--------------------------------------------------------------------------------------------------------------------- //---------------------------------------------------------------------------------------------------------------------
// PriorityQueue // PriorityQueue
const Message *PriorityQueue::FindFirstNonNullTail(Message::Priority aStartPriorityLevel) const const Message *PriorityQueue::FindTailForPriorityOrHigher(uint8_t aPriority) const
{ {
// Find the first non-`nullptr` tail starting from the given priority // This method finds the tail (last message entry in the list)
// level and moving forward (wrapping from priority value // that has priority level that is greater than or equal to
// `kNumPriorities` -1 back to 0). // `aPriority` and currently has messages in the queue.
//
// The `PriorityQueue` uses the `mTails` array to store pointers
// to the last message for each priority level. If a specific
// priority level has no messages, its corresponding `mTails`
// entry will be `nullptr`.
//
// The method iterates from `aPriority` upwards through higher
// priority levels, returning the first non-null tail pointer it
// encounters.
//
// The returned `Message` pointer indicates where a new message
// with `aPriority` should be inserted: it should be placed
// immediately after this returned message. If `nullptr` is
// returned, it means the new message will be the first of its
// priority level (or any higher priority) in the queue, and
// should be placed at the head.
const Message *tail = nullptr; const Message *tail = nullptr;
uint8_t priority;
priority = static_cast<uint8_t>(aStartPriorityLevel); for (uint8_t priority = aPriority; priority < Message::kNumPriorities; priority++)
do
{ {
if (mTails[priority] != nullptr) tail = mTails[priority];
if (tail != nullptr)
{ {
tail = mTails[priority];
break; break;
} }
}
priority = PrevPriority(priority);
} while (priority != aStartPriorityLevel);
return tail; return tail;
} }
const Message *PriorityQueue::GetHead(void) const
{
return Message::NextOf(FindFirstNonNullTail(Message::kPriorityLow));
}
const Message *PriorityQueue::GetHeadForPriority(Message::Priority aPriority) const const Message *PriorityQueue::GetHeadForPriority(Message::Priority aPriority) const
{ {
const Message *head; const Message *head = nullptr;
const Message *previousTail; uint8_t priority = static_cast<uint8_t>(aPriority);
if (mTails[aPriority] != nullptr) if (mTails[priority] == nullptr)
{ {
previousTail = FindFirstNonNullTail(static_cast<Message::Priority>(PrevPriority(aPriority))); head = nullptr;
}
OT_ASSERT(previousTail != nullptr); else if (mHead->GetPriority() == aPriority)
{
head = previousTail->Next(); head = mHead;
} }
else else
{ {
head = nullptr; const Message *previousTail = FindTailForPriorityOrHigher(priority + 1);
OT_ASSERT(previousTail != nullptr);
head = previousTail->Next();
} }
return head; return head;
} }
const Message *PriorityQueue::GetTail(void) const { return FindFirstNonNullTail(Message::kPriorityLow); } const Message *PriorityQueue::GetTail(void) const { return FindTailForPriorityOrHigher(Message::kPriorityLow); }
void PriorityQueue::Enqueue(Message &aMessage) void PriorityQueue::Enqueue(Message &aMessage)
{ {
Message::Priority priority; uint8_t priority;
Message *tail;
Message *next;
OT_ASSERT(!aMessage.IsInAQueue()); OT_ASSERT(!aMessage.IsInAQueue());
aMessage.SetPriorityQueue(this); aMessage.GetMetadata().mInPriorityQ = true;
priority = aMessage.GetPriority(); priority = aMessage.GetPriority();
tail = FindFirstNonNullTail(priority); // We insert the new `aMessage` immediately after the message
// returned by `FindTailForPriorityOrHigher()`.
//
// If `FindTailForPriorityOrHigher()` returns `nullptr`, it means
// `aMessage` has the highest priority of all existing messages
// or is the first message in the queue, so we add it at the head
// and update `mHead` accordingly.
//
// We first set the `Prev` and `Next` pointers within `aMessage`
// itself. Afterward, we update the `Next()` pointer of the
// preceding message (if any) and the `Prev()` pointer of the
// succeeding message (if any) to point back to the newly
// inserted `aMessage`, maintaining the linked list.
if (tail != nullptr) aMessage.Prev() = FindTailForPriorityOrHigher(priority);
if (aMessage.Prev() == nullptr)
{ {
next = tail->Next(); aMessage.Next() = mHead;
mHead = &aMessage;
aMessage.Next() = next;
aMessage.Prev() = tail;
next->Prev() = &aMessage;
tail->Next() = &aMessage;
} }
else else
{ {
aMessage.Next() = &aMessage; aMessage.Next() = aMessage.Prev()->Next();
aMessage.Prev() = &aMessage; aMessage.Prev()->Next() = &aMessage;
}
if (aMessage.Next() != nullptr)
{
aMessage.Next()->Prev() = &aMessage;
} }
mTails[priority] = &aMessage; mTails[priority] = &aMessage;
@@ -1071,32 +1078,44 @@ void PriorityQueue::Enqueue(Message &aMessage)
void PriorityQueue::Dequeue(Message &aMessage) void PriorityQueue::Dequeue(Message &aMessage)
{ {
Message::Priority priority; Message::Priority priority;
Message *tail;
OT_ASSERT(aMessage.GetPriorityQueue() == this); OT_ASSERT(aMessage.IsInAPriorityQueue());
priority = aMessage.GetPriority(); priority = aMessage.GetPriority();
tail = mTails[priority]; // If `aMessage` is the current tail for its priority, update
// `mTails[priority]`. The new tail becomes the preceding
// message entry. If the preceding message has a different
// (higher) priority, or if there's no preceding message, it
// means `aMessage` was the last of its priority, and the
// `mTails[priority]` is set to `nullptr`.
if (&aMessage == tail) if (&aMessage == mTails[priority])
{ {
tail = tail->Prev(); mTails[priority] = aMessage.Prev();
if ((&aMessage == tail) || (tail->GetPriority() != priority)) if ((mTails[priority] != nullptr) && (mTails[priority]->GetPriority() != priority))
{ {
tail = nullptr; mTails[priority] = nullptr;
} }
mTails[priority] = tail;
} }
aMessage.Next()->Prev() = aMessage.Prev(); if (&aMessage == mHead)
aMessage.Prev()->Next() = aMessage.Next(); {
aMessage.Next() = nullptr; mHead = aMessage.Next();
aMessage.Prev() = nullptr; }
aMessage.SetPriorityQueue(nullptr); if (aMessage.Next() != nullptr)
{
aMessage.Next()->Prev() = aMessage.Prev();
}
if (aMessage.Prev() != nullptr)
{
aMessage.Prev()->Next() = aMessage.Next();
}
aMessage.MarkAsNotInAQueue();
} }
void PriorityQueue::DequeueAndFree(Message &aMessage) void PriorityQueue::DequeueAndFree(Message &aMessage)
+19 -44
View File
@@ -235,7 +235,6 @@ protected:
TimeMilli mTimestamp; // The message timestamp. TimeMilli mTimestamp; // The message timestamp.
Message *mNext; // Next message in a doubly linked list. Message *mNext; // Next message in a doubly linked list.
Message *mPrev; // Previous message in a doubly linked list. Message *mPrev; // Previous message in a doubly linked list.
void *mQueue; // The queue where message is queued (if any). Queue type from `mInPriorityQ`.
TxCallback mTxCallback; // The callback to inform message TX success or failure. TxCallback mTxCallback; // The callback to inform message TX success or failure.
void *mTxContext; // The arbitrary context associated with `mTxCallback`. void *mTxContext; // The arbitrary context associated with `mTxCallback`.
RssAverager mRssAverager; // The averager maintaining the received signal strength (RSS) average. RssAverager mRssAverager; // The averager maintaining the received signal strength (RSS) average.
@@ -501,7 +500,7 @@ public:
* *
* @returns A pointer to the next message in the list or `nullptr` if at the end of the list. * @returns A pointer to the next message in the list or `nullptr` if at the end of the list.
*/ */
Message *GetNext(void) const; Message *GetNext(void) const { return Next(); }
/** /**
* Returns the number of bytes in the message. * Returns the number of bytes in the message.
@@ -1425,26 +1424,6 @@ public:
*/ */
void UpdateLinkInfoFrom(const ThreadLinkInfo &aLinkInfo); void UpdateLinkInfoFrom(const ThreadLinkInfo &aLinkInfo);
/**
* Returns a pointer to the message queue (if any) where this message is queued.
*
* @returns A pointer to the message queue or `nullptr` if not in any message queue.
*/
MessageQueue *GetMessageQueue(void) const
{
return !GetMetadata().mInPriorityQ ? static_cast<MessageQueue *>(GetMetadata().mQueue) : nullptr;
}
/**
* Returns a pointer to the priority message queue (if any) where this message is queued.
*
* @returns A pointer to the priority queue or `nullptr` if not in any priority queue.
*/
PriorityQueue *GetPriorityQueue(void) const
{
return GetMetadata().mInPriorityQ ? static_cast<PriorityQueue *>(GetMetadata().mQueue) : nullptr;
}
/** /**
* Indicates whether or not the message is also used for time sync purpose. * Indicates whether or not the message is also used for time sync purpose.
* *
@@ -1603,18 +1582,17 @@ private:
AsConst(this)->GetNextChunk(aLength, static_cast<Chunk &>(aChunk)); AsConst(this)->GetNextChunk(aLength, static_cast<Chunk &>(aChunk));
} }
bool IsInAQueue(void) const { return (GetMetadata().mQueue != nullptr); } void MarkAsNotInAQueue(void);
bool IsInAQueue(void) const { return (Prev() != this); }
bool IsInAPriorityQueue(void) const { return GetMetadata().mInPriorityQ; } bool IsInAPriorityQueue(void) const { return GetMetadata().mInPriorityQ; }
void SetMessageQueue(MessageQueue *aMessageQueue);
void SetPriorityQueue(PriorityQueue *aPriorityQueue);
void SetRssAverager(const RssAverager &aRssAverager) { GetMetadata().mRssAverager = aRssAverager; } void SetRssAverager(const RssAverager &aRssAverager) { GetMetadata().mRssAverager = aRssAverager; }
void SetLqiAverager(const LqiAverager &aLqiAverager) { GetMetadata().mLqiAverager = aLqiAverager; } void SetLqiAverager(const LqiAverager &aLqiAverager) { GetMetadata().mLqiAverager = aLqiAverager; }
Message *&Next(void) { return GetMetadata().mNext; } Message *&Next(void) { return GetMetadata().mNext; }
Message *const &Next(void) const { return GetMetadata().mNext; } Message *const &Next(void) const { return GetMetadata().mNext; }
Message *&Prev(void) { return GetMetadata().mPrev; } Message *&Prev(void) { return GetMetadata().mPrev; }
Message *const &Prev(void) const { return GetMetadata().mPrev; }
static Message *NextOf(Message *aMessage) { return (aMessage != nullptr) ? aMessage->Next() : nullptr; } static Message *NextOf(Message *aMessage) { return (aMessage != nullptr) ? aMessage->Next() : nullptr; }
static const Message *NextOf(const Message *aMessage) { return (aMessage != nullptr) ? aMessage->Next() : nullptr; } static const Message *NextOf(const Message *aMessage) { return (aMessage != nullptr) ? aMessage->Next() : nullptr; }
@@ -1625,7 +1603,7 @@ private:
/** /**
* Implements a message queue. * Implements a message queue.
*/ */
class MessageQueue : public otMessageQueue, private NonCopyable class MessageQueue : public otMessageQueue, public Clearable<MessageQueue>, private NonCopyable
{ {
friend class Message; friend class Message;
friend class PriorityQueue; friend class PriorityQueue;
@@ -1646,7 +1624,7 @@ public:
/** /**
* Initializes the message queue. * Initializes the message queue.
*/ */
MessageQueue(void) { SetTail(nullptr); } MessageQueue(void) { Clear(); }
#if OPENTHREAD_PLATFORM_NEXUS #if OPENTHREAD_PLATFORM_NEXUS
/** /**
@@ -1660,14 +1638,14 @@ public:
* *
* @returns A pointer to the first message. * @returns A pointer to the first message.
*/ */
Message *GetHead(void) { return Message::NextOf(GetTail()); } Message *GetHead(void) { return static_cast<Message *>(mData); }
/** /**
* Returns a pointer to the first message. * Returns a pointer to the first message.
* *
* @returns A pointer to the first message. * @returns A pointer to the first message.
*/ */
const Message *GetHead(void) const { return Message::NextOf(GetTail()); } const Message *GetHead(void) const { return static_cast<const Message *>(mData); }
/** /**
* Adds a message to the end of the list. * Adds a message to the end of the list.
@@ -1733,9 +1711,10 @@ public:
Message::ConstIterator end(void) const { return Message::ConstIterator(); } Message::ConstIterator end(void) const { return Message::ConstIterator(); }
private: private:
Message *GetTail(void) { return static_cast<Message *>(mData); } void SetHead(Message *aMessage) { mData = aMessage; }
const Message *GetTail(void) const { return static_cast<const Message *>(mData); } Message *GetTail(void) { return static_cast<Message *>(mData2); }
void SetTail(Message *aMessage) { mData = aMessage; } const Message *GetTail(void) const { return static_cast<const Message *>(mData2); }
void SetTail(Message *aMessage) { mData2 = aMessage; }
}; };
/** /**
@@ -1768,14 +1747,14 @@ public:
* *
* @returns A pointer to the first message. * @returns A pointer to the first message.
*/ */
Message *GetHead(void) { return AsNonConst(AsConst(this)->GetHead()); } Message *GetHead(void) { return mHead; }
/** /**
* Returns a pointer to the first message. * Returns a pointer to the first message.
* *
* @returns A pointer to the first message. * @returns A pointer to the first message.
*/ */
const Message *GetHead(void) const; const Message *GetHead(void) const { return mHead; }
/** /**
* Returns a pointer to the first message for a given priority level. * Returns a pointer to the first message for a given priority level.
@@ -1864,18 +1843,14 @@ public:
Message::ConstIterator end(void) const { return Message::ConstIterator(); } Message::ConstIterator end(void) const { return Message::ConstIterator(); }
private: private:
uint8_t PrevPriority(uint8_t aPriority) const const Message *FindTailForPriorityOrHigher(uint8_t aPriority) const;
Message *FindTailForPriorityOrHigher(uint8_t aPriority)
{ {
return (aPriority == Message::kNumPriorities - 1) ? 0 : (aPriority + 1); return AsNonConst(AsConst(this)->FindTailForPriorityOrHigher(aPriority));
}
const Message *FindFirstNonNullTail(Message::Priority aStartPriorityLevel) const;
Message *FindFirstNonNullTail(Message::Priority aStartPriorityLevel)
{
return AsNonConst(AsConst(this)->FindFirstNonNullTail(aStartPriorityLevel));
} }
Message *mHead;
Message *mTails[Message::kNumPriorities]; // Tail pointers associated with different priority levels. Message *mTails[Message::kNumPriorities]; // Tail pointers associated with different priority levels.
}; };
+40 -2
View File
@@ -133,6 +133,46 @@ void TestMessageQueue(void)
messageQueue.Dequeue(*messages[0]); messageQueue.Dequeue(*messages[0]);
VerifyMessageQueueContent(messageQueue, 0); VerifyMessageQueueContent(messageQueue, 0);
// Enqueue 2 messages and remove them in the same order added.
messageQueue.Enqueue(*messages[0]);
VerifyMessageQueueContent(messageQueue, 1, messages[0]);
messageQueue.Enqueue(*messages[1]);
VerifyMessageQueueContent(messageQueue, 2, messages[0], messages[1]);
messageQueue.Dequeue(*messages[0]);
VerifyMessageQueueContent(messageQueue, 1, messages[1]);
messageQueue.Dequeue(*messages[1]);
VerifyMessageQueueContent(messageQueue, 0);
// Enqueue 2 messages and remove them in reverse order added.
messageQueue.Enqueue(*messages[0]);
VerifyMessageQueueContent(messageQueue, 1, messages[0]);
messageQueue.Enqueue(*messages[1]);
VerifyMessageQueueContent(messageQueue, 2, messages[0], messages[1]);
messageQueue.Dequeue(*messages[1]);
VerifyMessageQueueContent(messageQueue, 1, messages[0]);
messageQueue.Dequeue(*messages[0]);
VerifyMessageQueueContent(messageQueue, 0);
// Enqueue 2 messages at the head and remove them in the same order added.
messageQueue.Enqueue(*messages[0], MessageQueue::kQueuePositionHead);
VerifyMessageQueueContent(messageQueue, 1, messages[0]);
messageQueue.Enqueue(*messages[1], MessageQueue::kQueuePositionHead);
VerifyMessageQueueContent(messageQueue, 2, messages[1], messages[0]);
messageQueue.Dequeue(*messages[0]);
VerifyMessageQueueContent(messageQueue, 1, messages[1]);
messageQueue.Dequeue(*messages[1]);
VerifyMessageQueueContent(messageQueue, 0);
// Enqueue 2 messages at the head and remove them in the reverse order added.
messageQueue.Enqueue(*messages[0], MessageQueue::kQueuePositionHead);
VerifyMessageQueueContent(messageQueue, 1, messages[0]);
messageQueue.Enqueue(*messages[1], MessageQueue::kQueuePositionHead);
VerifyMessageQueueContent(messageQueue, 2, messages[1], messages[0]);
messageQueue.Dequeue(*messages[1]);
VerifyMessageQueueContent(messageQueue, 1, messages[0]);
messageQueue.Dequeue(*messages[0]);
VerifyMessageQueueContent(messageQueue, 0);
// Enqueue 5 messages // Enqueue 5 messages
messageQueue.Enqueue(*messages[0]); messageQueue.Enqueue(*messages[0]);
VerifyMessageQueueContent(messageQueue, 1, messages[0]); VerifyMessageQueueContent(messageQueue, 1, messages[0]);
@@ -333,8 +373,6 @@ void TestMessageQueueOtApis(void)
message = otMessageQueueGetNext(&queue2, messages[0]); message = otMessageQueueGetNext(&queue2, messages[0]);
VerifyOrQuit(message == messages[1], "otMessageQueueGetNext() failed"); VerifyOrQuit(message == messages[1], "otMessageQueueGetNext() failed");
message = otMessageQueueGetNext(&queue, messages[0]);
VerifyOrQuit(message == nullptr, "otMessageQueueGetNext() did not return nullptr for message not in the queue.");
// Remove all element and make sure queue is empty // Remove all element and make sure queue is empty
otMessageQueueDequeue(&queue, messages[2]); otMessageQueueDequeue(&queue, messages[2]);
+494 -96
View File
@@ -37,10 +37,6 @@
namespace ot { namespace ot {
#define kNumNewPriorityTestMessages 2
#define kNumSetPriorityTestMessages 2
#define kNumTestMessages (kNumNewPriorityTestMessages + kNumSetPriorityTestMessages)
// This function verifies the content of the priority queue to match the passed in messages // This function verifies the content of the priority queue to match the passed in messages
void VerifyPriorityQueueContent(PriorityQueue &aPriorityQueue, int aExpectedLength, ...) void VerifyPriorityQueueContent(PriorityQueue &aPriorityQueue, int aExpectedLength, ...)
{ {
@@ -135,140 +131,541 @@ void VerifyPriorityQueueContent(PriorityQueue &aPriorityQueue, int aExpectedLeng
VerifyOrQuit(message == nullptr, "`for` loop iteration resulted in fewer entries than expected"); VerifyOrQuit(message == nullptr, "`for` loop iteration resulted in fewer entries than expected");
} }
// NOLINTBEGIN(modernize-loop-convert)
void TestPriorityQueue(void) void TestPriorityQueue(void)
{ {
static constexpr uint16_t kNumNewPriorityTestMessages = 2;
static constexpr uint16_t kNumSetPriorityTestMessages = 2;
static constexpr uint16_t kNumTestMessages = (kNumNewPriorityTestMessages + kNumSetPriorityTestMessages);
// Use two char abbreviated names for different priority levels;
static constexpr uint8_t kNw = Message::kPriorityNet; // Network level (highest priority)
static constexpr uint8_t kHi = Message::kPriorityHigh; // High priority
static constexpr uint8_t kMd = Message::kPriorityNormal; // Middle (or Normal) priority
static constexpr uint8_t kLo = Message::kPriorityLow; // Low priority.
static constexpr uint8_t kNumPriorities = Message::kNumPriorities;
Instance *instance; Instance *instance;
MessagePool *messagePool; MessagePool *messagePool;
PriorityQueue queue; PriorityQueue queue;
Message *msgNet[kNumTestMessages]; Message *msg[kNumPriorities][kNumTestMessages];
Message *msgHigh[kNumTestMessages];
Message *msgNor[kNumTestMessages];
Message *msgLow[kNumTestMessages];
instance = testInitInstance(); instance = testInitInstance();
VerifyOrQuit(instance != nullptr, "Null OpenThread instance"); VerifyOrQuit(instance != nullptr, "Null OpenThread instance");
messagePool = &instance->Get<MessagePool>(); messagePool = &instance->Get<MessagePool>();
// Use the function "Allocate()" to allocate messages with different priorities for (uint8_t prio = 0; prio < kNumPriorities; prio++)
for (int i = 0; i < kNumNewPriorityTestMessages; i++)
{ {
msgNet[i] = messagePool->Allocate(Message::kTypeIp6, 0, Message::Settings(Message::kPriorityNet)); Message::Priority priority = static_cast<Message::Priority>(prio);
VerifyOrQuit(msgNet[i] != nullptr);
msgHigh[i] = messagePool->Allocate(Message::kTypeIp6, 0, Message::Settings(Message::kPriorityHigh));
VerifyOrQuit(msgHigh[i] != nullptr);
msgNor[i] = messagePool->Allocate(Message::kTypeIp6, 0, Message::Settings(Message::kPriorityNormal));
VerifyOrQuit(msgNor[i] != nullptr);
msgLow[i] = messagePool->Allocate(Message::kTypeIp6, 0, Message::Settings(Message::kPriorityLow));
VerifyOrQuit(msgLow[i] != nullptr);
}
// Use the function "SetPriority()" to allocate messages with different priorities // Use the function "Allocate()" to allocate messages with different priorities
for (int i = kNumNewPriorityTestMessages; i < kNumTestMessages; i++) for (int i = 0; i < kNumNewPriorityTestMessages; i++)
{ {
msgNet[i] = messagePool->Allocate(Message::kTypeIp6); msg[prio][i] = messagePool->Allocate(Message::kTypeIp6, 0, Message::Settings(priority));
VerifyOrQuit(msgNet[i] != nullptr); VerifyOrQuit(msg[prio][i] != nullptr);
SuccessOrQuit(msgNet[i]->SetPriority(Message::kPriorityNet)); }
msgHigh[i] = messagePool->Allocate(Message::kTypeIp6);
VerifyOrQuit(msgHigh[i] != nullptr); // Use the function "SetPriority()" to allocate messages with different priorities
SuccessOrQuit(msgHigh[i]->SetPriority(Message::kPriorityHigh)); for (int i = kNumNewPriorityTestMessages; i < kNumTestMessages; i++)
msgNor[i] = messagePool->Allocate(Message::kTypeIp6); {
VerifyOrQuit(msgNor[i] != nullptr); msg[prio][i] = messagePool->Allocate(Message::kTypeIp6);
SuccessOrQuit(msgNor[i]->SetPriority(Message::kPriorityNormal)); VerifyOrQuit(msg[prio][i] != nullptr);
msgLow[i] = messagePool->Allocate(Message::kTypeIp6); SuccessOrQuit(msg[prio][i]->SetPriority(priority));
VerifyOrQuit(msgLow[i] != nullptr); }
SuccessOrQuit(msgLow[i]->SetPriority(Message::kPriorityLow));
} }
// Check the `GetPriority()` // Check the `GetPriority()`
for (int i = 0; i < kNumTestMessages; i++) for (int i = 0; i < kNumTestMessages; i++)
{ {
VerifyOrQuit(msgLow[i]->GetPriority() == Message::kPriorityLow); VerifyOrQuit(msg[kLo][i]->GetPriority() == Message::kPriorityLow);
VerifyOrQuit(msgNor[i]->GetPriority() == Message::kPriorityNormal); VerifyOrQuit(msg[kMd][i]->GetPriority() == Message::kPriorityNormal);
VerifyOrQuit(msgHigh[i]->GetPriority() == Message::kPriorityHigh); VerifyOrQuit(msg[kHi][i]->GetPriority() == Message::kPriorityHigh);
VerifyOrQuit(msgNet[i]->GetPriority() == Message::kPriorityNet); VerifyOrQuit(msg[kNw][i]->GetPriority() == Message::kPriorityNet);
} }
// Verify case of an empty queue. // Verify case of an empty queue.
VerifyPriorityQueueContent(queue, 0); VerifyPriorityQueueContent(queue, 0);
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Messages with the same priority only.
for (uint8_t prio = 0; prio < kNumPriorities; prio++)
{
// Add and remove one message only
queue.Enqueue(*msg[prio][0]);
VerifyPriorityQueueContent(queue, 1, msg[prio][0]);
queue.Dequeue(*msg[prio][0]);
VerifyPriorityQueueContent(queue, 0);
// Add three messages and then dequeue them in different orders.
for (uint8_t test = 0; test < 3; test++)
{
queue.Enqueue(*msg[prio][0]);
VerifyPriorityQueueContent(queue, 1, msg[prio][0]);
queue.Enqueue(*msg[prio][1]);
VerifyPriorityQueueContent(queue, 2, msg[prio][0], msg[prio][1]);
queue.Enqueue(*msg[prio][2]);
VerifyPriorityQueueContent(queue, 3, msg[prio][0], msg[prio][1], msg[prio][2]);
switch (test)
{
case 0:
// Remove the messages in the same order added.
queue.Dequeue(*msg[prio][0]);
VerifyPriorityQueueContent(queue, 2, msg[prio][1], msg[prio][2]);
queue.Dequeue(*msg[prio][1]);
VerifyPriorityQueueContent(queue, 1, msg[prio][2]);
queue.Dequeue(*msg[prio][2]);
break;
case 1:
// Remove the message in the reverse order added
queue.Dequeue(*msg[prio][2]);
VerifyPriorityQueueContent(queue, 2, msg[prio][0], msg[prio][1]);
queue.Dequeue(*msg[prio][1]);
VerifyPriorityQueueContent(queue, 1, msg[prio][0]);
queue.Dequeue(*msg[prio][0]);
break;
case 2:
// Remove the message in random order added.
queue.Dequeue(*msg[prio][1]);
VerifyPriorityQueueContent(queue, 2, msg[prio][0], msg[prio][2]);
queue.Dequeue(*msg[prio][0]);
VerifyPriorityQueueContent(queue, 1, msg[prio][2]);
queue.Dequeue(*msg[prio][2]);
break;
}
VerifyPriorityQueueContent(queue, 0);
}
}
VerifyPriorityQueueContent(queue, 0);
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Messages with the two different priorities (`prio1` lower than `prio2`)
for (uint8_t prio1 = 0; prio1 < kNumPriorities - 1; prio1++)
{
for (uint8_t prio2 = prio1 + 1; prio2 < kNumPriorities; prio2++)
{
// Add one message with `prio1` and one with `prio2` and
// then remove them. Cover all possible combinations
// (order to add and remove).
for (uint8_t test = 0; test < 4; test++)
{
switch (test)
{
case 0:
case 1:
// Add lower priority first, then higher priority
queue.Enqueue(*msg[prio1][0]);
VerifyPriorityQueueContent(queue, 1, msg[prio1][0]);
queue.Enqueue(*msg[prio2][0]);
break;
case 2:
case 3:
// Add higher priority first, then lower priority
queue.Enqueue(*msg[prio2][0]);
VerifyPriorityQueueContent(queue, 1, msg[prio2][0]);
queue.Enqueue(*msg[prio1][0]);
break;
}
VerifyPriorityQueueContent(queue, 2, msg[prio2][0], msg[prio1][0]);
switch (test)
{
case 0:
case 2:
// Remove lower priority first, then higher priority.
queue.Dequeue(*msg[prio1][0]);
VerifyPriorityQueueContent(queue, 1, msg[prio2][0]);
queue.Dequeue(*msg[prio2][0]);
break;
case 1:
case 3:
// Remove higher priority first, then lower priority
queue.Dequeue(*msg[prio2][0]);
VerifyPriorityQueueContent(queue, 1, msg[prio1][0]);
queue.Dequeue(*msg[prio1][0]);
break;
}
VerifyPriorityQueueContent(queue, 0);
}
// Add two messages with `prio1` (lower) and one with
// `prio2` (higher) and then remove them. Cover all
// possible combinations to add and remove.
for (uint8_t test = 0; test < 6; test++)
{
switch (test)
{
case 0:
case 1:
// Add two lower priority messages first, then one higher priority
queue.Enqueue(*msg[prio1][0]);
VerifyPriorityQueueContent(queue, 1, msg[prio1][0]);
queue.Enqueue(*msg[prio1][1]);
VerifyPriorityQueueContent(queue, 2, msg[prio1][0], msg[prio1][1]);
queue.Enqueue(*msg[prio2][0]);
break;
case 2:
case 3:
// Add one higher priority first, then two lower priority messages.
queue.Enqueue(*msg[prio2][0]);
VerifyPriorityQueueContent(queue, 1, msg[prio2][0]);
queue.Enqueue(*msg[prio1][0]);
VerifyPriorityQueueContent(queue, 2, msg[prio2][0], msg[prio1][0]);
queue.Enqueue(*msg[prio1][1]);
break;
case 4:
case 5:
// Add one lower priority first, then a higher priority, finally one lower priority.
queue.Enqueue(*msg[prio1][0]);
VerifyPriorityQueueContent(queue, 1, msg[prio1][0]);
queue.Enqueue(*msg[prio2][0]);
VerifyPriorityQueueContent(queue, 2, msg[prio2][0], msg[prio1][0]);
queue.Enqueue(*msg[prio1][1]);
break;
}
VerifyPriorityQueueContent(queue, 3, msg[prio2][0], msg[prio1][0], msg[prio1][1]);
switch (test)
{
case 0:
queue.Dequeue(*msg[prio1][0]);
VerifyPriorityQueueContent(queue, 2, msg[prio2][0], msg[prio1][1]);
queue.Dequeue(*msg[prio1][1]);
VerifyPriorityQueueContent(queue, 1, msg[prio2][0]);
queue.Dequeue(*msg[prio2][0]);
break;
case 1:
queue.Dequeue(*msg[prio1][0]);
VerifyPriorityQueueContent(queue, 2, msg[prio2][0], msg[prio1][1]);
queue.Dequeue(*msg[prio2][0]);
VerifyPriorityQueueContent(queue, 1, msg[prio1][1]);
queue.Dequeue(*msg[prio1][1]);
break;
case 2:
queue.Dequeue(*msg[prio1][1]);
VerifyPriorityQueueContent(queue, 2, msg[prio2][0], msg[prio1][0]);
queue.Dequeue(*msg[prio1][0]);
VerifyPriorityQueueContent(queue, 1, msg[prio2][0]);
queue.Dequeue(*msg[prio2][0]);
break;
case 3:
queue.Dequeue(*msg[prio1][1]);
VerifyPriorityQueueContent(queue, 2, msg[prio2][0], msg[prio1][0]);
queue.Dequeue(*msg[prio2][0]);
VerifyPriorityQueueContent(queue, 1, msg[prio1][0]);
queue.Dequeue(*msg[prio1][0]);
break;
case 4:
queue.Dequeue(*msg[prio2][0]);
VerifyPriorityQueueContent(queue, 2, msg[prio1][0], msg[prio1][1]);
queue.Dequeue(*msg[prio1][0]);
VerifyPriorityQueueContent(queue, 1, msg[prio1][1]);
queue.Dequeue(*msg[prio1][1]);
break;
case 5:
queue.Dequeue(*msg[prio2][0]);
VerifyPriorityQueueContent(queue, 2, msg[prio1][0], msg[prio1][1]);
queue.Dequeue(*msg[prio1][1]);
VerifyPriorityQueueContent(queue, 1, msg[prio1][0]);
queue.Dequeue(*msg[prio1][0]);
break;
}
}
VerifyPriorityQueueContent(queue, 0);
// Add two messages with `prio2` (higher) and one with
// `prio1` (lower) and then remove them. Cover all
// possible combinations to add and remove.
for (uint8_t test = 0; test < 6; test++)
{
switch (test)
{
case 0:
case 1:
// Add two higher priority messages first, then one lower priority
queue.Enqueue(*msg[prio2][0]);
VerifyPriorityQueueContent(queue, 1, msg[prio2][0]);
queue.Enqueue(*msg[prio2][1]);
VerifyPriorityQueueContent(queue, 2, msg[prio2][0], msg[prio2][1]);
queue.Enqueue(*msg[prio1][0]);
break;
case 2:
case 3:
// Add one lower priority first, then two higher priority messages.
queue.Enqueue(*msg[prio1][0]);
VerifyPriorityQueueContent(queue, 1, msg[prio1][0]);
queue.Enqueue(*msg[prio2][0]);
VerifyPriorityQueueContent(queue, 2, msg[prio2][0], msg[prio1][0]);
queue.Enqueue(*msg[prio2][1]);
break;
case 4:
case 5:
// Add one higher priority first, then a lower priority, finally one higher priority.
queue.Enqueue(*msg[prio2][0]);
VerifyPriorityQueueContent(queue, 1, msg[prio2][0]);
queue.Enqueue(*msg[prio1][0]);
VerifyPriorityQueueContent(queue, 2, msg[prio2][0], msg[prio1][0]);
queue.Enqueue(*msg[prio2][1]);
break;
}
VerifyPriorityQueueContent(queue, 3, msg[prio2][0], msg[prio2][1], msg[prio1][0]);
switch (test)
{
case 0:
queue.Dequeue(*msg[prio2][0]);
VerifyPriorityQueueContent(queue, 2, msg[prio2][1], msg[prio1][0]);
queue.Dequeue(*msg[prio2][1]);
VerifyPriorityQueueContent(queue, 1, msg[prio1][0]);
queue.Dequeue(*msg[prio1][0]);
break;
case 1:
queue.Dequeue(*msg[prio2][0]);
VerifyPriorityQueueContent(queue, 2, msg[prio2][1], msg[prio1][0]);
queue.Dequeue(*msg[prio1][0]);
VerifyPriorityQueueContent(queue, 1, msg[prio2][1]);
queue.Dequeue(*msg[prio2][1]);
break;
case 2:
queue.Dequeue(*msg[prio2][1]);
VerifyPriorityQueueContent(queue, 2, msg[prio2][0], msg[prio1][0]);
queue.Dequeue(*msg[prio2][0]);
VerifyPriorityQueueContent(queue, 1, msg[prio1][0]);
queue.Dequeue(*msg[prio1][0]);
break;
case 3:
queue.Dequeue(*msg[prio2][1]);
VerifyPriorityQueueContent(queue, 2, msg[prio2][0], msg[prio1][0]);
queue.Dequeue(*msg[prio1][0]);
VerifyPriorityQueueContent(queue, 1, msg[prio2][0]);
queue.Dequeue(*msg[prio2][0]);
break;
case 4:
queue.Dequeue(*msg[prio1][0]);
VerifyPriorityQueueContent(queue, 2, msg[prio2][0], msg[prio2][1]);
queue.Dequeue(*msg[prio2][0]);
VerifyPriorityQueueContent(queue, 1, msg[prio2][1]);
queue.Dequeue(*msg[prio2][1]);
break;
case 5:
queue.Dequeue(*msg[prio1][0]);
VerifyPriorityQueueContent(queue, 2, msg[prio2][0], msg[prio2][1]);
queue.Dequeue(*msg[prio2][1]);
VerifyPriorityQueueContent(queue, 1, msg[prio2][0]);
queue.Dequeue(*msg[prio2][0]);
break;
}
VerifyPriorityQueueContent(queue, 0);
}
}
}
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Messages with the three different priorities (prio1 < prio2 < prio3)
for (uint8_t prio1 = 0; prio1 < kNumPriorities - 2; prio1++)
{
for (uint8_t prio2 = prio1 + 1; prio2 < kNumPriorities - 1; prio2++)
{
for (uint8_t prio3 = prio2 + 1; prio3 < kNumPriorities; prio3++)
{
for (uint8_t test = 0; test < 6; test++)
{
switch (test)
{
case 0:
queue.Enqueue(*msg[prio1][0]);
VerifyPriorityQueueContent(queue, 1, msg[prio1][0]);
queue.Enqueue(*msg[prio2][0]);
VerifyPriorityQueueContent(queue, 2, msg[prio2][0], msg[prio1][0]);
queue.Enqueue(*msg[prio3][0]);
break;
case 1:
queue.Enqueue(*msg[prio1][0]);
VerifyPriorityQueueContent(queue, 1, msg[prio1][0]);
queue.Enqueue(*msg[prio3][0]);
VerifyPriorityQueueContent(queue, 2, msg[prio3][0], msg[prio1][0]);
queue.Enqueue(*msg[prio2][0]);
break;
case 2:
queue.Enqueue(*msg[prio2][0]);
VerifyPriorityQueueContent(queue, 1, msg[prio2][0]);
queue.Enqueue(*msg[prio1][0]);
VerifyPriorityQueueContent(queue, 2, msg[prio2][0], msg[prio1][0]);
queue.Enqueue(*msg[prio3][0]);
break;
case 3:
queue.Enqueue(*msg[prio2][0]);
VerifyPriorityQueueContent(queue, 1, msg[prio2][0]);
queue.Enqueue(*msg[prio3][0]);
VerifyPriorityQueueContent(queue, 2, msg[prio3][0], msg[prio2][0]);
queue.Enqueue(*msg[prio1][0]);
break;
case 4:
queue.Enqueue(*msg[prio3][0]);
VerifyPriorityQueueContent(queue, 1, msg[prio3][0]);
queue.Enqueue(*msg[prio1][0]);
VerifyPriorityQueueContent(queue, 2, msg[prio3][0], msg[prio1][0]);
queue.Enqueue(*msg[prio2][0]);
break;
case 5:
queue.Enqueue(*msg[prio3][0]);
VerifyPriorityQueueContent(queue, 1, msg[prio3][0]);
queue.Enqueue(*msg[prio2][0]);
VerifyPriorityQueueContent(queue, 2, msg[prio3][0], msg[prio2][0]);
queue.Enqueue(*msg[prio1][0]);
break;
}
VerifyPriorityQueueContent(queue, 3, msg[prio3][0], msg[prio2][0], msg[prio1][0]);
switch (test)
{
case 0:
case 1:
case 2:
queue.Dequeue(*msg[prio1][0]);
VerifyPriorityQueueContent(queue, 2, msg[prio3][0], msg[prio2][0]);
queue.Dequeue(*msg[prio2][0]);
VerifyPriorityQueueContent(queue, 1, msg[prio3][0]);
queue.Dequeue(*msg[prio3][0]);
break;
case 3:
case 4:
case 5:
queue.Dequeue(*msg[prio3][0]);
VerifyPriorityQueueContent(queue, 2, msg[prio2][0], msg[prio1][0]);
queue.Dequeue(*msg[prio1][0]);
VerifyPriorityQueueContent(queue, 1, msg[prio2][0]);
queue.Dequeue(*msg[prio2][0]);
break;
}
VerifyPriorityQueueContent(queue, 0);
}
}
}
}
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Add msgs in different orders and check the content of queue. // Add msgs in different orders and check the content of queue.
queue.Enqueue(*msgHigh[0]);
VerifyPriorityQueueContent(queue, 1, msgHigh[0]); queue.Enqueue(*msg[kHi][0]);
queue.Enqueue(*msgHigh[1]); VerifyPriorityQueueContent(queue, 1, msg[kHi][0]);
VerifyPriorityQueueContent(queue, 2, msgHigh[0], msgHigh[1]); queue.Enqueue(*msg[kHi][1]);
queue.Enqueue(*msgNet[0]); VerifyPriorityQueueContent(queue, 2, msg[kHi][0], msg[kHi][1]);
VerifyPriorityQueueContent(queue, 3, msgNet[0], msgHigh[0], msgHigh[1]); queue.Enqueue(*msg[kNw][0]);
queue.Enqueue(*msgNet[1]); VerifyPriorityQueueContent(queue, 3, msg[kNw][0], msg[kHi][0], msg[kHi][1]);
VerifyPriorityQueueContent(queue, 4, msgNet[0], msgNet[1], msgHigh[0], msgHigh[1]); queue.Enqueue(*msg[kNw][1]);
queue.Enqueue(*msgHigh[2]); VerifyPriorityQueueContent(queue, 4, msg[kNw][0], msg[kNw][1], msg[kHi][0], msg[kHi][1]);
VerifyPriorityQueueContent(queue, 5, msgNet[0], msgNet[1], msgHigh[0], msgHigh[1], msgHigh[2]); queue.Enqueue(*msg[kHi][2]);
queue.Enqueue(*msgLow[0]); VerifyPriorityQueueContent(queue, 5, msg[kNw][0], msg[kNw][1], msg[kHi][0], msg[kHi][1], msg[kHi][2]);
VerifyPriorityQueueContent(queue, 6, msgNet[0], msgNet[1], msgHigh[0], msgHigh[1], msgHigh[2], msgLow[0]); queue.Enqueue(*msg[kLo][0]);
queue.Enqueue(*msgNor[0]); VerifyPriorityQueueContent(queue, 6, msg[kNw][0], msg[kNw][1], msg[kHi][0], msg[kHi][1], msg[kHi][2], msg[kLo][0]);
VerifyPriorityQueueContent(queue, 7, msgNet[0], msgNet[1], msgHigh[0], msgHigh[1], msgHigh[2], msgNor[0], queue.Enqueue(*msg[kMd][0]);
msgLow[0]); VerifyPriorityQueueContent(queue, 7, msg[kNw][0], msg[kNw][1], msg[kHi][0], msg[kHi][1], msg[kHi][2], msg[kMd][0],
queue.Enqueue(*msgHigh[3]); msg[kLo][0]);
VerifyPriorityQueueContent(queue, 8, msgNet[0], msgNet[1], msgHigh[0], msgHigh[1], msgHigh[2], msgHigh[3], queue.Enqueue(*msg[kHi][3]);
msgNor[0], msgLow[0]); VerifyPriorityQueueContent(queue, 8, msg[kNw][0], msg[kNw][1], msg[kHi][0], msg[kHi][1], msg[kHi][2], msg[kHi][3],
msg[kMd][0], msg[kLo][0]);
// Remove messages in different order and check the content of queue in each step. // Remove messages in different order and check the content of queue in each step.
queue.Dequeue(*msgNet[0]); queue.Dequeue(*msg[kNw][0]);
VerifyPriorityQueueContent(queue, 7, msgNet[1], msgHigh[0], msgHigh[1], msgHigh[2], msgHigh[3], msgNor[0], VerifyPriorityQueueContent(queue, 7, msg[kNw][1], msg[kHi][0], msg[kHi][1], msg[kHi][2], msg[kHi][3], msg[kMd][0],
msgLow[0]); msg[kLo][0]);
queue.Dequeue(*msgHigh[2]); queue.Dequeue(*msg[kHi][2]);
VerifyPriorityQueueContent(queue, 6, msgNet[1], msgHigh[0], msgHigh[1], msgHigh[3], msgNor[0], msgLow[0]); VerifyPriorityQueueContent(queue, 6, msg[kNw][1], msg[kHi][0], msg[kHi][1], msg[kHi][3], msg[kMd][0], msg[kLo][0]);
queue.Dequeue(*msgNor[0]); queue.Dequeue(*msg[kMd][0]);
VerifyPriorityQueueContent(queue, 5, msgNet[1], msgHigh[0], msgHigh[1], msgHigh[3], msgLow[0]); VerifyPriorityQueueContent(queue, 5, msg[kNw][1], msg[kHi][0], msg[kHi][1], msg[kHi][3], msg[kLo][0]);
queue.Dequeue(*msgHigh[1]); queue.Dequeue(*msg[kHi][1]);
VerifyPriorityQueueContent(queue, 4, msgNet[1], msgHigh[0], msgHigh[3], msgLow[0]); VerifyPriorityQueueContent(queue, 4, msg[kNw][1], msg[kHi][0], msg[kHi][3], msg[kLo][0]);
queue.Dequeue(*msgLow[0]); queue.Dequeue(*msg[kLo][0]);
VerifyPriorityQueueContent(queue, 3, msgNet[1], msgHigh[0], msgHigh[3]); VerifyPriorityQueueContent(queue, 3, msg[kNw][1], msg[kHi][0], msg[kHi][3]);
queue.Dequeue(*msgNet[1]); queue.Dequeue(*msg[kNw][1]);
VerifyPriorityQueueContent(queue, 2, msgHigh[0], msgHigh[3]); VerifyPriorityQueueContent(queue, 2, msg[kHi][0], msg[kHi][3]);
queue.Dequeue(*msgHigh[0]); queue.Dequeue(*msg[kHi][0]);
VerifyPriorityQueueContent(queue, 1, msgHigh[3]); VerifyPriorityQueueContent(queue, 1, msg[kHi][3]);
queue.Dequeue(*msgHigh[3]); queue.Dequeue(*msg[kHi][3]);
VerifyPriorityQueueContent(queue, 0); VerifyPriorityQueueContent(queue, 0);
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Validate that priority of an already queued message in a priority queue cannot change // Validate that priority of an already queued message in a priority queue cannot change
queue.Enqueue(*msgNor[0]); queue.Enqueue(*msg[kMd][0]);
VerifyPriorityQueueContent(queue, 1, msgNor[0]); VerifyPriorityQueueContent(queue, 1, msg[kMd][0]);
queue.Enqueue(*msgHigh[0]); queue.Enqueue(*msg[kHi][0]);
VerifyPriorityQueueContent(queue, 2, msgHigh[0], msgNor[0]); VerifyPriorityQueueContent(queue, 2, msg[kHi][0], msg[kMd][0]);
queue.Enqueue(*msgLow[0]); queue.Enqueue(*msg[kLo][0]);
VerifyPriorityQueueContent(queue, 3, msgHigh[0], msgNor[0], msgLow[0]); VerifyPriorityQueueContent(queue, 3, msg[kHi][0], msg[kMd][0], msg[kLo][0]);
VerifyOrQuit(msgNor[0]->SetPriority(Message::kPriorityNet) == kErrorInvalidState); VerifyOrQuit(msg[kMd][0]->SetPriority(Message::kPriorityNet) == kErrorInvalidState);
VerifyOrQuit(msgLow[0]->SetPriority(Message::kPriorityLow) == kErrorInvalidState); VerifyOrQuit(msg[kLo][0]->SetPriority(Message::kPriorityLow) == kErrorInvalidState);
VerifyOrQuit(msgLow[0]->SetPriority(Message::kPriorityNormal) == kErrorInvalidState); VerifyOrQuit(msg[kLo][0]->SetPriority(Message::kPriorityNormal) == kErrorInvalidState);
VerifyOrQuit(msgHigh[0]->SetPriority(Message::kPriorityNormal) == kErrorInvalidState); VerifyOrQuit(msg[kHi][0]->SetPriority(Message::kPriorityNormal) == kErrorInvalidState);
VerifyPriorityQueueContent(queue, 3, msgHigh[0], msgNor[0], msgLow[0]); VerifyPriorityQueueContent(queue, 3, msg[kHi][0], msg[kMd][0], msg[kLo][0]);
// Remove messages from the queue // Remove messages from the queue
queue.Dequeue(*msgHigh[0]); queue.Dequeue(*msg[kHi][0]);
VerifyPriorityQueueContent(queue, 2, msgNor[0], msgLow[0]); VerifyPriorityQueueContent(queue, 2, msg[kMd][0], msg[kLo][0]);
queue.Dequeue(*msgLow[0]); queue.Dequeue(*msg[kLo][0]);
VerifyPriorityQueueContent(queue, 1, msgNor[0]); VerifyPriorityQueueContent(queue, 1, msg[kMd][0]);
queue.Dequeue(*msgNor[0]); queue.Dequeue(*msg[kMd][0]);
VerifyPriorityQueueContent(queue, 0); VerifyPriorityQueueContent(queue, 0);
for (Message *message : msgNor) for (Message *message : msg[kMd])
{ {
SuccessOrQuit(message->SetPriority(Message::kPriorityNormal)); SuccessOrQuit(message->SetPriority(Message::kPriorityNormal));
} }
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Range-based `for` and dequeue during iteration // Range-based `for` and dequeue during iteration
for (uint16_t removeIndex = 0; removeIndex < 4; removeIndex++) for (uint16_t removeIndex = 0; removeIndex < 4; removeIndex++)
{ {
uint16_t index = 0; uint16_t index = 0;
queue.Enqueue(*msgNor[0]); queue.Enqueue(*msg[kMd][0]);
queue.Enqueue(*msgNor[1]); queue.Enqueue(*msg[kMd][1]);
queue.Enqueue(*msgNor[2]); queue.Enqueue(*msg[kMd][2]);
queue.Enqueue(*msgNor[3]); queue.Enqueue(*msg[kMd][3]);
VerifyPriorityQueueContent(queue, 4, msgNor[0], msgNor[1], msgNor[2], msgNor[3]); VerifyPriorityQueueContent(queue, 4, msg[kMd][0], msg[kMd][1], msg[kMd][2], msg[kMd][3]);
// While iterating over the queue remove the entry at `removeIndex` // While iterating over the queue remove the entry at `removeIndex`
for (Message &message : queue) for (Message &message : queue)
@@ -278,7 +675,7 @@ void TestPriorityQueue(void)
queue.Dequeue(message); queue.Dequeue(message);
} }
VerifyOrQuit(&message == msgNor[index++]); VerifyOrQuit(&message == msg[kMd][index++]);
} }
index = 0; index = 0;
@@ -291,7 +688,7 @@ void TestPriorityQueue(void)
index++; index++;
} }
VerifyOrQuit(&message == msgNor[index++]); VerifyOrQuit(&message == msg[kMd][index++]);
queue.Dequeue(message); queue.Dequeue(message);
} }
@@ -300,6 +697,7 @@ void TestPriorityQueue(void)
testFreeInstance(instance); testFreeInstance(instance);
} }
// NOLINTEND(modernize-loop-convert)
} // namespace ot } // namespace ot