Tuesday, September 15, 2015

Task dependent synchronization: Sleep Task, Wake-up Task and Cancel Wake-up

Sleep Task

ER tslp_tsk(TMO tmout)
{

        Sanity check of context and arguments
        if (interrupt context or dispatch disabled state or Timer or overrun
         handlers)
                return E_CTX;

        Take Task ID of current running task
        Take TCB
        Enter critical section
        Take number of pending wakeups
        if (No pending wakeup) {
                if (Not polling) {
                        if (timeout is NOT FOREVER) {
                                add taks to timer queue;
                                make tcb status as SLEEP with TIMEOUT
                        } else /* forever timeout */
                                make tcb status just as SLEEP
                        }
                        remove task from Ready queue and dispatch
                        /* critical section is exit inside dispatch */
                        return error code from dispatch;
                }
                /* else, Polling */ {
                Exit critical section and return E_TMOUT; /* ?? Not needed */
                /* No polling service call is provided for slp_tsk- ITRON spec */
        }
        /* else, pending wakeups */
        Decrement the number of pending wake-ups by 1
        Exit critical section and return E_OK;
}

Wakeup Task

ER wup_tsk(ID tskid)
{
        if (tskid is TSK_SELF)
                Take taskid from Ready Queue[0]
        if (Task ID is greater than maximum of Task ID)
                return E_ID;
        Enter critical section
        Take TCB
        if (TCB == NULL)
                return E_NOEXS;
        Take status from TCB
        if (status is sleeping) {
                make the status as Ready
                if (Sleeping with TIMEOUT)
                        remove task from Timer queue
                if (Not suspended) {
                        add task to ready queue and dispatch
                        return;
                } /* Else, suspended */
                Exit critical section and just return E_OK;
        }
        /* Not sleeping */
        if (status is not Dormant) {
                increment pending wakeup count
                if (pending wakeup count does not exceed maximum)
                        exit critical section and return E_OK
                /* else, exceeded */
                assign wakeup count to the maximum
                exit critical section and return E_QOVR;
        }
        /* Dormant */
        exit critical section and return E_OBJ;
}

Cancel Wakeup Task

ER can_wup(ID tskid)
{
        if (tskid is TSK_SELF)
                Take taskid from Ready Queue[0]
        if (Task ID is greater than maximum of Task ID)
                return E_ID;
        Enter critical section
        Take TCB
        if (TCB == NULL)
                return E_NOEXS;
        else if (Task status is Dormat)
                return E_OBJ;
        else {
                Take backup of number of pending wakeup requests
                make pending wakeup counts as zero
        }
        Exit critical section
        return saved wake up counts;
}

Task Management: Get priority, Reference to Task State

Acquire Task Priority

ER get_pri(ID tskid, PRI *p_tskpri)
{
        if (tskid is TSK_SELF)
                Take taskid from Ready Queue[0]
        if (Task ID is greater than maximum of Task ID)
                return E_ID;
        if (called from ISR and specified task id as SELF)
                return E_ID;

        if (task id is currently running one) {
                assign p_tskpri = Running priority from TCB;
                return E_OK
        }

        /* For any other task */
        Enter critical section;
        Take TCB
        if (TCB == NULL)
                return E_NOEXS;
        if (Task status is Dormat)
                return E_OBJ;
        assign p_tskpri = Running priority from TCB;
        Exit critical section
        return E_OK;
}

Reference Task State

ER ref_tsk(ID tskid, T_RTSK *pk_rtsk)
{

        if (Task ID is greater than maximum of Task ID)
                return E_ID;
        Enter critical section;

        if (tskid is TSK_SELF)
                Take taskid from Ready Queue[0]
        Take TCB
        if (TCB == NULL)
                return E_NOEXS;

        Fill out following information from TCB
        pk_rtsk->tskpri
        pk_rtsk->tskbpri
        pk_rtsk->actcnt

        Take task status from TCB
        if (status is Dormant) {
                Exit critical section
                pk_rtsk->tskstat = TTS_DMT;
                Fill out all other fields with Zero.
                return E_OK;
        }

        Assign pk_rtsk->wupcnt from TCB;
        if (status is Ready) {
                if (Task is currently running) {
                        exit critical sections
                        Fill pk_rtsk->tskstat = TTS_RUN;
                        Fill suspension count as zero
                } else /* Not currently running */ {
                        Take suspension status from TCB
                        Exit critical section
                        Fill pk_rtsk->suscnt with the suspension count
                        If (task is not suspended)
                                Assign task state as TTS_RDY;
                        else
                                Assign task state as TTS_SUS;
                }
                /* Assign the following fields as zero */
                pk_rtsk->tskwait = 0;
                pk_rtsk->lefttmo = 0;
                pk_rtsk->wobjid = 0;
                return E_OK;
        }
        /* Status is not Ready */
        if (waiting in timer queue)
                assign pk_rtsk->lefttmo = TCB timeout - current system time;
        else
                assign pk_rtsk->lefttmo = TMO_FEVR;
        if (waiting in dlt_tsk call)
                pk_rtsk->wobjid = 0;
        else
                pk_rtsk->wobjid = Take the object id from the saved context;
        Take suspension status
        Exit critical section
        Assign pk_rtsk->suscnt with suspension count
        if (status is suspended)
                pk_rtsk->tskstat = WAITING state ;
        else
                pk_rtsk->tskstat = WAITING SUSPENDED state;
        Assign pk_rtsk->tskwait with WAITING Resource type
        return E_OK;
}

Simplified Reference Task State

ER ref_tst(ID tskid, T_RTST *pk_rtst)
{
        if (Task ID is greater than maximum of Task ID)
                return E_ID;
        Enter critical section;

        if (tskid is TSK_SELF)
                Take taskid from Ready Queue[0]
        Take TCB
        if (TCB == NULL)
                return E_NOEXS;

        Take task status from TCB
        if (status is Dormant) {
                Exit critical section
                pk_rtsk->tskstat = TTS_DMT;
                Fill out all other fields with Zero.
                return E_OK;
        }
        if (status is Ready) {
                if (Task is currently running) {
                        exit critical sections
                        Fill pk_rtsk->tskstat = TTS_RUN;
                } else /* Not currently running */ {
                        Take suspension status from TCB
                        Exit critical section
                        If (task is not suspended)
                                Assign task state as TTS_RDY;
                        else
                                Assign task state as TTS_SUS;
                }
                /* Assign the following fields as zero */
                pk_rtsk->tskwait = 0;
                return E_OK;
        }
        Take suspension status
        Exit critical section
        Assign pk_rtsk->suscnt with suspension count
        if (status is suspended)
                pk_rtsk->tskstat = WAITING state ;
        else
                pk_rtsk->tskstat = WAITING SUSPENDED state;
        Assign pk_rtsk->tskwait with WAITING Resource type
        return E_OK;
}

Timer Management: Timeout handler vs Cyclic Handler vs Alarm Handler


The Time management functions are called from idle task whenenver there is Timer interrupt. When there is timer interrupt, the priority of the Idle task (System task) is raised to the highest (0) and scheduling returns to the counting loop of the idle task. It skips the counting loop and disables the interupt to create the critical section and increments the timer count. And, it processes the timer tasks further.

Alarm handler and Cyclic handler are just executing handler at interrupt enabled context. Cyclic handler is cyclic. Alarm is just once. But, other timeout handler is not handler. Instead, waking up tasks sleeping at various resources and system calls with various reasons.

But, why two timeout redundant values? 1) Timeout queue 2) TCB timeout

System Dispatch Service

ER System Dispatch Functions(void)
{
        If (there is system error)
                return system error;
        Enable dispatch;
        Initializes internal strucutres;
        /* Enter endless loop */
        for (;;) {
                This task is assigned lowest priority in the system.
                Maximum priority + 1;
                If (Dispatch is disabled)
                        Special Dispatch task = Previous running task
                else
                        No special request
                Schedule and dispatch;

                if (overrun handler conndition )
                        Call Overrun handler

                /* counter loop */
                for (;;) {
                        count down and stay here if reached zero;
                        Poll for Timer interrup flag;
                        if (timer interrrupt flag is set )
                                break;
                }
                Get idle count;
                Enter critical section;/* diable interrupt */
                for (till number of pending timer interrupts reaches zero)
                        Update system clock;
                        check timer queue - Timeout check processing;
                        check cyclic handler management
                        check alarm handler management
                }
        }
}

Timeout check processing

void timeout_check_processing(void)
{

        Take queue corresponding to lower 32 bit of system time

        for (till there is valid entry in the head of the queue) {
                Take taskid in the head of the queue
                if (task id is zero)
                        return;
                Take TCB
                for (till an entry matching the timeout value is reached) {
                        if (matching) { /* ?? I think this will match always */
                                Remove the task from the timer queue;
                                Take backup of the task status
                                Assign the task status as READY
                                Assign the return code based on previous status
                                if (in Timer queue due to dly_tsk(DLY)) {
                                        Assign return code as E_OK
                                } else {
                                Assign return code as E_TIMEOUT
                                if (waiting for any resource) {
                                        delete from the resource's waiting queue
                                        if (resource is Mutex) {
                                                Get the Mutex ID
                                                if (Mutex is INHERITENCE and
                                                  priority is higher or equal
                                                        ceiling priority)
                                                        Adjust Mutex priority
                                        } else if (resource is Message buffer)
                                                adjust resource allocation
                                        } else if (resource is variable m-pool)
                                                adjust resource allocation
                                        }
                                }
                                if (not suspended) {
                                        Take priority
                                        if (priority is higher than current Application priority)
                                                Add to delayed Dispatch
                                                /* As this function won't call dispatch */
                                        Add to Ready queue of the task's priority
                                }
                                temporarily open and close critical
                                        section to open up the door for dispatch
                                break; /* Again go to head for check */
                                        /* Anything might have happened when you opened up the door */
                        }
                        if (reached end of queue)
                                return;
                        move to next task in the queue chain
                }
        }
}

Cyclic Handler Processing

void Cyclic_handler_processing(void)
{

        Take the cyclic handler queue corresponding to the system time low 32 bit
        for (till valid entry in the head of the queue) {
                Take the cyclic handler ID at the queue head
                if (cyclic handler ID is 0)
                        return;
                for (till an entry which matches with time stamp) {
                        if (deadline matches with time stamp) {
                                delete from cyclic handler queue
                                calculate new target time as it is cyclic handler
                                add to the new queue of new target time
                                temporarily enable interrupt
                                if (cyclic hander is ON now) {
                                        Execute cyclic handler
                                }
                                disable interrupt again
                                break; /* Restart from head again */
                                /* anything is possible in the cycle gap */
                        }
                        if (reached end of queue)
                                return;
                        move to next cyclic handler in the same queue
                }
        }
}

Alarm Handler Processing

void alarm_handler_processing(void)
{
        Take alarm handler queue corresponding to the system time low 32 bit
        for (till valid entry in the head of the queue) {
                for (till an entry which matches with time stamp) {
                        if (deadline matches with time stamp) {
                                delete from alarm handler queue
                                reset the alarm handler entry fields
                                temporarily enable interrupt
                                Execute alarm handler
                                disable interrupt again
                                break; /* Restart from head again */
                                /* anything is possible in the cycle gap */
                        }
                        if (reached end of queue)
                                return;
                        move to next alarm handler in the same queue
                }
        }
}

Accumulating or queuing inside system processing is not real-time. Realtime never accumulates in production center. Always, wherever there is a change in the system state, it opens the gate and put the car on the road. It is like Toyota. Never keep the doors closed.
Whether the car runs on the road or stands depends on the user program. But, kernel will pass the message always at real time.

Friday, September 04, 2015

Task Management: Change Task priority

① Change Task's base priority

There three kinds of priorities.

1) Initial priority - Assigned when Task is created through Task creation structure. Once taks is created, this will be base priority.
2) Base priority - Priority changed on runtime through chg_pri
3) Running priority - Unless otherwise Mutexes are locked, this will be same as base priority. But, when Mutexes are locked, running priority is changed according to priority inheritence algorithm.

When priority is changed, all operations which are based on priority needs to be realigned. Whether it is get_mpl, snd_mbf or waiting for mutex or locked mutex. Please note that the task needs to yield, if the current running priotiy is same as of it.

② uITRON Implementation of chg_pri()

ER chg_pri(ID tskid, PRI tskpri)
{
        if (TSK_SELF is specified as Task ID)
                Set tskid as ReadyQueue[0]
        Enter critical section
        Sanity check on arguments
        /* Dormant task, Timer tasks(idle tasks) cannot be target */
        /* ISR cannot specify TSK_SELF */
        Take TCB of target task ID
        if (target priority == TPRI_INI)
                take task priority from initialization structure
        yesmtx = check if target task has locked mutex or waiting for mutex

        /* Just changing the Base priority; Not running priority */
        /* When mutex is locked/waited, but target pri is lower than running pri */
        if (yesmtx) {
                if (target priority is higher than running priority) {
                        /* Whether the target pri is valid or not? */
                        for (all mutexes locked by target task) {
                                if (CEILING priority &&
                                        target pri is higher than ceiling pri) {
                                        return E_ILUSE;
                                }
                                MaxLocked = Get Maximum of Inherited&Ceiling pri of locked mutexes
                        }
                        /* Is the running not equal to Maximum of locked mutex priorites ????. So, I guess following will be false forever */
                        if (Target pri is lower than MaxLocked)
                                goto Just_exit
                }
                else (target pri is lower than running pri) {
Just_exit:              I cannot change running pri as mutex make running pri as higher
                        So, I just change base priority and leaving now.
                        Mutex functions will adjust the running further based on the base pri.
                }
        }

        /* Changing the Running priority too */
        Take back up of target task's current running pri;
        Assign target priority to both base as well as running priority
        If (Task is ReadyToRun and not suspended) {
                move the position of Task in the ReadyQueue
                if (task is running task and target pri is lesser/equal
                                 priority of current user task priority) {
                        OR
                (task is not Running and target pri is higher/equal priority of
                                current user task priority) {
                        /* if you are equal, I will dispatch */
                        /* if you are waiting and higher, then dispatch */
                        /* if you are running and become lower, then dispatch */
                        return dispatch();
                }
                /* I am still running after changing the priority */
                /* then update current running priority and current application priority */
                current running priority = target priority;
                if (current running priority != 0) { /* Reverse is not possible as system task cannot be target ??? */
                        current application priority = current running priority;
                }
                return;
        }

        /* No, the target guy is waiting for something */
        if (waiting for Resource) {
                Adjust the position in the queue if it is priority based queue
                (Not FIFO)
                if (waiting for Mutex) {
                        get the Mutex ID waiting for
                        if (Mutex is INHERITENCE based) {
                                Mutex priority should be equal to Top priority
                                task in the waiting queue
                                if (this is not true) /* above position adjustment caused this */
                                        realign the task priority of holding the mutex
                        }
                }
        } else if (waiting for Message buffer) {
                Check the change in position caused any advantage
                if (caused advantage)
                        return dispatch;
        } else if (waiting for Memory Pool) {
                Check the change in position caused any advantage
                if (caused advantage)
                        return dispatch;
        }
        exit critical section;
        return;
}

Changing priority - Adjusting the foundation..