mirror of
https://github.com/espressif/openthread.git
synced 2026-08-05 02:17:47 +00:00
[tasklet] fix Unpost() behavior during tasklet processing (#13039)
This commit fixes an issue where a tasklet could not be successfully unposted if it was already scheduled for execution in the current event loop iteration. Previously, `Scheduler::ProcessQueuedTasklets()` copied and cleared the queued tasklets before running them. If a running tasklet called `Unpost()` on another tasklet that was also in the copied list, the unpost operation would fail to remove it because it only checked the main queue. To address this, the `Scheduler` now explicitly maintains two separate queues: `mPostedQueue` and `mRuningQueue`. The `Tasklet::Unpost()` method is updated to remove the target tasklet from both queues, ensuring it is correctly dequeued even if it is pending in the running list. The queue logic is encapsulated into a nested `Queue` class to manage the circular singly linked-list operations cleanly. Additionally, unit tests are expanded to cover scenarios where tasklets post or unpost other tasklets during execution.
This commit is contained in:
@@ -38,9 +38,12 @@ namespace ot {
|
||||
#define Log(aMessage) fprintf(stderr, aMessage "\n\r")
|
||||
|
||||
static Instance *sInstance = nullptr;
|
||||
static Tasklet *sTask1 = nullptr;
|
||||
static bool sTask1Handled = false;
|
||||
static bool sTask2Handled = false;
|
||||
static bool sTask3Handled = false;
|
||||
static bool sTask4Handled = false;
|
||||
static bool sTask5Handled = false;
|
||||
static bool sSignalPendingCalled = false;
|
||||
static bool sShouldTask3RepostItself = false;
|
||||
|
||||
@@ -61,6 +64,8 @@ void ResetTestFlags(void)
|
||||
sTask1Handled = false;
|
||||
sTask2Handled = false;
|
||||
sTask3Handled = false;
|
||||
sTask4Handled = false;
|
||||
sTask5Handled = false;
|
||||
sSignalPendingCalled = false;
|
||||
}
|
||||
|
||||
@@ -99,6 +104,26 @@ void HandleTask3(Tasklet &aTasklet)
|
||||
}
|
||||
}
|
||||
|
||||
void HandleTask4(Tasklet &aTasklet)
|
||||
{
|
||||
Log(" HandleTask4() - will post task1");
|
||||
CheckTaskeltFromHandler(aTasklet);
|
||||
VerifyOrQuit(!sTask4Handled);
|
||||
sTask4Handled = true;
|
||||
|
||||
sTask1->Post();
|
||||
}
|
||||
|
||||
void HandleTask5(Tasklet &aTasklet)
|
||||
{
|
||||
Log(" HandleTask5() - will un-post task1");
|
||||
CheckTaskeltFromHandler(aTasklet);
|
||||
VerifyOrQuit(!sTask5Handled);
|
||||
sTask5Handled = true;
|
||||
|
||||
sTask1->Unpost();
|
||||
}
|
||||
|
||||
void TestTasklet(void)
|
||||
{
|
||||
Log("TestTasklet");
|
||||
@@ -111,6 +136,10 @@ void TestTasklet(void)
|
||||
Tasklet task1(*sInstance, HandleTask1);
|
||||
Tasklet task2(*sInstance, HandleTask2);
|
||||
Tasklet task3(*sInstance, HandleTask3);
|
||||
Tasklet task4(*sInstance, HandleTask4);
|
||||
Tasklet task5(*sInstance, HandleTask5);
|
||||
|
||||
sTask1 = &task1;
|
||||
|
||||
Log("Process all initially posted tasks after `Instance` initialization");
|
||||
|
||||
@@ -563,6 +592,214 @@ void TestTasklet(void)
|
||||
|
||||
VerifyOrQuit(!sSignalPendingCalled);
|
||||
VerifyOrQuit(!scheduler.AreTaskletsPending());
|
||||
|
||||
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
Log("Post task4 (which posts task1 from its handler) and then task1");
|
||||
Log("We expect the posting of task1 (from task4 handler) to be ignored since it is already posted");
|
||||
|
||||
ResetTestFlags();
|
||||
|
||||
task4.Post();
|
||||
task1.Post();
|
||||
|
||||
VerifyOrQuit(task4.IsPosted());
|
||||
VerifyOrQuit(task1.IsPosted());
|
||||
|
||||
VerifyOrQuit(sSignalPendingCalled);
|
||||
sSignalPendingCalled = false;
|
||||
|
||||
scheduler.ProcessQueuedTasklets();
|
||||
|
||||
VerifyOrQuit(sTask4Handled);
|
||||
VerifyOrQuit(sTask1Handled);
|
||||
|
||||
VerifyOrQuit(!task4.IsPosted());
|
||||
VerifyOrQuit(!task1.IsPosted());
|
||||
|
||||
VerifyOrQuit(!sSignalPendingCalled);
|
||||
VerifyOrQuit(!scheduler.AreTaskletsPending());
|
||||
|
||||
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
Log("Post task1 first and then task4 (which posts task1 from its handler) now");
|
||||
Log("Since task1 is first, it should be handled before task4's handler and task4's handler should post task1");
|
||||
|
||||
ResetTestFlags();
|
||||
|
||||
task1.Post();
|
||||
task4.Post();
|
||||
|
||||
VerifyOrQuit(task4.IsPosted());
|
||||
VerifyOrQuit(task1.IsPosted());
|
||||
|
||||
VerifyOrQuit(sSignalPendingCalled);
|
||||
sSignalPendingCalled = false;
|
||||
|
||||
scheduler.ProcessQueuedTasklets();
|
||||
|
||||
VerifyOrQuit(sTask4Handled);
|
||||
VerifyOrQuit(sTask1Handled);
|
||||
|
||||
VerifyOrQuit(!task4.IsPosted());
|
||||
VerifyOrQuit(task1.IsPosted());
|
||||
|
||||
VerifyOrQuit(sSignalPendingCalled);
|
||||
VerifyOrQuit(scheduler.AreTaskletsPending());
|
||||
|
||||
ResetTestFlags();
|
||||
|
||||
scheduler.ProcessQueuedTasklets();
|
||||
VerifyOrQuit(!sTask4Handled);
|
||||
VerifyOrQuit(sTask1Handled);
|
||||
|
||||
VerifyOrQuit(!task4.IsPosted());
|
||||
VerifyOrQuit(!task1.IsPosted());
|
||||
|
||||
VerifyOrQuit(!sSignalPendingCalled);
|
||||
VerifyOrQuit(!scheduler.AreTaskletsPending());
|
||||
|
||||
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
Log("Post task5 (which un-posts task1 from its handler) and then task1");
|
||||
Log("Since task5 is first, we expect task1 to be removed and its handler never called");
|
||||
|
||||
ResetTestFlags();
|
||||
|
||||
task5.Post();
|
||||
task1.Post();
|
||||
|
||||
VerifyOrQuit(task5.IsPosted());
|
||||
VerifyOrQuit(task1.IsPosted());
|
||||
|
||||
VerifyOrQuit(sSignalPendingCalled);
|
||||
sSignalPendingCalled = false;
|
||||
|
||||
scheduler.ProcessQueuedTasklets();
|
||||
|
||||
VerifyOrQuit(sTask5Handled);
|
||||
VerifyOrQuit(!sTask1Handled);
|
||||
|
||||
VerifyOrQuit(!task5.IsPosted());
|
||||
VerifyOrQuit(!task1.IsPosted());
|
||||
|
||||
VerifyOrQuit(!sSignalPendingCalled);
|
||||
VerifyOrQuit(!scheduler.AreTaskletsPending());
|
||||
|
||||
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
Log("Post task1 first, then task5 (which un-posts task1 from its handler)");
|
||||
Log("Both tasks should be handled");
|
||||
|
||||
ResetTestFlags();
|
||||
|
||||
task1.Post();
|
||||
task5.Post();
|
||||
|
||||
VerifyOrQuit(task1.IsPosted());
|
||||
VerifyOrQuit(task5.IsPosted());
|
||||
|
||||
VerifyOrQuit(sSignalPendingCalled);
|
||||
sSignalPendingCalled = false;
|
||||
|
||||
scheduler.ProcessQueuedTasklets();
|
||||
|
||||
VerifyOrQuit(sTask1Handled);
|
||||
VerifyOrQuit(sTask5Handled);
|
||||
|
||||
VerifyOrQuit(!task5.IsPosted());
|
||||
VerifyOrQuit(!task1.IsPosted());
|
||||
|
||||
VerifyOrQuit(!sSignalPendingCalled);
|
||||
VerifyOrQuit(!scheduler.AreTaskletsPending());
|
||||
|
||||
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
Log("Post task5 on its own, which un-posts task1 from its handler - it should do nothing to task1");
|
||||
|
||||
ResetTestFlags();
|
||||
|
||||
task5.Post();
|
||||
|
||||
VerifyOrQuit(task5.IsPosted());
|
||||
|
||||
VerifyOrQuit(sSignalPendingCalled);
|
||||
sSignalPendingCalled = false;
|
||||
|
||||
scheduler.ProcessQueuedTasklets();
|
||||
|
||||
VerifyOrQuit(sTask5Handled);
|
||||
|
||||
VerifyOrQuit(!task5.IsPosted());
|
||||
VerifyOrQuit(!task1.IsPosted());
|
||||
|
||||
VerifyOrQuit(!sSignalPendingCalled);
|
||||
VerifyOrQuit(!scheduler.AreTaskletsPending());
|
||||
|
||||
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
Log("Post task4 (which posts task1 from its handler), then task5 (which un-posts task1 from its handler)");
|
||||
Log("The two should cancel each other and at the end task1 should not be posted");
|
||||
|
||||
ResetTestFlags();
|
||||
|
||||
task4.Post();
|
||||
task5.Post();
|
||||
|
||||
VerifyOrQuit(task4.IsPosted());
|
||||
VerifyOrQuit(task5.IsPosted());
|
||||
VerifyOrQuit(!task1.IsPosted());
|
||||
|
||||
VerifyOrQuit(sSignalPendingCalled);
|
||||
sSignalPendingCalled = false;
|
||||
|
||||
scheduler.ProcessQueuedTasklets();
|
||||
|
||||
VerifyOrQuit(sTask4Handled);
|
||||
VerifyOrQuit(sTask5Handled);
|
||||
VerifyOrQuit(!sTask1Handled);
|
||||
|
||||
VerifyOrQuit(!task5.IsPosted());
|
||||
VerifyOrQuit(!task4.IsPosted());
|
||||
VerifyOrQuit(!task1.IsPosted());
|
||||
|
||||
VerifyOrQuit(sSignalPendingCalled);
|
||||
VerifyOrQuit(!scheduler.AreTaskletsPending());
|
||||
|
||||
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
Log("Post task5 (which un-posts task1 from its handler), then task1, and then task4 (which posts task1)");
|
||||
Log("The task1 should be removed while processing task5, but then posted again from task4");
|
||||
|
||||
ResetTestFlags();
|
||||
|
||||
task5.Post();
|
||||
task1.Post();
|
||||
task4.Post();
|
||||
|
||||
VerifyOrQuit(task4.IsPosted());
|
||||
VerifyOrQuit(task5.IsPosted());
|
||||
VerifyOrQuit(task1.IsPosted());
|
||||
|
||||
VerifyOrQuit(sSignalPendingCalled);
|
||||
sSignalPendingCalled = false;
|
||||
|
||||
scheduler.ProcessQueuedTasklets();
|
||||
|
||||
VerifyOrQuit(sTask5Handled);
|
||||
VerifyOrQuit(sTask4Handled);
|
||||
VerifyOrQuit(!sTask1Handled);
|
||||
|
||||
VerifyOrQuit(!task5.IsPosted());
|
||||
VerifyOrQuit(!task4.IsPosted());
|
||||
VerifyOrQuit(task1.IsPosted());
|
||||
|
||||
VerifyOrQuit(sSignalPendingCalled);
|
||||
VerifyOrQuit(scheduler.AreTaskletsPending());
|
||||
|
||||
// Handle the posted task1
|
||||
ResetTestFlags();
|
||||
|
||||
scheduler.ProcessQueuedTasklets();
|
||||
VerifyOrQuit(sTask1Handled);
|
||||
|
||||
VerifyOrQuit(!task1.IsPosted());
|
||||
|
||||
VerifyOrQuit(!sSignalPendingCalled);
|
||||
VerifyOrQuit(!scheduler.AreTaskletsPending());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user