Function Call Relationships: (Clockless Function Calls for Delayed Tasks)
usrClock-->tickAnnounce-->windTickAnnounce-->Q_ADVANCEN
Let's start with the reduction in waiting time.
A global queue in the system is defined as a waiting task.
The structure definition of tickQHead queue:Q_HEAD tickQHead;
typedef struct { Q_NODE *pFirstNode; /*Node of the first task*/ UINT qPriv1; /**/ UINT qPriv2; /**/ Q_CLASS *pQClass; /*Each class corresponds to a set of operation functions.*/ } Q_HEAD;
Operational function set corresponding to each class:
This is the interrupt function of the system clock, in which time slices are reduced.typedef struct q_class /* Q_CLASS */ { FUNCPTR createRtn; /* create and initialize a queue */ FUNCPTR initRtn; /* initialize a queue */ FUNCPTR deleteRtn; /* delete and terminate a queue */ FUNCPTR terminateRtn; /* terminate a queue */ FUNCPTR putRtn; /* insert a node into q with insertion key */ FUNCPTR getRtn; /* return and remove lead node routine */ FUNCPTR removeRtn; /* remove routine */ FUNCPTR resortRtn; /* resort node to new priority */ FUNCPTR advanceRtn; /* advance queue by one tick routine */ FUNCPTR getExpiredRtn; /* return and remove an expired Q_NODE */ FUNCPTR keyRtn; /* return insertion key of node */ FUNCPTR calibrateRtn; /* calibrate every node in queue by an offset */ FUNCPTR infoRtn; /* return array of nodes in queue */ FUNCPTR eachRtn; /* call a user routine for each node in queue */ FUNCPTR restoreRtn; /* restore a node position in queue */ FUNCPTR nextRtn; /* return next node in a queue */ struct q_class *valid; /* valid == pointer to queue class */ } Q_CLASS;
void windTickAnnounce (void) { .... Q_ADVANCEN (&tickQHead, 1); .... }
Reduce the time slice of tasks on the queue.
#define Q_ADVANCEN(pQHead, n) \ if (((Q_HEAD *)pQHead)->pQClass->valid == qPriDeltaClassId && \ ((Q_HEAD *)pQHead)->pFirstNode != NULL) \ { \ if (n > ((Q_PRI_NODE *)((Q_HEAD *)pQHead)->pFirstNode)->key) \ ((Q_PRI_NODE *)((Q_HEAD *)pQHead)->pFirstNode)->key = 0; \ else \ ((Q_PRI_NODE *)((Q_HEAD *)pQHead)->pFirstNode)->key -= n; \ }
Separate description:typedef struct { DL_NODE node; /*Nodes that make up queues*/ ULONG key; /*Task Delay Time*/ } Q_PRI_NODE;
In this sentence, first determine whether the set of tickQHead's operation functions is qPriDeltaClassId, then determine whether the first node is empty, if both are true, and then proceed down.(((Q_HEAD *)pQHead)->pQClass->valid == qPriDeltaClassId && \ ((Q_HEAD *)pQHead)->pFirstNode != NULL)
When 1 is greater than the key of the first node (delay time), it is assigned a value of 0, otherwise minus 1.if (n > ((Q_PRI_NODE *)((Q_HEAD *)pQHead)->pFirstNode)->key) \ ((Q_PRI_NODE *)((Q_HEAD *)pQHead)->pFirstNode)->key = 0; \
A problem arises here. Every time the operation time slice is taken, the system only operates the time slice value of the first node. The time slice of the later task is not moved. What's the matter?
As the problem side explains later, let's first look at the operation of putting tasks on a waiting queue:
Operational functions that put tasks in queues: void qPriDeltaPut ( Q_PRI_HEAD *pQPriHead, /*The waiting queue to put in*/ Q_PRI_NODE *pQPriNode, /*Task Node to Operate*/ ULONG key /*Delay Time*/ ) { /*Get the first node of the queue*/ FAST Q_PRI_NODE *pQNode = (Q_PRI_NODE *) DLL_FIRST (pQPriHead); pQPriNode->key = key; /*Assign waiting time for tasks to be operated on*/ /*Determine whether it is an empty queue, and if it is an empty queue, put the task to the end.*/ while (pQNode != NULL) { /*If the delay value of inserting task is less than the time of pQNode, insert it in front of pQNode and reduce the time of inserting task; if the delay time of pQNode is longer than that of pQNode, subtract the time of pQNode from the time of pQNode and insert h into the back of pQNode.*/ if (pQPriNode->key < pQNode->key) { dllInsert (pQPriHead, DLL_PREVIOUS (&pQNode->node), &pQPriNode->node); /* Now decrement the delta key of the guy behind us. */ pQNode->key -= pQPriNode->key; return; /* we're done */ } pQPriNode->key -= pQNode->key; pQNode = (Q_PRI_NODE *) DLL_NEXT (&pQNode->node); } dllInsert (pQPriHead, (DL_NODE *) DLL_LAST (pQPriHead), &pQPriNode->node); }
Let's give an example to answer this question:
There are three tasks that are delayed to execute after themselves. A delayed 15 seconds, B delayed 16 seconds, C delayed 17 seconds. It is clear that the three tasks are inserted into the queue as follows:
First: A delay of 15 seconds
Second: B delays 1 second.
Third: C delay of 2 seconds.
At this time, the values of B and D are the difference between A and A respectively. Then when the clock runs to reduce the delay time, only the first one is reduced at a time. When A arrives at the time, B becomes the first one. This B has been delayed for 15 seconds, plus the preparation delay time of 1 second, which is exactly the set delay of 16 seconds, the same as the C task.
What are the benefits of this?
No matter how many tasks there are, just need to operate the first, it is really a lot of convenience.