Quality RTOS & Embedded Software

 Real time embedded FreeRTOS RSS feed 
Quick Start Supported MCUs PDF Books Trace Tools Ecosystem TCP & FAT




Loading

Using Queues in critical sections

Posted by Vandra Akos on June 27, 2012
Hi!

Is it possible to use queues in critical sections? Of course it cannot block, but I would like try to read a value from the queue, or return failiure if it is empty. Like receiving with 0 timeout.
Should the normal Receive() function be used with 0 timeout, or should the ISR version be used?

Regards,
Ákos Vandra

RE: Using Queues in critical sections

Posted by Richard on June 27, 2012
As a general rule - do not call API functions from critical sections. That said, it might be ok in some situations for some API functions, but it is too complex to analise and document, and therefore too complex for the user, hence the general rule.

I would say, if you want to send or receive in a critical section, then do use the FromISR version. That is quite ok, and as you can't block, will be faster too.

Regards.

RE: Using Queues in critical sections

Posted by Vandra Akos on July 3, 2012
Hi Richard,

Thanks for taking interest in my problem.
I looked over the code to be executed, and it *should* have worked, however I suspect there is a "bug" in the Cortex-M3 port - even though it does not cause a problem if the OS is used "as it should be". That said, it is still a bug.

The following defines do not behave as they should:

#define portSET_INTERRUPT_MASK_FROM_ISR()0;portSET_INTERRUPT_MASK()
#define portCLEAR_INTERRUPT_MASK_FROM_ISR(x)portCLEAR_INTERRUPT_MASK();(void)x

First one always returns 0 as the interrupt mask, second one always clears all interrupts masks, and does not restore the previous state, as it would be logical in the context of xQueueGenericSendFromISR:

signed portBASE_TYPE xQueueGenericSendFromISR( xQueueHandle pxQueue, const void * const pvItemToQueue, signed portBASE_TYPE *pxHigherPriorityTaskWoken, portBASE_TYPE xCopyPosition )
{
signed portBASE_TYPE xReturn;
unsigned portBASE_TYPE uxSavedInterruptStatus;

/* Similar to xQueueGenericSend, except we don't block if there is no room
in the queue. Also we don't directly wake a task that was blocked on a
queue read, instead we return a flag to say whether a context switch is
required or not (i.e. has a task with a higher priority than us been woken
by thispost). */
uxSavedInterruptStatus = portSET_INTERRUPT_MASK_FROM_ISR();
{

(....................)

}
portCLEAR_INTERRUPT_MASK_FROM_ISR( uxSavedInterruptStatus );

return xReturn;
}

Now suppose I have a few interrupts that also need OS API, but have a low priority.
When one of my functions are meddling with the same peripheral that is used by the ISR, they set mask that interrupt by setting the BASEPRI register (disabling all interrupts below a certain priority). If another interrupt happens at that point that will use queue ISR functions, after returning from the interrupt, my interrupt that should be disabled is not. BANG.

The fix for this problem is quite obvious, but if you like I can create a patch.

Regards,
Ákos Vandra

RE: Using Queues in critical sections

Posted by Richard on July 3, 2012
“First one always returns 0 as the interrupt mask, second one always clears all interrupts masks, and does not restore the previous state, as it would be logical in the context of xQueueGenericSendFromISR:”


The first macro sets the mask to configMAX_SYSCALL_INTERRUPT_PRIORITY. It does not have to return anything on the Cortex-M (see below), but because the macro is used in the core code and therefore its prototype cannot change, it has to conform to the standard prototype for the macro, so just hard codes 0 as the return type. Most compiles will just remove this because the value is not used anywhere.

The second macro lowers the interrupt mask again. It always clears the interrupt mask completely but the Cortex-M NVIC is intelligent and will not accept an interrupt that has a priority below the currently executing priority, no matter what its BASEPRI value. Therefore, I believe the macros do actually function as intended.

For example, if an interrupt at a medium priority, and that interrupt makes use of these macros, then BASEPRI will get set to zero. If a low priority interrupt is then set to pending, it will not be accepted by the CPU core even though BASEPRI is zero, because the currently executing interrupt has a medium priority. When the currently executing interrupt completes, the lower priority interrupt will then be accepted and executed.

Always setting the value to zero is an optimisation because it is not necessary to explicitly save and restore the priority mask as it is on most other (every other) processor that has an unintelligent interrupt controller.

Regards.


RE: Using Queues in critical sections

Posted by Vandra Akos on July 3, 2012
Hi!

I'm not sure you understood my scenario. I'll try to clear it out for you:

1. "A" task is running, doing its job.
2. "A" task enters some sort of ciritcal-ish section, and sets the NVIC interrupt mask to medium.
3. "Low priority" interrupt occurs, ignored due to the interrupt mask
4. "Medium priority" interrupt occurs, uses the mentioned functions, and thus sets the interrupt mask back to 0
5. Low priority interrupt runs, even though "A" task expects it to be disabled <-- BAD
6. "A" task gets back the control, and screws up, due to the handled low priority interrupt.

Regards,
Ákos Vandra

RE: Using Queues in critical sections

Posted by Richard on July 3, 2012
Ah - ok, now I think I understand. What you are pointing out is that the FromISR functions cannot be used in a task, not that the FromISR functions don't work in an interrupt.

So the aforementioned macros could be modified to return BASEPRI and set the old BASEPRI, rather than returning and setting 0. Doing that would make the macros take longer to execute, for a scenario which is not really supposed to be done anyway. I'm not sure about including that in the main line code (not ruled it out, just need to think about it).

Regards.

RE: Using Queues in critical sections

Posted by Akos on July 3, 2012
Hi richard,

In the scenario the FromISR functions are called from within the medium interrupt routine, not the task.

Regards,
Ákos Vandra

RE: Using Queues in critical sections

Posted by Richard on July 3, 2012
Ok - maybe this still needs some clarification. I will break down your scenario:

“1. "A" task is running, doing its job.”


A good starting point!


“2. "A" task enters some sort of ciritcal-ish section, and sets the NVIC interrupt mask to medium.”


A task entering a critical section should only do so by calling taskENTER_CRITICAL() / taskEXIT_CRITICAL(). This will set basepri to configMAX_SYSCALL_INTERRUPT_PRIORITY, which prevents any interrupt that can legitimately use the FreeRTOS ISR safe API functions from executing. See: http://www.freertos.org/RTOS-Cortex-M3-M4.html


“3. "Low priority" interrupt occurs, ignored due to the interrupt mask”


So this interrupt has a priority that is masked by configMAX_SYSCALL_INTERRUPT_PRIORITY.


“4. "Medium priority" interrupt occurs, uses the mentioned functions, and thus sets the interrupt mask back to 0”


This is where it breaks down. An interrupt that is not masked by configMAX_SYSCALL_INTERRUPT_PRIORITY must not use system calls (FreeRTOS API functions). Therefore, if this scenario really happens, then the application is out of the stated user requirements.

So that leaves the question as to whether it is OK to call the FromISR functions from a task, and the answer is I think as per my original answer - yes it is - but with the caveat that the FromISR function is not itself called from a task level critical section. However, there is another rule that API functions should not be called from critical sections, so that too should never happen.

Regards.

RE: Using Queues in critical sections

Posted by Akos on July 3, 2012
Hi richard.

My medium interrupt priority is lower than the MAX_SYSCALL_INTERRUPT_PRIORITY, thus it should be able to call API functions.
My low interrupt priority is even lower.

I called it critical-ish section because it does not need to disable all interrupts that are lower than max_syscall_priority, it needs to disable only the lower priority interrupt, that's why it sets the basepri register directly, and expects it not to change until it exits from that section.

So let's see some numbers, maybe that clarifies.

We have one task, TaskA.
Two interrupts:
ItL with priority 25 (higher number denotes lower priority)
ItM with priority 7
Max_SYSCALL is 6.



1. "A" task is running, doing its job.
2. "A" task enters a section where it needs to disable ItL, but ItM may run without bothering the job, so it's best if we leave it on, so we may handle that one asap. We set BasePRI to 15.

3. ItL interrupt occurs, ignored due to its priority (25) > baspri (15)
4. ItM interrupt occurs, calls xQueueReceiveFromISR, and thus sets the interrupt mask back to 0. It may call the function, as it's priority is lower than SysCall_priority.

5. Low priority interrupt runs, bacause BASEPRI was reset. "A" task expects it to be still disabled <-- BAD
6. "A" task gets back the control, and screws up, due to the handled low priority interrupt.






RE: Using Queues in critical sections

Posted by Richard on July 3, 2012
I think I understand the scenario now - and my previous reply is still valid. Your problem is occurring because you are accessing BASEPRI directly, rather than through the kernel using taskENTER_CRITICAL(). The kernel assumes it has control of BASEPRI.

Have you considered just disabling the interrupt for the peripheral you are accessing in the critical-ish section, rather than masking all interrupts up to the syscall value?

Regards.

RE: Using Queues in critical sections

Posted by Vandra Akos on July 6, 2012
Ah, I see. It wasn't clear for me that accessing the BASEPRI register should be done only through the kernel. Maybe this fact needs mentioning somewhere in the docs?

Your suggestion should work, thanks.

Regards,
Ákos Vandra

RE: Using Queues in critical sections

Posted by Richard on July 6, 2012
“Maybe this fact needs mentioning somewhere in the docs?”


See item number 5:
http://www.freertos.org/FAQHelp.html

Regards.

RE: Using Queues in critical sections

Posted by Vandra Akos on July 6, 2012
Oops, my bad, sorry! :-/

Regards,
Ákos Vandra

RE: Using Queues in critical sections

Posted by Peter A. Bigot on July 6, 2012
The FAQ has:
“Do not use any method for disabling and enabling interrupts other than calls to portENTER_CRITICAL() and portEXIT_CRITICAL().”

I've always found it rather confusing that many of the portX macros have taskX equivalents which are macros that do nothing but invoke the portX one. What's the recommended practice on selecting which spelling of that functionality should be used in code? Is there any function/macro for which there's a taskX version that is not equivalent to a portX version (if both exist)?


[ Back to the top ]    [ About FreeRTOS ]    [ Sitemap ]    [ ]




Copyright (C) Amazon Web Services, Inc. or its affiliates. All rights reserved.

Latest News

FreeRTOS kernel V10 is available for immediate download. Now MIT licensed.


FreeRTOS Partners

ARM Connected RTOS partner for all ARM microcontroller cores

IAR Partner

Microchip Premier RTOS Partner

RTOS partner of NXP for all NXP ARM microcontrollers

STMicro RTOS partner supporting ARM7, ARM Cortex-M3, ARM Cortex-M4 and ARM Cortex-M0

Texas Instruments MCU Developer Network RTOS partner for ARM and MSP430 microcontrollers

OpenRTOS and SafeRTOS