Summarize how the system clock in vxWorks modifies the time slice of delayed tasks

Keywords: less

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.
Q_HEAD	  tickQHead;
The structure definition of tickQHead queue:
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:
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;
This is the interrupt function of the system clock, in which time slices are reduced.

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;	\
	}
typedef struct		
    {
    DL_NODE	node;	/*Nodes that make up queues*/
    ULONG	key;	/*Task Delay Time*/
    } Q_PRI_NODE;
Separate description:

(((Q_HEAD *)pQHead)->pQClass->valid == qPriDeltaClassId &&	\
	((Q_HEAD *)pQHead)->pFirstNode != NULL)
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.

if (n > ((Q_PRI_NODE *)((Q_HEAD *)pQHead)->pFirstNode)->key)	\
	    ((Q_PRI_NODE *)((Q_HEAD *)pQHead)->pFirstNode)->key = 0;	\
When 1 is greater than the key of the first node (delay time), it is assigned a value of 0, otherwise minus 1.


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.









Posted by alexcrosson on Sat, 08 Jun 2019 11:57:59 -0700