FreeRTOS 8.0.1 Some tests of the Demo fails when using cooperative scheduler

sergechatroux wrote on Thursday, August 28, 2014:

I work at STMicroelectronics and maintain a port for an internal core called STxP70, the compiler is an Open64-based.
To test this port, I use the Demo example provided with FreeRTOS, I run this Demo on various targets, hardware, simulators, TLM.

During the update to the latest version (8.0.1), some tests failed when using the cooperative scheduler (in this mode, I increment the timer in the IDLE task hook).

I analyze the issue for the recursive mutexes test (Demo\Common\Minimal\recmutex.c).
Version 8.0.1 of the scheduler introduces the new define taskYIELD_IF_USING_PREEMPTION that prevents a low priority task to yield if a higher priority task becomes ready.

#if( configUSE_PREEMPTION == 0 ) /* If the cooperative scheduler is being used then a yield should not be performed just because a higher priority task has been woken. */ #define taskYIELD_IF_USING_PREEMPTION() #else #define taskYIELD_IF_USING_PREEMPTION() portYIELD_WITHIN_API() #endif

The test fails in prvRecursiveMutexPollingTask task in the following sequence of code:

/* We can resume the other tasks here even though they have a higher priority than the polling task. When they execute they will attempt to obtain the mutex but fail because the polling task is still the mutex holder. The polling task (this task) will then inherit the higher priority. The Blocking task will block indefinitely when it attempts to obtain the mutex, the Controlling task will only block for a fixed period and an error will be latched if the polling task has not returned the mutex by the time this fixed period has expired. */ vTaskResume( xBlockingTaskHandle ); vTaskResume( xControllingTaskHandle );

/* The other two tasks should now have executed and no longer
be suspended. */
if( ( xBlockingIsSuspended == pdTRUE ) || ( xControllingIsSuspended == pdTRUE ) )
xErrorOccurred = pdTRUE;

The calls to vTaskResume set the BlockingTask and ControllingTask tasks to the ready queue but the kernel does not schedule them because of the taskYIELD_IF_USING_PREEMPTION.
As a result, the Booleans xBlockingIsSuspended and xControllingIsSuspended are still to the pdFALSE value and the test fails.

The prvRecursiveMutexPollingTask task performs other checks on priority inheritance that also failed because the higher priority tasks do not try to get the mutex (as there are not scheduled).

According to my tests, there is a similar issue with AltQTest.c test.

Should I consider that this test can no more be performed using the cooperative scheduler ?

What is the goal of preventing a higher priority task to be scheduled in cooperative scheduler ?

Thank you for your support.

sergechatroux wrote on Thursday, August 28, 2014:

I get a similar issue with the Demo\Common\Full\events.c test.
I need to enable the cooperative scheduler to yield in queue.c

#if( configUSE_PREEMPTION == 0 ) #define queueYIELD_IF_USING_PREEMPTION() portYIELD_WITHIN_API() ...

rtel wrote on Thursday, August 28, 2014:


Thank you for the detailed explanation. A few points:

  • The demo and test tasks are created and primarily executed with configUSE_PREEMPTION set to 1 as they are intended to test the preemptive behaviour, and the delivered demo applications all have configUSE_PREEMPTION set to 1 to test the preemptive code. As you will see when looking at the code though we also occasionally run the tests in co-operative mode just for good measure as ideally they would run in either mode, and when we do this it is not unusual for some to report failures (rather than actually fail, as they do what they are supposed to do, just not what the test code is testing for) if they have not been tried in cooperative mode before. When that happens we will add in the appropriate yields within the test code itself. It sounds like, in the case you highlight here, that we have not run that particular test in co-operative mode and not noticed what you have reported. We will take a look and update if necessary.

  • The way the co-operative scheduler worked changed in version 7.6.0 - did you update your code from a version prior to version 7.6.0?

  • The ‘alternative’ API (the functions that have Alt in their name) are deprecated, so I should not worry about those.

  • It is also worth noting that V8.1.0 is now available, however I would suggest you don’t update to that just yet as there will be a patch release soon as V8.1.0 does not allow mutexes (with priority inheritance) to be given from an interrupt and a couple of people have mentioned this is a problem for them - so the patch will re-introduce this possibility to make sure these users don’t need to change anything in their code [I’m about to post the patch to the forum thread for these users to try immediately after sending this post].


sergechatroux wrote on Friday, August 29, 2014:

Thank you for your answer.

I worked on FreeRTOS since version 6.1.0 and I did port for versions 7.1.0, 7.4.1 and 7.5.0.
I currently upgrade from version 7.5.0 to 8.0.1.
Yesterday, I also did the port to version 8.1.0 and I will take upcoming patch.

About the Demo, I performed all the tests in both premptive and cooperative mode and they all succeeded from versions 6.1.0 to 7.5.0.

I did not read history of version 7.6.0, sorry.

But I am very surprise of this modification. The history file does not explain why this change had been done.

The resursive mutex test of the demo shows that preventing a low priority task to yield if a higher priority task should be woken up is an issue.

Tasks Rec1 , Rec2 and Rec3 have priority 2, 1, 0.

The previous cooperative behavior was (FreeRTOS version <= 7.5.0):

  • Task Rec3 holds the mutex
  • Task Rec3 wakes up Rec2
  • Task Rec2 tries to get the mutex and blocks
  • Task Rec3 inherits of Rec2 priority and wakes up Rec1
  • Task Rec1 tries to get the mutex and blocks
  • Task Rec3 inherits of Rec2 priority and releases the mutex
    Which was similar to preemptive execution.

Now we have:

  • Task Rec3 holds the mutex
  • Task Rec3 wakes up Rec2
  • Task Rec3 wakes up Rec1
  • Task Rec1 get/release the mutex
  • Task Rec2 get/release the mutex

In this model, there is no more priority inheritance.
High priority tasks wait for a low priority one to yield.

It looks like a case of priority inversion.

Please note that I hope that you will not remove the ‘alternative’ API for Queues.
I work on a multicore extension of FreeRTOS based on Naoki AIZU port
And the ‘alternative’ Queues API is used in this port.

rtel wrote on Friday, August 29, 2014:

So the change was made for exactly the reason you point out ;o) Previously the switch would have happened automatically - but the user feedback received was that this was preventing their use because the way it was implemented was not truly co-operative, and that giving the semaphore/mutex/whatever should unblock the task receiving the semaphore/mutex/whatever but a context switch should not occur until the application writer explicitly requested it by calling yield(). That way the application writer could use the primitives offered by FreeRTOS while maintaining complete control over exactly when a switch between tasks occurred.

Ref the alternative API - I would really like to take it out of the code because it clutters up the files. However I am a little nervous of doing so because I don’t know if or how people are still using it. Be aware though that, although the demos that use the alternative API are still executed prior to each release to see that they still work, the alternative API is not actively maintained or updated. New functionality added to the standard API will not necessarily be added to the alternative API.


sergechatroux wrote on Friday, August 29, 2014:

Thank you for this detailed answer.
I will be careful with the alternate API.