mirror of
https://github.com/betaflight/betaflight.git
synced 2025-07-25 17:25:20 +03:00
Optimised scheduler queue iterators.
This commit is contained in:
parent
3d403fed8f
commit
757fb54512
4 changed files with 60 additions and 17 deletions
|
@ -22,9 +22,11 @@
|
||||||
|
|
||||||
#ifdef UNIT_TEST
|
#ifdef UNIT_TEST
|
||||||
#define STATIC_UNIT_TESTED // make visible to unit test
|
#define STATIC_UNIT_TESTED // make visible to unit test
|
||||||
|
#define INLINE_UNIT_TESTED // make visible to unit test
|
||||||
#define UNIT_TESTED
|
#define UNIT_TESTED
|
||||||
#else
|
#else
|
||||||
#define STATIC_UNIT_TESTED static
|
#define STATIC_UNIT_TESTED static
|
||||||
|
#define INLINE_UNIT_TESTED inline
|
||||||
#define UNIT_TESTED
|
#define UNIT_TESTED
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
|
@ -43,7 +43,6 @@ bool unittest_outsideRealtimeGuardInterval;
|
||||||
|
|
||||||
#else
|
#else
|
||||||
|
|
||||||
#define SET_SCHEDULER_LOCALS() {}
|
|
||||||
#define GET_SCHEDULER_LOCALS() {}
|
#define GET_SCHEDULER_LOCALS() {}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -58,10 +57,6 @@ uint8_t unittest_scheduler_selectedTaskDynPrio;
|
||||||
uint16_t unittest_scheduler_waitingTasks;
|
uint16_t unittest_scheduler_waitingTasks;
|
||||||
uint32_t unittest_scheduler_timeToNextRealtimeTask;
|
uint32_t unittest_scheduler_timeToNextRealtimeTask;
|
||||||
|
|
||||||
#define SET_SCHEDULER_LOCALS() \
|
|
||||||
{ \
|
|
||||||
}
|
|
||||||
|
|
||||||
#define GET_SCHEDULER_LOCALS() \
|
#define GET_SCHEDULER_LOCALS() \
|
||||||
{ \
|
{ \
|
||||||
unittest_scheduler_selectedTask = selectedTask; \
|
unittest_scheduler_selectedTask = selectedTask; \
|
||||||
|
|
|
@ -50,9 +50,9 @@ static int taskQueuePos = 0;
|
||||||
static int taskQueueSize = 0;
|
static int taskQueueSize = 0;
|
||||||
// No need for a linked list for the queue, since items are only inserted at startup
|
// No need for a linked list for the queue, since items are only inserted at startup
|
||||||
#ifdef UNIT_TEST
|
#ifdef UNIT_TEST
|
||||||
STATIC_UNIT_TESTED cfTask_t* taskQueueArray[TASK_COUNT + 1]; // 1 extra space so test code can check for buffer overruns
|
STATIC_UNIT_TESTED cfTask_t* taskQueueArray[TASK_COUNT + 2]; // 1 extra space so test code can check for buffer overruns
|
||||||
#else
|
#else
|
||||||
static cfTask_t* taskQueueArray[TASK_COUNT];
|
static cfTask_t* taskQueueArray[TASK_COUNT + 1]; // extra item for NULL pointer at end of queue
|
||||||
#endif
|
#endif
|
||||||
STATIC_UNIT_TESTED void queueClear(void)
|
STATIC_UNIT_TESTED void queueClear(void)
|
||||||
{
|
{
|
||||||
|
@ -102,23 +102,31 @@ STATIC_UNIT_TESTED void queueRemove(cfTask_t *task)
|
||||||
{
|
{
|
||||||
for (int ii = 0; ii < taskQueueSize; ++ii) {
|
for (int ii = 0; ii < taskQueueSize; ++ii) {
|
||||||
if (taskQueueArray[ii] == task) {
|
if (taskQueueArray[ii] == task) {
|
||||||
--taskQueueSize;
|
|
||||||
memmove(&taskQueueArray[ii], &taskQueueArray[ii+1], sizeof(task) * (taskQueueSize - ii));
|
memmove(&taskQueueArray[ii], &taskQueueArray[ii+1], sizeof(task) * (taskQueueSize - ii));
|
||||||
|
--taskQueueSize;
|
||||||
|
if (taskQueueSize == 0) {
|
||||||
|
taskQueueArray[0] = NULL; // ensure item zero is null when queue is empty
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
STATIC_UNIT_TESTED cfTask_t *queueFirst(void)
|
/*
|
||||||
|
* Returns first item queue or NULL if queue empty
|
||||||
|
*/
|
||||||
|
INLINE_UNIT_TESTED cfTask_t *queueFirst(void)
|
||||||
{
|
{
|
||||||
taskQueuePos = 0;
|
taskQueuePos = 0;
|
||||||
return taskQueueSize > 0 ? taskQueueArray[0] : NULL;
|
return taskQueueArray[0]; // guaranteed to be NULL if queue is empty
|
||||||
}
|
}
|
||||||
|
|
||||||
STATIC_UNIT_TESTED cfTask_t *queueNext(void)
|
/*
|
||||||
|
* Returns next item in queue or NULL if at end of queue
|
||||||
|
*/
|
||||||
|
INLINE_UNIT_TESTED cfTask_t *queueNext(void)
|
||||||
{
|
{
|
||||||
++taskQueuePos;
|
return taskQueueArray[++taskQueuePos]; // guaranteed to be NULL at end of queue
|
||||||
return taskQueuePos < taskQueueSize ? taskQueueArray[taskQueuePos] : NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void taskSystem(void)
|
void taskSystem(void)
|
||||||
|
@ -195,7 +203,6 @@ void schedulerInit(void)
|
||||||
|
|
||||||
void scheduler(void)
|
void scheduler(void)
|
||||||
{
|
{
|
||||||
SET_SCHEDULER_LOCALS();
|
|
||||||
/* Cache currentTime */
|
/* Cache currentTime */
|
||||||
currentTime = micros();
|
currentTime = micros();
|
||||||
|
|
||||||
|
|
|
@ -94,25 +94,43 @@ TEST(SchedulerUnittest, TestPriorites)
|
||||||
EXPECT_EQ(TASK_PRIORITY_MEDIUM, cfTasks[TASK_BATTERY].staticPriority);
|
EXPECT_EQ(TASK_PRIORITY_MEDIUM, cfTasks[TASK_BATTERY].staticPriority);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(SchedulerUnittest, TestQueue)
|
TEST(SchedulerUnittest, TestQueueInit)
|
||||||
{
|
{
|
||||||
queueClear();
|
queueClear();
|
||||||
EXPECT_EQ(0, queueSize());
|
EXPECT_EQ(0, queueSize());
|
||||||
|
EXPECT_EQ(0, queueFirst());
|
||||||
|
EXPECT_EQ(0, queueNext());
|
||||||
|
for (int ii = 0; ii <= TASK_COUNT; ++ii) {
|
||||||
|
EXPECT_EQ(0, taskQueueArray[ii]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cfTask_t *deadBeefPtr = reinterpret_cast<cfTask_t*>(0xDEADBEEF);
|
||||||
|
|
||||||
|
TEST(SchedulerUnittest, TestQueue)
|
||||||
|
{
|
||||||
|
queueClear();
|
||||||
|
taskQueueArray[TASK_COUNT + 1] = deadBeefPtr;
|
||||||
|
|
||||||
queueAdd(&cfTasks[TASK_SYSTEM]); // TASK_PRIORITY_HIGH
|
queueAdd(&cfTasks[TASK_SYSTEM]); // TASK_PRIORITY_HIGH
|
||||||
EXPECT_EQ(1, queueSize());
|
EXPECT_EQ(1, queueSize());
|
||||||
EXPECT_EQ(&cfTasks[TASK_SYSTEM], queueFirst());
|
EXPECT_EQ(&cfTasks[TASK_SYSTEM], queueFirst());
|
||||||
|
EXPECT_EQ(deadBeefPtr, taskQueueArray[TASK_COUNT + 1]);
|
||||||
|
|
||||||
queueAdd(&cfTasks[TASK_GYROPID]); // TASK_PRIORITY_REALTIME
|
queueAdd(&cfTasks[TASK_GYROPID]); // TASK_PRIORITY_REALTIME
|
||||||
EXPECT_EQ(2, queueSize());
|
EXPECT_EQ(2, queueSize());
|
||||||
EXPECT_EQ(&cfTasks[TASK_GYROPID], queueFirst());
|
EXPECT_EQ(&cfTasks[TASK_GYROPID], queueFirst());
|
||||||
EXPECT_EQ(&cfTasks[TASK_SYSTEM], queueNext());
|
EXPECT_EQ(&cfTasks[TASK_SYSTEM], queueNext());
|
||||||
|
EXPECT_EQ(NULL, queueNext());
|
||||||
|
EXPECT_EQ(deadBeefPtr, taskQueueArray[TASK_COUNT + 1]);
|
||||||
|
|
||||||
queueAdd(&cfTasks[TASK_SERIAL]); // TASK_PRIORITY_LOW
|
queueAdd(&cfTasks[TASK_SERIAL]); // TASK_PRIORITY_LOW
|
||||||
EXPECT_EQ(3, queueSize());
|
EXPECT_EQ(3, queueSize());
|
||||||
EXPECT_EQ(&cfTasks[TASK_GYROPID], queueFirst());
|
EXPECT_EQ(&cfTasks[TASK_GYROPID], queueFirst());
|
||||||
EXPECT_EQ(&cfTasks[TASK_SYSTEM], queueNext());
|
EXPECT_EQ(&cfTasks[TASK_SYSTEM], queueNext());
|
||||||
EXPECT_EQ(&cfTasks[TASK_SERIAL], queueNext());
|
EXPECT_EQ(&cfTasks[TASK_SERIAL], queueNext());
|
||||||
|
EXPECT_EQ(NULL, queueNext());
|
||||||
|
EXPECT_EQ(deadBeefPtr, taskQueueArray[TASK_COUNT + 1]);
|
||||||
|
|
||||||
queueAdd(&cfTasks[TASK_BEEPER]); // TASK_PRIORITY_MEDIUM
|
queueAdd(&cfTasks[TASK_BEEPER]); // TASK_PRIORITY_MEDIUM
|
||||||
EXPECT_EQ(4, queueSize());
|
EXPECT_EQ(4, queueSize());
|
||||||
|
@ -120,6 +138,8 @@ TEST(SchedulerUnittest, TestQueue)
|
||||||
EXPECT_EQ(&cfTasks[TASK_SYSTEM], queueNext());
|
EXPECT_EQ(&cfTasks[TASK_SYSTEM], queueNext());
|
||||||
EXPECT_EQ(&cfTasks[TASK_BEEPER], queueNext());
|
EXPECT_EQ(&cfTasks[TASK_BEEPER], queueNext());
|
||||||
EXPECT_EQ(&cfTasks[TASK_SERIAL], queueNext());
|
EXPECT_EQ(&cfTasks[TASK_SERIAL], queueNext());
|
||||||
|
EXPECT_EQ(NULL, queueNext());
|
||||||
|
EXPECT_EQ(deadBeefPtr, taskQueueArray[TASK_COUNT + 1]);
|
||||||
|
|
||||||
queueAdd(&cfTasks[TASK_RX]); // TASK_PRIORITY_HIGH
|
queueAdd(&cfTasks[TASK_RX]); // TASK_PRIORITY_HIGH
|
||||||
EXPECT_EQ(5, queueSize());
|
EXPECT_EQ(5, queueSize());
|
||||||
|
@ -128,6 +148,8 @@ TEST(SchedulerUnittest, TestQueue)
|
||||||
EXPECT_EQ(&cfTasks[TASK_RX], queueNext());
|
EXPECT_EQ(&cfTasks[TASK_RX], queueNext());
|
||||||
EXPECT_EQ(&cfTasks[TASK_BEEPER], queueNext());
|
EXPECT_EQ(&cfTasks[TASK_BEEPER], queueNext());
|
||||||
EXPECT_EQ(&cfTasks[TASK_SERIAL], queueNext());
|
EXPECT_EQ(&cfTasks[TASK_SERIAL], queueNext());
|
||||||
|
EXPECT_EQ(NULL, queueNext());
|
||||||
|
EXPECT_EQ(deadBeefPtr, taskQueueArray[TASK_COUNT + 1]);
|
||||||
|
|
||||||
queueRemove(&cfTasks[TASK_SYSTEM]); // TASK_PRIORITY_HIGH
|
queueRemove(&cfTasks[TASK_SYSTEM]); // TASK_PRIORITY_HIGH
|
||||||
EXPECT_EQ(4, queueSize());
|
EXPECT_EQ(4, queueSize());
|
||||||
|
@ -135,12 +157,15 @@ TEST(SchedulerUnittest, TestQueue)
|
||||||
EXPECT_EQ(&cfTasks[TASK_RX], queueNext());
|
EXPECT_EQ(&cfTasks[TASK_RX], queueNext());
|
||||||
EXPECT_EQ(&cfTasks[TASK_BEEPER], queueNext());
|
EXPECT_EQ(&cfTasks[TASK_BEEPER], queueNext());
|
||||||
EXPECT_EQ(&cfTasks[TASK_SERIAL], queueNext());
|
EXPECT_EQ(&cfTasks[TASK_SERIAL], queueNext());
|
||||||
|
EXPECT_EQ(NULL, queueNext());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(SchedulerUnittest, TestQueueArray)
|
TEST(SchedulerUnittest, TestQueueArray)
|
||||||
{
|
{
|
||||||
// test there are no "out by one" errors or buffer overruns when items are added and removed
|
// test there are no "out by one" errors or buffer overruns when items are added and removed
|
||||||
queueClear();
|
queueClear();
|
||||||
|
taskQueueArray[TASK_COUNT + 1] = deadBeefPtr;
|
||||||
|
|
||||||
for (int taskId=0; taskId < TASK_COUNT - 1; ++taskId) {
|
for (int taskId=0; taskId < TASK_COUNT - 1; ++taskId) {
|
||||||
setTaskEnabled(static_cast<cfTaskId_e>(taskId), true);
|
setTaskEnabled(static_cast<cfTaskId_e>(taskId), true);
|
||||||
}
|
}
|
||||||
|
@ -149,13 +174,15 @@ TEST(SchedulerUnittest, TestQueueArray)
|
||||||
cfTask_t *lastTaskPrev = taskQueueArray[TASK_COUNT - 2];
|
cfTask_t *lastTaskPrev = taskQueueArray[TASK_COUNT - 2];
|
||||||
EXPECT_EQ(static_cast<cfTask_t*>(0), taskQueueArray[TASK_COUNT - 1]);
|
EXPECT_EQ(static_cast<cfTask_t*>(0), taskQueueArray[TASK_COUNT - 1]);
|
||||||
EXPECT_EQ(static_cast<cfTask_t*>(0), taskQueueArray[TASK_COUNT]);
|
EXPECT_EQ(static_cast<cfTask_t*>(0), taskQueueArray[TASK_COUNT]);
|
||||||
|
EXPECT_EQ(deadBeefPtr, taskQueueArray[TASK_COUNT + 1]);
|
||||||
|
|
||||||
setTaskEnabled(TASK_SYSTEM, false);
|
setTaskEnabled(TASK_SYSTEM, false);
|
||||||
EXPECT_EQ(TASK_COUNT - 2, queueSize());
|
EXPECT_EQ(TASK_COUNT - 2, queueSize());
|
||||||
EXPECT_EQ(lastTaskPrev, taskQueueArray[TASK_COUNT - 3]);
|
EXPECT_EQ(lastTaskPrev, taskQueueArray[TASK_COUNT - 3]);
|
||||||
EXPECT_EQ(lastTaskPrev, taskQueueArray[TASK_COUNT - 2]); // this won't have been moved
|
EXPECT_EQ(NULL, taskQueueArray[TASK_COUNT - 2]); // NULL at end of queue
|
||||||
EXPECT_EQ(static_cast<cfTask_t*>(0), taskQueueArray[TASK_COUNT - 1]);
|
EXPECT_EQ(static_cast<cfTask_t*>(0), taskQueueArray[TASK_COUNT - 1]);
|
||||||
EXPECT_EQ(static_cast<cfTask_t*>(0), taskQueueArray[TASK_COUNT]);
|
EXPECT_EQ(static_cast<cfTask_t*>(0), taskQueueArray[TASK_COUNT]);
|
||||||
|
EXPECT_EQ(deadBeefPtr, taskQueueArray[TASK_COUNT + 1]);
|
||||||
|
|
||||||
taskQueueArray[TASK_COUNT - 2] = 0;
|
taskQueueArray[TASK_COUNT - 2] = 0;
|
||||||
setTaskEnabled(TASK_SYSTEM, true);
|
setTaskEnabled(TASK_SYSTEM, true);
|
||||||
|
@ -163,23 +190,35 @@ TEST(SchedulerUnittest, TestQueueArray)
|
||||||
EXPECT_EQ(lastTaskPrev, taskQueueArray[TASK_COUNT - 2]);
|
EXPECT_EQ(lastTaskPrev, taskQueueArray[TASK_COUNT - 2]);
|
||||||
EXPECT_EQ(static_cast<cfTask_t*>(0), taskQueueArray[TASK_COUNT - 1]);
|
EXPECT_EQ(static_cast<cfTask_t*>(0), taskQueueArray[TASK_COUNT - 1]);
|
||||||
EXPECT_EQ(static_cast<cfTask_t*>(0), taskQueueArray[TASK_COUNT]);
|
EXPECT_EQ(static_cast<cfTask_t*>(0), taskQueueArray[TASK_COUNT]);
|
||||||
|
EXPECT_EQ(deadBeefPtr, taskQueueArray[TASK_COUNT + 1]);
|
||||||
|
|
||||||
// now there are TASK_COUNT items in the array
|
// now there are TASK_COUNT items in the array
|
||||||
setTaskEnabled(static_cast<cfTaskId_e>(TASK_COUNT - 1), true);
|
setTaskEnabled(static_cast<cfTaskId_e>(TASK_COUNT - 1), true);
|
||||||
EXPECT_EQ(TASK_COUNT, queueSize());
|
EXPECT_EQ(TASK_COUNT, queueSize());
|
||||||
EXPECT_EQ(lastTaskPrev, taskQueueArray[TASK_COUNT - 1]);
|
EXPECT_EQ(lastTaskPrev, taskQueueArray[TASK_COUNT - 1]);
|
||||||
EXPECT_EQ(static_cast<cfTask_t*>(0), taskQueueArray[TASK_COUNT]); // check no buffer overrun
|
EXPECT_EQ(static_cast<cfTask_t*>(0), taskQueueArray[TASK_COUNT]); // check no buffer overrun
|
||||||
|
EXPECT_EQ(deadBeefPtr, taskQueueArray[TASK_COUNT + 1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(SchedulerUnittest, TestInit)
|
TEST(SchedulerUnittest, TestSchedulerInit)
|
||||||
{
|
{
|
||||||
schedulerInit();
|
schedulerInit();
|
||||||
EXPECT_EQ(1, queueSize());
|
EXPECT_EQ(1, queueSize());
|
||||||
EXPECT_EQ(&cfTasks[TASK_SYSTEM], queueFirst());
|
EXPECT_EQ(&cfTasks[TASK_SYSTEM], queueFirst());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(SchedulerUnittest, TestScheduleEmptyQueue)
|
||||||
|
{
|
||||||
|
queueClear();
|
||||||
|
simulatedTime = 4000;
|
||||||
|
// run the with an empty queue
|
||||||
|
scheduler();
|
||||||
|
EXPECT_EQ(static_cast<cfTask_t*>(0), unittest_scheduler_selectedTask);
|
||||||
|
}
|
||||||
|
|
||||||
TEST(SchedulerUnittest, TestSingleTask)
|
TEST(SchedulerUnittest, TestSingleTask)
|
||||||
{
|
{
|
||||||
|
schedulerInit();
|
||||||
// disable all tasks except TASK_GYROPID
|
// disable all tasks except TASK_GYROPID
|
||||||
for (int taskId=0; taskId < TASK_COUNT; ++taskId) {
|
for (int taskId=0; taskId < TASK_COUNT; ++taskId) {
|
||||||
setTaskEnabled(static_cast<cfTaskId_e>(taskId), false);
|
setTaskEnabled(static_cast<cfTaskId_e>(taskId), false);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue