rtel wrote on Sunday, March 13, 2005:
> If the scheduler is suspended when the ISR is invoked:
> 1. The call to taskYIELD() at the end of the ISR saves the context and calls
> vTaskSwitchContext(). This sees that the scheduler is suspended so returns to
> taskYIELD() without a task switch.
This is correct.
A good optimisation might be for cSemaphoreGiveFromISR() not to return true if the scheduler is suspended. This would prevent the save/restore of the context and could be achieved with the following modification:
Look for the function cQueueSendFromISR() in queue.c. In this function there is one place where pdTRUE can be returned. Change this to:
if( ucSchedulerSuspended == pdFALSE )
return pdTRUE;
else
return pdFALSE;
Two points to note:
1) You would have to make ucSchedulerSuspended accessible. Currently it is declared static in tasks.c.
2) I have not tested this!
I will have a look at this possible optimisation, and if satisfactory include it in the V3.0.0 release.
A similar change could be made for the ISR queue receive function.
> 2. When the ISR returns, and cTaskResumeAll() executes, the task placed in the
> xPendingReadyList by the call to cSemaphoreGiveFromISR gets moved to the ready
> list. However, there is not a call to taskYIELD during cTaskResumeAll(), unless
> one or more ticks were lost during the schedulers suspension. The result is
> that the context switch has to wait for the next tick.
If a task is unblocked while the scheduler is locked it is placed in the pending read list (as you say) and the context switch does not occur. A context switch cannot occur when the scheduler is suspended.
Here is another optimisation that will make the kernel behave how you want:
Go to the function cTaskResumeAll() in tasks.c. Find the line:
if( usCurrentNumberOfTasks > ( unsigned portSHORT ) 0 )
Within this if statement there is a while loop that takes tasks from the pending list and places them in the ready list. You will then see an if() statement that catches up with any ticks that occurred while the scheduler was suspended. If any ticks were missed then a YIELD() occurs - but what you want if for a YIELD() to occur if either a tick was missed OR a task was removed from the pending list. This could be done thus:
if( usCurrentNumberOfTasks > ( unsigned portSHORT ) 0 )
{
____portCHAR cYieldRequired = pdFALSE; // <<<<< new variable.
____/* Move any readied tasks from the pending list into the
____appropriate ready list. */
____while( ( pxTCB = ( tskTCB * ) listGET_OWNER_OF_HEAD_ENTRY( ( ( xList * ) &xPendingReadyList ) ) ) != NULL )
____{
________vListRemove( &( pxTCB->xEventListItem ) );
________vListRemove( &( pxTCB->xGenericListItem ) );
________prvAddTaskToReadyQueue( pxTCB );
________// <<<< Added if statement.
________if( pxTCB->ucPriority > pxCurrentTCB->ucPriority )
________{
____________cYieldRequired = pdTRUE
________}
____}
____/* If any ticks occurred while the scheduler was suspended then
____they should be processed now. This ensures the tick count does not
____slip, and that any delayed tasks are resumed at the correct time. */
____if( ucMissedTicks > 0 )
____{
________cYieldRequired = pdTRUE; // <<<<< New line
________while( ucMissedTicks > 0 )
________{
____________vTaskIncrementTick();
____________–ucMissedTicks;
________}
____}
____if( cYieldRequired )
____{
________/* As we have processed some ticks it is appropriate to yield
________to ensure the highest priority task that is ready to run is
________the task actually running. */
________cAlreadyYielded = ( signed portCHAR ) pdTRUE;
________taskYIELD();
____}
}
This way the cYieldRequired flag gets set to true if EITHER a tick was missed OR a task with higher priority than the current task was removed from the pending list.
Hope this makes sence - I have put in all the _____ to try and retain the indentation.
Again I have not tried this but it seems to be another good candidate for the V3.0.0 release.
Let me know what you think or if you have any problems.