Wednesday, October 18th 2017, 1:59am UTC+2

You are not logged in.

  • Login
  • Register

Dear visitor, welcome to SEGGER Forum. If this is your first visit here, please read the Help. It explains how this page works. You must be registered before you can use all the page's features. Please use the registration form, to register here or read more information about the registration process. If you are already registered, please login here.

alberto

Beginner

Date of registration: Aug 8th 2015

Posts: 2

1

Monday, August 10th 2015, 3:06pm

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




Source code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
/*********************************************************************
 *                                                                    *
 *       OS version: 4.10                                             *
 *                                                                    *
 **********************************************************************

 ----------------------------------------------------------------------


 --------- END-OF-HEADER --------------------------------------------*/


 #include "RTOS.h"

 OS_STACKPTR int StackHP[128], StackMP[128], StackLP[128];   /* Task stacks */
 OS_TASK TCBHP, TCBMP, TCBLP;                                /* Task-control-blocks */
 OS_RSEMA Resource;

 static void LPTask(void) {
   OS_DelayUntil(1000);
   OS_Use(&Resource);
   while(OS_GetTime() < 4000);
   OS_Delay(10);
   while(OS_GetTime() < 6000);
   OS_Unuse(&Resource);

   //end
   while (1) {
     OS_Delay (1000);
   }
 }

 static void HPTask(void) {
   OS_DelayUntil(2000);
   OS_Use(&Resource);
   OS_Unuse(&Resource);
   if (OS_GetTime() >= 10000){
           asm("NOP");
   }
   else{
           asm("NOP");
   }

   //end
   while (1) {
    OS_Delay (1000);
   }
 }

 static void MPTask(void) {
   OS_DelayUntil(3000);
   while(OS_GetTime() < 10000);

   //end
   while (1) {
     OS_Delay (1000);
   }
 }

 /*********************************************************************
 *
 *       main
 *
 *********************************************************************/

 int main(void) {
   OS_IncDI();                      /* Initially disable interrupts  */
   OS_InitKern();                   /* Initialize OS                 */
   OS_InitHW();                     /* Initialize Hardware for OS    */

   OS_CREATERSEMA(&Resource);

   /* You need to create at least one task before calling OS_Start() */
   OS_CREATETASK(&TCBHP, "HP Task", HPTask, 100, StackHP);
   OS_CREATETASK(&TCBMP, "MP Task", MPTask,  75, StackMP);
   OS_CREATETASK(&TCBLP, "LP Task", LPTask,  50, StackLP);
    OS_Start();                      /* Start multitasking            */
   return 0;
 }

SEGGER - Til

Super Moderator

Date of registration: Nov 14th 2007

Posts: 255

2

Thursday, August 13th 2015, 11:19am

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

alberto

Beginner

Date of registration: Aug 8th 2015

Posts: 2

3

Thursday, August 13th 2015, 4:38pm

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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
 static void LPTask(void) {
   OS_DelayUntil(1000);
   OS_Use(&Resource);
   while(OS_GetTime() < 4000);
   OS_Delay(10);
   while(OS_GetTime() < 6000);
   
   OS_EnterRegion();
   OS_TASK * OwnerTask = OS_GetResourceOwner(&Resource); // this task   
   OS_Unuse(&Resource);   
   if(OwnerTask) OS_SetPriority(OwnerTask, 50); //I reset the priority of the LPTask to its default
   OS_LeaveRegion();

   //end
   while (1) {
     OS_Delay (1000);
   }
 }

 static void HPTask(void) {
   OS_DelayUntil(2000);
   
   OS_EnterRegion();
   OS_TASK * OwnerTask = OS_GetResourceOwner(&Resource); //the LPTask
   if(OwnerTask) OS_SetPriority(OwnerTask, 100); // I manually set the priority of LPTask to the priority of the HPtask
   OS_LeaveRegion();
   
   OS_Use(&Resource);
   OS_Unuse(&Resource);
   if (OS_GetTime() >= 10000){
           asm("NOP");
   }
   else{
           asm("NOP");
   }

   //end
   while (1) {
    OS_Delay (1000);
   }
 }


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

mbenisapt

Beginner

Date of registration: Jul 15th 2016

Posts: 1

4

Friday, July 15th 2016, 2:44pm

I concur that it's a big problem for large applications with real-time components

... 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 ...