FreeRTOS: Why to call taskYIELD_FROM_ISR() method within the isrHandler
Asked Answered
G

2

10

I try to understand why the user has to call the taskYIELD_FROM_ISR() method and why it isn't automatically called by the RTOS within the xStreamBufferSendFromISR method.

My question refers to the FreeRTOS_Manual p. 369.

/* A stream buffer that has already been created. */
StreamBufferHandle_t xStreamBuffer;

void vAnInterruptServiceRoutine( void ) {
  size_t xBytesSent;
  char *pcStringToSend = "String to send";
  BaseType_t xHigherPriorityTaskWoken = pdFALSE;

  /* Attempt to send the string to the stream buffer. */
  xBytesSent = xStreamBufferSendFromISR(xStreamBuffer,(void *) pcStringToSend,strlen( pcStringToSend),&xHigherPriorityTaskWoken);

  if(xBytesSent != strlen(pcStringToSend)){
    /* There was not enough free space in the stream buffer for the entire string to be written, ut xBytesSent bytes were written. */
  }

  /* 
    If xHigherPriorityTaskWoken was set to pdTRUE inside xStreamBufferSendFromISR() then a    
    task that has a priority above the priority of the currently executing task was unblocked 
    and a context switch should be performed to ensure the ISR returns to the unblocked task. 
    In most FreeRTOS ports this is done by simply passing xHigherPriorityTaskWoken into 
    taskYIELD_FROM_ISR(), which will test the variables value, and perform the context switch 
    if necessary. Check the documentation for the port in use for port specific instructions. 
  */

  taskYIELD_FROM_ISR(xHigherPriorityTaskWoken);
}

My understanding of the scenario

Preconditions

  • There are two tasks
    • Task1 (high priority) and Task2 (low priority)
  • Task1 is in Blocked-State waiting for a streamBuffer input
  • Task2 is in Running-State and is interrupted from vAnInterruptServiceRoutine

Within the ISR

  • The ISR method calls xStreamBufferSendFromISR()
  • This call means, that the Blocked-State of Task1 changes into the Ready-State

Case A If the ISR method is returning now, the scheduler will not be called and Task2 is running until the time slice period is over and then the scheduler switches to the high prior Task1.

Case B If the ISR method calls taskYIELD_FROM_ISR(xHigherPriorityTaskWoken); at last, the scheduler will be called and after returning the ISR, Task1 will be running instead of Task2.

Questions

  1. Is it right, that Task2 will be executed until a time slice period is over or is there another tigger for the task switch?
  2. Why is the taskYIELD_FROM_ISR() method not called automatically by the RTOS when calling xStreamBufferSendFromISR()?
Gunfight answered 30/10, 2019 at 17:58 Comment(0)
A
11
  1. Yes, without a call to taskYIELD_FROM_ISR, the scheduler gives context to Task1 on the next scheduled tick. This can be verified with Segger SystemView. uartPrintTask blocks on a semaphore and then prints data from a buffer. Notice the long delay between when uartPrintTask is "ready" and when it is "running". This delay is variable - although it never lasts longer than 1mS (the tick rate in the example) enter image description here

Now, the same example with taskYIELD_FROM_ISR added at the end of the ISR. uartPrintTask is consistently executed after the ISR enter image description here 2. FreeRTOS can't automatically call anything from an ISR. You've got full control over all interrupts. taskYIELD_FROM_ISR should be placed at the end of your ISR implementation (but you may have placed the call to xStreamBufferSendFromISR anywhere in the ISR).

One of the beautiful things about FreeRTOS (in my opinion) is that it doesn't hijack anything and gives tons of flexibility. You can have interrupts that execute completely "underneath" the RTOS - FreeRTOS doesn't need to know anything about them. Not automatically calling taskYIELD_FROM_ISR is another example of this flexibility (in my opinion).

From the SafeRTOS manual:

Calling either xQueueSendFromISR() or xQueueReceiveFromISR() within an interrupt service routine can potentially cause a task to leave the Blocked state which then necessitates a context switch if the unblocked task has a higher priority than the interrupted task. A context switch is performed transparently (within the API functions) when either xQueueSend() or xQueueReceive() cause a task of higher priority than the calling task to exit the Blocked state. This behavior is desirable from a task, but not from an interrupt service routine. Therefore, xQueueSendFromISR() and xQueueReceiveFromISR(), rather than performing the context switch themselves, instead return a value indicative of whether a context switch is required. If a context switch is required, the application writer can use taskYIELD_FROM_ISR() to perform the context switch at the most appropriate time, normally at the end of the interrupt handler. See “xQueueSendFromISR()” on page 69 and “xQueueReceiveFromISR()” on page 71 which describe the xQueueSendFromISR() and xQueueReceiveFromISR() functions respectively for more information.

Ajani answered 7/11, 2019 at 5:53 Comment(6)
While some people can try to sell this as a "feature" of FreeRTOS, the necessity of calling taskYIELD_FROM_ISR() as well as using the "FromISR" APIs in ISRs puts a big burden on the programmer. It also leads to duplication of APIs ("regular" and "FromISR()" APIs). But most unfortunately, this design aspect of FreeRTOS opens up a wide range of possible errors where the wrong API type is used. For example, callback functions are often invoked from the ISR context. Moreover, the flag xHigherPriorityTaskWoken needs to be somehow propagated up the call stack.Packhorse
Yes, having additional 'FromISR' variants do add another set of functions to wrap one's head around. I don't understand your point on callbacks though . . . How does using a callback function from an ISR have anything to do with FreeRTOS? Are you arguing that callbacks should be somehow disallowed? I'm curious because I used them without issue in event-driven 'bare-metal' solutions before adopting an RTOS. Callbacks can be a very lightweight way of adding flexibility to what is otherwise a standard, reusable ISR. Obviously they're a double-edged sword since they execute in ISR context.Ajani
Callback functions are certainly fast, but you always need to be very careful about the context in which the callback is called. In FreeRTOS this is additionally exacerbated by the need to use "regular" API in certain callbacks (e.g., FreeRTOS software timers) and "FromISR" API in other callbacks (e.g., FreeRTOS "tick hook function"). It's really easy to make a mistake here, and FreeRTOS doesn't help you to know when you do.Packhorse
All fair statements. I feel that FreeRTOS is quite similar to 'C' in these respects - there's lots of raw power, but care must be taken. . . The liberal use of configAssert() does uncover configuration and usage problems early in the dev cycle, which is a big help.Ajani
In your experiment that calls portYIELD_FROM_ISR(), why does the "wasteful task" still runs a bit after the ISR?Helianthus
@DanielChin - wastefulTask will consume any available CPU time: void wastefulTask( void* NotUsed) { while(1) { volatile int i, j; i = 10; j = i; i = j; } } Code for the full example can be found on github This example is from Hands on RTOS with MicrocontrollersAjani
K
-1
  • 1) I think so
  • 2) I'm not really sure of this. I think that is done to keep interrupt fast and to keep context switch overhead low. So it is in your choice to trigger a context switch or not depending on the response time you need.
Krongold answered 31/10, 2019 at 9:40 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.