From 335b305ba8fbb93311a46915fb554f307612d2b6 Mon Sep 17 00:00:00 2001 From: Abtin Keshavarzian Date: Fri, 8 Nov 2019 16:17:01 -0800 Subject: [PATCH] [tasklet] use circular linked list (#4309) This commit contains the following changes: - Changes `TaskletScheduler` to use a circular linked list. - Adds `Tasklet::IsPosted()` which indicates whether a tasklet is posted or not (by checking `mNext` pointer against NULL). - Simplifies processing of tasklets from `ProcessQueuedTasklets()` by creating a copy of current list and clearing the main list (this way, a newly posted tasklet would automatically trigger a call to `otTaskletsSignalPending()`). --- src/core/common/tasklet.cpp | 88 ++++++++++++++++--------------------- src/core/common/tasklet.hpp | 31 ++++++------- 2 files changed, 54 insertions(+), 65 deletions(-) diff --git a/src/core/common/tasklet.cpp b/src/core/common/tasklet.cpp index abe607e4c..3481af94d 100644 --- a/src/core/common/tasklet.cpp +++ b/src/core/common/tasklet.cpp @@ -50,78 +50,66 @@ Tasklet::Tasklet(Instance &aInstance, Handler aHandler, void *aOwner) } otError Tasklet::Post(void) -{ - return Get().Post(*this); -} - -TaskletScheduler::TaskletScheduler(void) - : mHead(NULL) - , mTail(NULL) -{ -} - -otError TaskletScheduler::Post(Tasklet &aTasklet) { otError error = OT_ERROR_NONE; - VerifyOrExit(mTail != &aTasklet && aTasklet.mNext == NULL, error = OT_ERROR_ALREADY); - - VerifyOrExit(&aTasklet.Get() == this); - - if (mTail == NULL) - { - mHead = &aTasklet; - mTail = &aTasklet; - otTaskletsSignalPending(&aTasklet.GetInstance()); - } - else - { - mTail->mNext = &aTasklet; - mTail = &aTasklet; - } + VerifyOrExit(!IsPosted(), error = OT_ERROR_ALREADY); + Get().PostTasklet(*this); exit: return error; } -Tasklet *TaskletScheduler::PopTasklet(void) +TaskletScheduler::TaskletScheduler(void) + : mTail(NULL) { - Tasklet *task = mHead; +} - if (task != NULL) +void TaskletScheduler::PostTasklet(Tasklet &aTasklet) +{ + // Tasklets are saved in a circular singly linked list. + + if (mTail == NULL) { - mHead = mHead->mNext; - - if (mHead == NULL) - { - mTail = NULL; - } - - task->mNext = NULL; + mTail = &aTasklet; + mTail->mNext = mTail; + otTaskletsSignalPending(&aTasklet.GetInstance()); + } + else + { + aTasklet.mNext = mTail->mNext; + mTail->mNext = &aTasklet; + mTail = &aTasklet; } - - return task; } void TaskletScheduler::ProcessQueuedTasklets(void) { Tasklet *tail = mTail; - Tasklet *cur; - while ((cur = PopTasklet()) != NULL) + // This method processes all tasklets queued when this is called. We + // keep a copy the current list and then clear the main list by + // setting `mTail` to NULL. A newly posted tasklet while processing + // the currently queued tasklets will then trigger a call to + // `otTaskletsSignalPending()`. + + mTail = NULL; + + while (tail != NULL) { - cur->RunTask(); + Tasklet *tasklet = tail->mNext; - // only process tasklets that were queued at the time this method was called - if (cur == tail) + if (tasklet == tail) { - if (mHead != NULL) - { - otTaskletsSignalPending(&mHead->GetInstance()); - } - - break; + tail = NULL; } + else + { + tail->mNext = tasklet->mNext; + } + + tasklet->mNext = NULL; + tasklet->RunTask(); } } diff --git a/src/core/common/tasklet.hpp b/src/core/common/tasklet.hpp index c98dbfda0..f8fb8dd1e 100644 --- a/src/core/common/tasklet.hpp +++ b/src/core/common/tasklet.hpp @@ -84,11 +84,20 @@ public: Tasklet(Instance &aInstance, Handler aHandler, void *aOwner); /** - * This method puts the tasklet on the run queue. + * This method puts the tasklet on the tasklet scheduler run queue. * */ otError Post(void); + /** + * This method indicates whether the tasklet is posted or not. + * + * @retval TRUE The tasklet is posted. + * @retval FALSE The tasklet is not posted. + * + */ + bool IsPosted(void) const { return (mNext != NULL); } + private: void RunTask(void) { mHandler(*this); } @@ -140,6 +149,8 @@ private: */ class TaskletScheduler { + friend class Tasklet; + public: /** * This constructor initializes the object. @@ -147,16 +158,6 @@ public: */ TaskletScheduler(void); - /** - * This method enqueues a tasklet into the run queue. - * - * @param[in] aTasklet A reference to the tasklet to enqueue. - * - * @retval OT_ERROR_NONE Successfully enqueued the tasklet. - * @retval OT_ERROR_ALREADY The tasklet was already enqueued. - */ - otError Post(Tasklet &aTasklet); - /** * This method indicates whether or not there are tasklets pending. * @@ -164,7 +165,7 @@ public: * @retval FALSE If there are no tasklets pending. * */ - bool AreTaskletsPending(void) const { return mHead != NULL; } + bool AreTaskletsPending(void) const { return mTail != NULL; } /** * This method processes all tasklets queued when this is called. @@ -173,9 +174,9 @@ public: void ProcessQueuedTasklets(void); private: - Tasklet *PopTasklet(void); - Tasklet *mHead; - Tasklet *mTail; + void PostTasklet(Tasklet &aTasklet); + + Tasklet *mTail; // A circular singly linked-list }; /**