Queue for newest values used in critical section?

Hi all,

Apologies if this is covered already, I found this thread but it did not help me too much:
https://forums.freertos.org/t/using-queues-in-critical-sections/4660

I’m using a queue from an ISR to pass data to a task. However, once the queue is full I want it to discard its oldest values and take the new ones rather than the default behavior of rejecting new values. I am always running non-blocking and the code below is simulating what I want but without the ISR (I have used a SWTimer, I realise I need to use the FromISR functions in an ISR).

To do what I want I need to pop the queue once it is full, this is a fault case for me so I try to push first:

if(xQueueSendToBack(pkt_queue, (void *)pkt_ptr, (TickType_t)0) == pdFALSE)
{
    // Must be in a critical section or interrupts disabled by this point to avoid other ISRs adding to the queue when we remove an item
    packet_t discard;
    taskENTER_CRITICAL();
    xQueueReceive(pkt_queue, &discard, (TickType_t)0);
    xQueueSendToBack(pkt_queue, (void *)pkt_ptr, (TickType_t)0);
    taskEXIT_CRITICAL();
}

This seems to work although the documentation states:

FreeRTOS API functions must not be called from within a critical section.

Maybe the queue operations do not count? Maybe they do not rely on the kernel to do anything.

All suggestions very welcome. Is there a better implementation than what I have done here?

Thanks very much

Ed

I’d stick to the docs and don’t rely on implementation details.
Why do you need a critical section in the ISR at all ?
There is a xQueueSendToBackFromISR and xQueueReceiveFromISR.
See Queue Management API documentation.

Hi Hartmut,
There are multiple writers to the queue. If you remove and item outside a critical section then another thread might fill that spot before this one does. It would be possible to control with interrupt priorities.
Thanks
Ed

Sorry - I don’t get the big picture of your approach. Why is important when which item is enqueued ?
However, if there are multiple FromISR writers and you really need a critical section, enclose it by taskENTER/EXIT_CRITICAL_FROM_ISR.

Hi Hartmut,

The at risk sequence is:

  1. Queue is full
  2. ISR1 fails to add an item
  3. ISR1 removes and item to make space
  4. ← ISR2 is triggered and finding a free space in the queue adds its item
  5. ISR1 fails to add its item as queue is full again

Writing this out makes me think I could just try to add the item in a loop. Then it would keep removing items until it was successful. That would avoid the need for a critical section but would mean the items are out of order which might be acceptable but is not ideal.

Ok - got it. Except why the order of queued items is important. If the interrupts are independent the order is determined by the interrupt sources and is out of your hands. It doesn’t make much sense to take care of an order in the rare case where one interrupt is nested (if this is supported by your MCU), I think.
A retry loop is a reasonable alternative to the critical section, even though it doesn’t add much overhead.

This answer depends on the FreeRTOS port you are using. It is right for ports that support interrupt nesting - which I’m assuming is the case as my understanding is the issue only occurs when interrupts nest.

The rule about not calling API functions from critical sections applies to tasks because tasks may need to block or otherwise get preempted and critical sections prevent that. It may not apply to calling API functions from interrupts though - not sure but all interrupt safe APIs run to completion anyway as they can’t block.

Do you need to run the interrupts at different priorities? If not then they won’t nest anyway.

Hi Barry and Hartmut,

Thanks for your suggestions. I have decided to avoid using critical sections by applying the loop as described above. Agree that queue operations probably work fine with interrupts disabled but as I have an easy alternative that seems a better choice.

Thanks

Ed