mirror of
https://github.com/espressif/openthread.git
synced 2026-07-08 05:10:25 +00:00
c6f3f1ff31
This commit modifies `Message::SetPriority()` to prevent changing a message's priority after it has been enqueued in a `PriorityQueue`. Attempting to do so will now return `kErrorInvalidState`. The functionality for altering the priority of an already-enqueued message is not currently used or required. Should this behavior become necessary in the future, the recommended approach is to explicitly dequeue the message first, then change its priority, and finally re-add it to the queue. This makes the intended behavior clearer and more explicit within the code. This commit also updates the `test_priority_queue` unit test to reflect this change.
312 lines
13 KiB
C++
312 lines
13 KiB
C++
/*
|
|
* Copyright (c) 2016, 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 <stdarg.h>
|
|
|
|
#include "common/debug.hpp"
|
|
#include "common/message.hpp"
|
|
#include "instance/instance.hpp"
|
|
|
|
#include "test_platform.h"
|
|
#include "test_util.h"
|
|
|
|
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
|
|
void VerifyPriorityQueueContent(PriorityQueue &aPriorityQueue, int aExpectedLength, ...)
|
|
{
|
|
const PriorityQueue &constQueue = aPriorityQueue;
|
|
va_list args;
|
|
Message *message;
|
|
Message *msgArg;
|
|
int8_t curPriority = Message::kNumPriorities;
|
|
PriorityQueue::Info info;
|
|
|
|
// Check the `GetInfo`
|
|
memset(&info, 0, sizeof(info));
|
|
aPriorityQueue.GetInfo(info);
|
|
VerifyOrQuit(info.mNumMessages == aExpectedLength, "GetInfo() result does not match expected len.");
|
|
|
|
va_start(args, aExpectedLength);
|
|
|
|
if (aExpectedLength == 0)
|
|
{
|
|
message = aPriorityQueue.GetHead();
|
|
VerifyOrQuit(message == nullptr, "PriorityQueue is not empty when expected len is zero.");
|
|
|
|
VerifyOrQuit(aPriorityQueue.GetHeadForPriority(Message::kPriorityLow) == nullptr);
|
|
VerifyOrQuit(aPriorityQueue.GetHeadForPriority(Message::kPriorityNormal) == nullptr);
|
|
VerifyOrQuit(aPriorityQueue.GetHeadForPriority(Message::kPriorityHigh) == nullptr);
|
|
VerifyOrQuit(aPriorityQueue.GetHeadForPriority(Message::kPriorityNet) == nullptr);
|
|
}
|
|
else
|
|
{
|
|
// Go through all messages in the queue and verify they match the passed-in messages
|
|
for (message = aPriorityQueue.GetHead(); message != nullptr; message = message->GetNext())
|
|
{
|
|
VerifyOrQuit(aExpectedLength != 0, "PriorityQueue contains more entries than expected.");
|
|
|
|
msgArg = va_arg(args, Message *);
|
|
|
|
if (msgArg->GetPriority() != curPriority)
|
|
{
|
|
for (curPriority--; curPriority != msgArg->GetPriority(); curPriority--)
|
|
{
|
|
// Check the `GetHeadForPriority` is nullptr if there are no expected message for this priority
|
|
// level.
|
|
VerifyOrQuit(aPriorityQueue.GetHeadForPriority(static_cast<Message::Priority>(curPriority)) ==
|
|
nullptr,
|
|
"is non-nullptr when no expected msg for this priority.");
|
|
}
|
|
|
|
// Check the `GetHeadForPriority`.
|
|
VerifyOrQuit(aPriorityQueue.GetHeadForPriority(static_cast<Message::Priority>(curPriority)) == msgArg);
|
|
}
|
|
|
|
// Check the queued message to match the one from argument list
|
|
VerifyOrQuit(msgArg == message, "PriorityQueue content does not match what is expected.");
|
|
|
|
aExpectedLength--;
|
|
}
|
|
|
|
VerifyOrQuit(aExpectedLength == 0, "PriorityQueue contains less entries than expected.");
|
|
|
|
// Check the `GetHeadForPriority` is nullptr if there are no expected message for any remaining priority level.
|
|
for (curPriority--; curPriority >= 0; curPriority--)
|
|
{
|
|
VerifyOrQuit(aPriorityQueue.GetHeadForPriority(static_cast<Message::Priority>(curPriority)) == nullptr,
|
|
"is non-nullptr when no expected msg for this priority.");
|
|
}
|
|
}
|
|
|
|
va_end(args);
|
|
|
|
// Check range-based `for` loop iteration using non-const iterator
|
|
|
|
message = aPriorityQueue.GetHead();
|
|
|
|
for (Message &msg : aPriorityQueue)
|
|
{
|
|
VerifyOrQuit(message == &msg, "`for` loop iteration does not match expected");
|
|
message = message->GetNext();
|
|
}
|
|
|
|
VerifyOrQuit(message == nullptr, "`for` loop iteration resulted in fewer entries than expected");
|
|
|
|
// Check range-base `for` iteration using const iterator
|
|
|
|
message = aPriorityQueue.GetHead();
|
|
|
|
for (const Message &constMsg : constQueue)
|
|
{
|
|
VerifyOrQuit(message == &constMsg, "`for` loop iteration does not match expected");
|
|
message = message->GetNext();
|
|
}
|
|
|
|
VerifyOrQuit(message == nullptr, "`for` loop iteration resulted in fewer entries than expected");
|
|
}
|
|
|
|
void TestPriorityQueue(void)
|
|
{
|
|
Instance *instance;
|
|
MessagePool *messagePool;
|
|
PriorityQueue queue;
|
|
Message *msgNet[kNumTestMessages];
|
|
Message *msgHigh[kNumTestMessages];
|
|
Message *msgNor[kNumTestMessages];
|
|
Message *msgLow[kNumTestMessages];
|
|
|
|
instance = testInitInstance();
|
|
VerifyOrQuit(instance != nullptr, "Null OpenThread instance");
|
|
|
|
messagePool = &instance->Get<MessagePool>();
|
|
|
|
// Use the function "Allocate()" to allocate messages with different priorities
|
|
for (int i = 0; i < kNumNewPriorityTestMessages; i++)
|
|
{
|
|
msgNet[i] = messagePool->Allocate(Message::kTypeIp6, 0, Message::Settings(Message::kPriorityNet));
|
|
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
|
|
for (int i = kNumNewPriorityTestMessages; i < kNumTestMessages; i++)
|
|
{
|
|
msgNet[i] = messagePool->Allocate(Message::kTypeIp6);
|
|
VerifyOrQuit(msgNet[i] != nullptr);
|
|
SuccessOrQuit(msgNet[i]->SetPriority(Message::kPriorityNet));
|
|
msgHigh[i] = messagePool->Allocate(Message::kTypeIp6);
|
|
VerifyOrQuit(msgHigh[i] != nullptr);
|
|
SuccessOrQuit(msgHigh[i]->SetPriority(Message::kPriorityHigh));
|
|
msgNor[i] = messagePool->Allocate(Message::kTypeIp6);
|
|
VerifyOrQuit(msgNor[i] != nullptr);
|
|
SuccessOrQuit(msgNor[i]->SetPriority(Message::kPriorityNormal));
|
|
msgLow[i] = messagePool->Allocate(Message::kTypeIp6);
|
|
VerifyOrQuit(msgLow[i] != nullptr);
|
|
SuccessOrQuit(msgLow[i]->SetPriority(Message::kPriorityLow));
|
|
}
|
|
|
|
// Check the `GetPriority()`
|
|
for (int i = 0; i < kNumTestMessages; i++)
|
|
{
|
|
VerifyOrQuit(msgLow[i]->GetPriority() == Message::kPriorityLow);
|
|
VerifyOrQuit(msgNor[i]->GetPriority() == Message::kPriorityNormal);
|
|
VerifyOrQuit(msgHigh[i]->GetPriority() == Message::kPriorityHigh);
|
|
VerifyOrQuit(msgNet[i]->GetPriority() == Message::kPriorityNet);
|
|
}
|
|
|
|
// Verify case of an empty queue.
|
|
VerifyPriorityQueueContent(queue, 0);
|
|
|
|
// Add msgs in different orders and check the content of queue.
|
|
queue.Enqueue(*msgHigh[0]);
|
|
VerifyPriorityQueueContent(queue, 1, msgHigh[0]);
|
|
queue.Enqueue(*msgHigh[1]);
|
|
VerifyPriorityQueueContent(queue, 2, msgHigh[0], msgHigh[1]);
|
|
queue.Enqueue(*msgNet[0]);
|
|
VerifyPriorityQueueContent(queue, 3, msgNet[0], msgHigh[0], msgHigh[1]);
|
|
queue.Enqueue(*msgNet[1]);
|
|
VerifyPriorityQueueContent(queue, 4, msgNet[0], msgNet[1], msgHigh[0], msgHigh[1]);
|
|
queue.Enqueue(*msgHigh[2]);
|
|
VerifyPriorityQueueContent(queue, 5, msgNet[0], msgNet[1], msgHigh[0], msgHigh[1], msgHigh[2]);
|
|
queue.Enqueue(*msgLow[0]);
|
|
VerifyPriorityQueueContent(queue, 6, msgNet[0], msgNet[1], msgHigh[0], msgHigh[1], msgHigh[2], msgLow[0]);
|
|
queue.Enqueue(*msgNor[0]);
|
|
VerifyPriorityQueueContent(queue, 7, msgNet[0], msgNet[1], msgHigh[0], msgHigh[1], msgHigh[2], msgNor[0],
|
|
msgLow[0]);
|
|
queue.Enqueue(*msgHigh[3]);
|
|
VerifyPriorityQueueContent(queue, 8, msgNet[0], msgNet[1], msgHigh[0], msgHigh[1], msgHigh[2], msgHigh[3],
|
|
msgNor[0], msgLow[0]);
|
|
|
|
// Remove messages in different order and check the content of queue in each step.
|
|
queue.Dequeue(*msgNet[0]);
|
|
VerifyPriorityQueueContent(queue, 7, msgNet[1], msgHigh[0], msgHigh[1], msgHigh[2], msgHigh[3], msgNor[0],
|
|
msgLow[0]);
|
|
queue.Dequeue(*msgHigh[2]);
|
|
VerifyPriorityQueueContent(queue, 6, msgNet[1], msgHigh[0], msgHigh[1], msgHigh[3], msgNor[0], msgLow[0]);
|
|
queue.Dequeue(*msgNor[0]);
|
|
VerifyPriorityQueueContent(queue, 5, msgNet[1], msgHigh[0], msgHigh[1], msgHigh[3], msgLow[0]);
|
|
queue.Dequeue(*msgHigh[1]);
|
|
VerifyPriorityQueueContent(queue, 4, msgNet[1], msgHigh[0], msgHigh[3], msgLow[0]);
|
|
queue.Dequeue(*msgLow[0]);
|
|
VerifyPriorityQueueContent(queue, 3, msgNet[1], msgHigh[0], msgHigh[3]);
|
|
queue.Dequeue(*msgNet[1]);
|
|
VerifyPriorityQueueContent(queue, 2, msgHigh[0], msgHigh[3]);
|
|
queue.Dequeue(*msgHigh[0]);
|
|
VerifyPriorityQueueContent(queue, 1, msgHigh[3]);
|
|
queue.Dequeue(*msgHigh[3]);
|
|
VerifyPriorityQueueContent(queue, 0);
|
|
|
|
// Validate that priority of an already queued message in a priority queue cannot change
|
|
queue.Enqueue(*msgNor[0]);
|
|
VerifyPriorityQueueContent(queue, 1, msgNor[0]);
|
|
queue.Enqueue(*msgHigh[0]);
|
|
VerifyPriorityQueueContent(queue, 2, msgHigh[0], msgNor[0]);
|
|
queue.Enqueue(*msgLow[0]);
|
|
VerifyPriorityQueueContent(queue, 3, msgHigh[0], msgNor[0], msgLow[0]);
|
|
|
|
VerifyOrQuit(msgNor[0]->SetPriority(Message::kPriorityNet) == kErrorInvalidState);
|
|
VerifyOrQuit(msgLow[0]->SetPriority(Message::kPriorityLow) == kErrorInvalidState);
|
|
VerifyOrQuit(msgLow[0]->SetPriority(Message::kPriorityNormal) == kErrorInvalidState);
|
|
VerifyOrQuit(msgHigh[0]->SetPriority(Message::kPriorityNormal) == kErrorInvalidState);
|
|
VerifyPriorityQueueContent(queue, 3, msgHigh[0], msgNor[0], msgLow[0]);
|
|
|
|
// Remove messages from the queue
|
|
queue.Dequeue(*msgHigh[0]);
|
|
VerifyPriorityQueueContent(queue, 2, msgNor[0], msgLow[0]);
|
|
queue.Dequeue(*msgLow[0]);
|
|
VerifyPriorityQueueContent(queue, 1, msgNor[0]);
|
|
queue.Dequeue(*msgNor[0]);
|
|
VerifyPriorityQueueContent(queue, 0);
|
|
|
|
for (Message *message : msgNor)
|
|
{
|
|
SuccessOrQuit(message->SetPriority(Message::kPriorityNormal));
|
|
}
|
|
|
|
// Range-based `for` and dequeue during iteration
|
|
|
|
for (uint16_t removeIndex = 0; removeIndex < 4; removeIndex++)
|
|
{
|
|
uint16_t index = 0;
|
|
|
|
queue.Enqueue(*msgNor[0]);
|
|
queue.Enqueue(*msgNor[1]);
|
|
queue.Enqueue(*msgNor[2]);
|
|
queue.Enqueue(*msgNor[3]);
|
|
VerifyPriorityQueueContent(queue, 4, msgNor[0], msgNor[1], msgNor[2], msgNor[3]);
|
|
|
|
// While iterating over the queue remove the entry at `removeIndex`
|
|
for (Message &message : queue)
|
|
{
|
|
if (index == removeIndex)
|
|
{
|
|
queue.Dequeue(message);
|
|
}
|
|
|
|
VerifyOrQuit(&message == msgNor[index++]);
|
|
}
|
|
|
|
index = 0;
|
|
|
|
// Iterate over the queue and remove all
|
|
for (Message &message : queue)
|
|
{
|
|
if (index == removeIndex)
|
|
{
|
|
index++;
|
|
}
|
|
|
|
VerifyOrQuit(&message == msgNor[index++]);
|
|
queue.Dequeue(message);
|
|
}
|
|
|
|
VerifyPriorityQueueContent(queue, 0);
|
|
}
|
|
|
|
testFreeInstance(instance);
|
|
}
|
|
|
|
} // namespace ot
|
|
|
|
int main(void)
|
|
{
|
|
ot::TestPriorityQueue();
|
|
printf("All tests passed\n");
|
|
return 0;
|
|
}
|