Priorities are not observed (apparently)

vvanbeveren wrote on Friday, May 25, 2018:

I’m using FreeRTOS 8.0.1 on an ARM Cortrex M4F (NRF52832) I have the following issue: There is a writing task SM (priority 2, sorry for the name, it actually means ‘System Manager’ :slight_smile: ) and a reading task NI (priority 1). The reading task sometimes needs to read a lot of data from our storage. However, it does this in bursts, taking a mutex, reading a block, and releasing the mutex again.

The writing task should have precidence over the reading task. However this does not seem to happen. The reading task does not seem to want to yield to the writing task. At some point the writing task will not be able to get the mutex within a reasonable limit. In that case the writing tasks writes an error message and does a busy waiting loop.

There are no mutex besides the read and write mutex (and the SPI mutex below). But all should be released once the task have completed the read and/or write operation. I’ve looked through the code and every branch is covered.

Two observations, see SystemView screenshot. First the SM task indicates it wants to run (-300 us), but does not seem to get to run! second the NI wants to run, and does get to run. Why? MS task has higher priority and should run first.

Second, even though the SM task has a higher priority, the NI task still gets time from FreeRTOS.

Any insights welcome.

Sysview screenshot

rtel wrote on Friday, May 25, 2018:

I can’t see the SystemView image at the moment.

Have you read the section “Mutexes and Task Scheduling” in the book that
can be downloaded here: - it starts on
page 256 in my copy. I don’t think it is exactly what you are seeing,
as it refers to the case where both tasks have the same priority, but it
may give you some ideas.

Does the writing task block anywhere else? Or just on the Mutex?

vvanbeveren wrote on Monday, May 28, 2018:

Hi Richard,

No both tasks have different priority. Its like pre-emption isn’t happening, though the kernel was configured as such and uses 32 kHz RTC for ticks.

From the attachment you can see where the mutex is released. At this point the SM task goes into ready, which is exactly what should happen, except that that it does not go into running. The NI task just keeps running and starts loading the next block of data, at which point the SM task can no longer acquire the lock, as it missed the window of oppertunity.

I can ‘hack’ it by introducing a 1 tick delay after the release of the mutex by the NI task. This will cause the SM task to actually start running.

If I do an explicit taskYield() it does seem to help, but it doesn’t always work. The portYield is written like this:

/* Scheduler utilities. */
#define portYIELD() do \
{                      \
    /* Set a PendSV to request a context switch. */ \
    SCB->ICSR = SCB_ICSR_PENDSVSET_Msk;             \
    __SEV();                                        \
    /* Barriers are normally not required but do ensure the code is completely  \
    within the specified behaviour for the architecture. */  \
    __DSB();                                        \
    __ISB();                                        \
}while (0)

vvanbeveren wrote on Monday, May 28, 2018:

Hi Richard,

I seem to not have replied to this post, as was my intention. See reply below.


rtel wrote on Monday, May 28, 2018:

This does not look like our code - see the implementation of portYIELD()
on line 81 (at the time of writing)

Where did the FreeRTOS port come from? What else is different from the
official port?

vvanbeveren wrote on Tuesday, May 29, 2018:

Hi Richard,

This is part of the nRF5x SDK from Nordic Radio. Looking at the code, though written differently, it does do the same thing. Much has changed, but technically it seems very similar. I have attached the complete portmacro file.

What would prevent FreeRTOS from preempting after the release of the lock? And why is it different when using a delay?


rtel wrote on Tuesday, May 29, 2018:

What would prevent FreeRTOS from preempting after the release of the
lock? And why is it different when using a delay?

The only thing would be if you were in a critical section or the
scheduler was suspended (vTaskSuspendAll()) - but you are not supposed
to call API functions when either of those are the case. We have lots
of tests to check tasks run in the correct order. Normally the only
time things happen in a way users don’t expect is when the two tasks
have the same priority - hence the book chapter.