ISR/xHigherPriorityTaskWoken/invoke context switch - non default?

Hi,

Typically there is code like that in an ISR:

void vSoftwareInterruptHandler( void )
{
portBASE_TYPE xHigherPriorityTaskWoken = pdFALSE;

    /* 'Give' the semaphore to unblock the task. */
    xSemaphoreGiveFromISR( xBinarySemaphore, &xHigherPriorityTaskWoken );

    /* Clear the software interrupt bit using the interrupt controllers
    Clear Pending register. */
    mainCLEAR_INTERRUPT();

    /* Giving the semaphore may have unblocked a task - if it did and the
    unblocked task has a priority equal to or above the currently executing
    task then xHigherPriorityTaskWoken will have been set to pdTRUE and
    portEND_SWITCHING_ISR() will force a context switch to the newly unblocked
    higher priority task.

    NOTE: The syntax for forcing a context switch within an ISR varies between
    FreeRTOS ports.  The portEND_SWITCHING_ISR() macro is provided as part of
    the Cortex M3 port layer for this purpose.  taskYIELD() must never be called
    from an ISR! */

    if (xHigherPriorityTaskWoken!=pdFALSE)
        portEND_SWITCHING_ISR( xHigherPriorityTaskWoken );

}

This strikes me a bit as odd since I would assume that the default behaviour that most people want is to enforce a context switch after the ISR to the higher priority task which was awoken by the ISR.

Why isn’t this the default behavior and we need to do all this extra calls explicitly?

What would be a valid reason to wait for the next context switch to switch to the highest priority task instead of making this the default behaviour?

Regards,

Robert

Since ISRs are processor specific and invoked by HW, FreeRTOS calls must be explicit.
Also some FreeRTOS features are port specific and might not be applicable on all ports/processors.

The reason for needing the call is that for some processors, you need to wait to the end of the ISR to do that operation, as it will actually switch to the new task right there and then and not wait for the ISR to return.

Yes, if the only processors to be supported did the switch by pending an interrupt that only happens when all ISRs had returned, this wouldn’t have been needed.

1 Like

Some good info can also be found here and here. I won’t restate what has been said there as others have said it better.

Why it isn’t the default:

  • Backwards compatibility - we’d need yet another config value. I’m not a fan of even more config values. Also demand for this isn’t very high.
  • Irregular behaviors across ports - the context switch may need to be called at the end, it may not.
  • Unique behaviors across IRS - you may want certain ISRs to cause a context switch always, other maybe only under certain conditions.

Hi,

Thank you for your quick reply.

The second link should probably be FreeRTOS FAQ - FreeRTOS API - FreeRTOS

Here is my summary, which might be added to the FAQ:

Q:

• Why do APIs for exclusive use in ISRs set pxHigherPriorityTaskWoken rather than perform a context switch?

A:

• demand to automatically call the context switch isn’t very high

• backwards compatibility
– we’d need yet another config value (not a fan of even more config values)

• don’t enforce unique behaviours across ISRs
– you may want certain ISRs to cause a context switch always, others you may want to perform a context switch only under certain conditions (see FAQ below)

• deal with irregular (hardware/ISR) behaviours across ports - the context switch may need to be called at the end, it may not
– for some processors, you need to wait until the end of the ISR to do that operation, as it will actually switch to the new task right there and then and not wait for the ISR to return
– if the only processors to be supported did the switch by pending an interrupt that only happens when all ISRs have returned, this wouldn’t have been needed
– some FreeRTOS features are port-specific and might not be applicable on all ports/processors
– different ports handle the scheduling differently
· In some ports, the call to portYIELD_FROM_ISR will immediately go to the scheduler and it will then go to that task,
· stopping in the middle of the ISR
· the ISR is really running in the context of the task that was interrupted,
· just as if it had a subroutine call at that point
· this means the rest of the ISR code won’t run until that task gets its next chance to run,
· and since the whole point of putting code into an ISR is to get it to run right away,
· that tends not to be what you want
· On machines where portYIELD_FROM_ISR just sets a pending interrupt bit at the lowest priority,
· then yes, that could have been done in the FreeRTOS functions,
· but then FreeRTOS could not be run on machines which don’t work that way,
· and one goal of FreeRTOS was to support a wide variety of platforms

• (from FreeRTOS FAQ):

• in summary, it empowers the application writer to decide if a context switch is necessary, and in so doing, enables the application writer to prevent unnecessary context switch thrashing
• as an example, imagine a simple interrupt service routine that receives strings character by character
– rather than performing a context switch after each character it might be more efficient to perform a context switch only once the entire string has been received

• See full discussion here:
FromISR/FROM_ISR

• Another post here:
Why don't do the context switch in the internal of the API ended with FromISR? - #5 by richard-damon

• FreeRTOS FAQ:
FreeRTOS FAQ - FreeRTOS API - FreeRTOS

Regards,

Robert

Hi,

• (from FreeRTOS FAQ):

• in summary, it empowers the application writer to decide if a context switch is necessary, and in so doing, enables the application writer to prevent unnecessary context switch thrashing
• as an example, imagine a simple interrupt service routine that receives strings character by character
– rather than performing a context switch after each character it might be more efficient to perform a context switch only once the entire string has been received

Is this really a good example or just a priority inversion masked as “prevent unnecessary context switch thrashing”? Does it encourage non-deterministic code?

In case the ISR provokes a context switch at the end for every character probably the high-priority task should pick it up. Otherwise, the ISR could e.g. write into a circular buffer or use a different pattern.

Regards,

Robert

This is a valid point, yet I do not think that deterministic behavior is of any significance in this use case. Not enforcing a context switch after every character very simply implies that there may or may not be more than one character more buffered before the processor task has a chance to process those characters, so you are on the spot with your non-determinism - however, if we are talking serial drivers here, there is not really a merit in deterministic behavior. Depending on the protocol definition, it will take at least a minimum amount of characters in every frame to process before the frame is completly read and can be passed on to the next layer. The processor task does not need to look at every character in real time while frame reception is still in process, so statistically, we gain throughput by potentially allowing more characters to be buffered before we let the processor task run.

Edit: Sometimes it makes sense (again depending on the protocol) to preprocess characters in the ISR and do the context switch enforcement conditionally, for example when the protocol supports a unique end-of-frame character; the ISR may in that case enforce a context switch if (and possibly only if) the end of frame character is found. The downside of that architecture, trivially, is that the ISO/OSI layering is severely undermined and protocol idiosyncracies will propagate down the physical layer. Yet sometimes that is desired behavior.

Yes, it does seem a bit odd. “From FreeRTOS V7.3.0 pxHigherPriorityTaskWoken is an optional parameter and can be set to NULL.” This here is the major part of the problem because optional can be taken to mean that you don’t have to do it. There ought to be a warning that if you do not do this optional thing, your code will appear to be functioning because a context switch will occur at the next tick. The overall problem is that often I am calling a FromISR routine in code that has been called from the original ISR. I can call the yield because I am using Cortex, but from the discussion others are not so lucky.

Now if the root ISR routine could setup a “woken” flag – on a stack to account for nested ISR – any FromISR routine might use that extra parameter to indicate if you wanted to switch as needed or not. The root ISR would call yield as needed. Not quite sure why anyone would want to call an RTOS function that implies context switching with the switching a hit-or-miss thing based on how soon the next tick comes.

You can sort of do this if you want. Setup a global woken flag that is set to 0. At the end of the ISR, copy the flag to a local temporary, clear the flag, and do yield if the local variable is true.