Priority inheritance and OS API inside protected section

This site uses cookies. By continuing to browse this site, you are agreeing to our Cookie Policy.

  • Priority inheritance and OS API inside protected section

    Hello,
    I'm writing here because i would like to have some opinion from experienced users about the "priority inheritance" of embOS tasks, it's use and it's limitations.
    In my opinion it would be a very useful feature to handle prioritized access to resources, but we found that you have to be very careful when you use some OS API.

    Here the story...

    We're using embOS for our multitasking application.

    In our application, one task have to access to a peripheral "P" in "precise" time slots, within a calculated jitter.
    This task has to run in real time, so its priority has been increased higher than the others.
    In the following days, we had to add some features to our application, including one that needs access to the same peripheral P, but in a relaxed way, by a lower priority task.
    We need mutual exclusion to access P, so I've decided to use a "resource semaphore".

    I know that embOS handles the problem of priority inversion for me, by the use of priority inheritance.
    So I was pretty confident that I would not had any problem, and the jitter would been under control.
    Infact this worked very well, until we decided to "optimize" the peripheral driver...
    We inserted an "OS_Delay(T)" inside a "busy-wait" loop, executed in the section protected by resource semaphore.
    This delay didn't change the peripheral access time, but release the CPU to other tasks.
    Surprisingly, we found that, sometime, the higher priority task was deleyed much longer than we aspect, because tasks with lower priority were running instead of it.
    Working on this, we ended with the below example. It shows that priority inheritance fails if we call the API "OS_Delay()" inside the section protected by the resource semaphore.
    If you want, run this example, and you'll see that the first "asm("NOP");" of HPTask will be executed. The resource semaphore section lasts for more than expected, showing priority inversion.
    If you comment out the "OS_Delay(10);" line in the LPTask, you'll see that the second "asm("NOP");" of HPTask will be executed, showing that "priority inheritance" works as it has to do.

    My questions are:

    - In this example priority inheritance is working the right way?
    - Are there other APIs that have the same behaviour of OS_Delay() for this example?

    Thank you :-),
    Alberto




    Brainfuck Source Code

    1. /*********************************************************************
    2. * *
    3. * OS version: 4.10 *
    4. * *
    5. **********************************************************************
    6. ----------------------------------------------------------------------
    7. --------- END-OF-HEADER --------------------------------------------*/
    8. #include "RTOS.h"
    9. OS_STACKPTR int StackHP[128], StackMP[128], StackLP[128]; /* Task stacks */
    10. OS_TASK TCBHP, TCBMP, TCBLP; /* Task-control-blocks */
    11. OS_RSEMA Resource;
    12. static void LPTask(void) {
    13. OS_DelayUntil(1000);
    14. OS_Use(&Resource);
    15. while(OS_GetTime() < 4000);
    16. OS_Delay(10);
    17. while(OS_GetTime() < 6000);
    18. OS_Unuse(&Resource);
    19. //end
    20. while (1) {
    21. OS_Delay (1000);
    22. }
    23. }
    24. static void HPTask(void) {
    25. OS_DelayUntil(2000);
    26. OS_Use(&Resource);
    27. OS_Unuse(&Resource);
    28. if (OS_GetTime() >= 10000){
    29. asm("NOP");
    30. }
    31. else{
    32. asm("NOP");
    33. }
    34. //end
    35. while (1) {
    36. OS_Delay (1000);
    37. }
    38. }
    39. static void MPTask(void) {
    40. OS_DelayUntil(3000);
    41. while(OS_GetTime() < 10000);
    42. //end
    43. while (1) {
    44. OS_Delay (1000);
    45. }
    46. }
    47. /*********************************************************************
    48. *
    49. * main
    50. *
    51. *********************************************************************/
    52. int main(void) {
    53. OS_IncDI(); /* Initially disable interrupts */
    54. OS_InitKern(); /* Initialize OS */
    55. OS_InitHW(); /* Initialize Hardware for OS */
    56. OS_CREATERSEMA(&Resource);
    57. /* You need to create at least one task before calling OS_Start() */
    58. OS_CREATETASK(&TCBHP, "HP Task", HPTask, 100, StackHP);
    59. OS_CREATETASK(&TCBMP, "MP Task", MPTask, 75, StackMP);
    60. OS_CREATETASK(&TCBLP, "LP Task", LPTask, 50, StackLP);
    61. OS_Start(); /* Start multitasking */
    62. return 0;
    63. }
    Display All
  • Hi Alberto,

    - In this example priority inheritance is working the right way?
    Yes, it is working the right way.
    The OS_Use(&Resource) in the HPTask causes the priority inheritance.
    The LPTask occupies the resource semaphore
    Instead of running further the HPTask and being blocked the scheduler runs the LPTask again.
    The basic idea is that the LPTask frees the resource sempahore with OS_Unuse().
    OS_Unuse() checks if any task is waiting for the resource semaphore which is true for the HPTask.
    Therefore the scheduler runs the HPTask again.
    But if you do a cooperative task switch with e.g. OS_Delay() before freeing the resource semaphore the LPTask is deactivated and the HPTask can't run because it is still waiting for the resource semaphore.
    In your application the MPTask runs then until it calls OS_Delay().
    In the meantimer the delay for the LPTask ended and the LPTask frees the resource semaphore which activates the HPTask.

    - Are there other APIs that have the same behaviour of OS_Delay() for this example?
    Yes, you will get the same situation with other API functions which do a cooperate task switch.


    Best regards,
    Til
    Please read the forum rules before posting.

    Keep in mind, this is *not* a support forum.
    Our engineers will try to answer your questions between their projects if possible but this can be delayed by longer periods of time.
    Should you be entitled to support you can contact us via our support system: segger.com/ticket/

    Or you can contact us via e-mail.
  • Hi Til,

    Thank you very much for the reply.

    In the last days we've tried to do "by hand" what we suppose priority inheritance has to do, and we've solved our issue.

    I show here a simplified version of what we have done. I've modified the code of LPTask and HPTask the way that can seen below, and all seems to work as we want, irrespective of the cooperate task switch. The MPTask does not run anymore instead of HPTask.

    Source Code

    1. static void LPTask(void) {
    2. OS_DelayUntil(1000);
    3. OS_Use(&Resource);
    4. while(OS_GetTime() < 4000);
    5. OS_Delay(10);
    6. while(OS_GetTime() < 6000);
    7. OS_EnterRegion();
    8. OS_TASK * OwnerTask = OS_GetResourceOwner(&Resource); // this task
    9. OS_Unuse(&Resource);
    10. if(OwnerTask) OS_SetPriority(OwnerTask, 50); //I reset the priority of the LPTask to its default
    11. OS_LeaveRegion();
    12. //end
    13. while (1) {
    14. OS_Delay (1000);
    15. }
    16. }
    17. static void HPTask(void) {
    18. OS_DelayUntil(2000);
    19. OS_EnterRegion();
    20. OS_TASK * OwnerTask = OS_GetResourceOwner(&Resource); //the LPTask
    21. if(OwnerTask) OS_SetPriority(OwnerTask, 100); // I manually set the priority of LPTask to the priority of the HPtask
    22. OS_LeaveRegion();
    23. OS_Use(&Resource);
    24. OS_Unuse(&Resource);
    25. if (OS_GetTime() >= 10000){
    26. asm("NOP");
    27. }
    28. else{
    29. asm("NOP");
    30. }
    31. //end
    32. while (1) {
    33. OS_Delay (1000);
    34. }
    35. }
    Display All


    What it looks like to me, is that a cooperative switch causes LPTask to lose the priority gained during the "priority inheritance phase". Probably in the doc there is not an explicit warning, but I may be wrong, I should check ...
    I think, in big applications, it is very hard to be sure that the functions called in the semaphore protected region does not use these API, so as a rule of thumb, is better to forget that embOS does priority inheritance and protect against priority inversion in some other way ( unless we are very sure of which OS API are called by the library functions we use inside the section protected by semaphores).

    Do you agree with this? Do you suggest a better strategy?

    P.S. I didn't check the code because i'm out of office now, but it should give the idea...

    Thanks,
    Best Regards,
    Alberto