Extensive critical section in xQueueReceive()

adarkar9 wrote on Friday, March 14, 2008:

I’m posting to share my recent findings.  Under certain circumstances, a call to xQueueReceive() can leave interrupts disabled for an extensive period of time.  I am running FreeRTOS on a C8051F120 from Silicon Labs.  The processor is running at 100 MHz.  Here is what I have observed:

If a queue changes from empty to not empty within xQueueReceive() between the return from to prvIsQueueEmpty() and the call to taskENTER_CRITICAL() a few lines later, the resultant critical section lasts for 140 us instead of the normal 11 us.

I made these measurements by setting a pin high just after the taskENTER_CRITICAL() call and clearing the pin just before the calls to taskYIELD() in both xQueueReceive() and xTaskResumeAll().

rtel wrote on Friday, March 14, 2008:

So the sequence you are commenting about is this:

1) prvIsQueueEmpty() decides the queue is empty.
2) It is determined that you specified a block time so you enter the if() statement and place yourself on the event list of the queue.
3) An interrupt places something in the queue
4) The critical section is entered - start of measurement.
5) The queue is unlocked - removing the task from the event list again due to the interrupt activity.
6) End of measurement, just before the yield.

So effectively it is the unlocking of the queue that is being measured.

You could try using xQueueAltReceive() instead.  This uses the ‘traditional’ approach used by a lot of RTOSes of just using a critical section over the whole function.  The code is simpler so will run much faster, but if measuring the critical section length then it will probably do worse in some circumstances and better in others.

If you need greater temporal accuracy in interrupt handling then you can use an interrupt that is of higher priority than those used by the kernel, this is implemented on some ports but not sure about the 8051 port.


adarkar9 wrote on Wednesday, March 19, 2008:

Thanks for the suggestions.

I could use a high priority interrupt, but I want to be able to post a semaphore from my ISR.  The xQueueAltReceive() suggestion is interesting, but I have since found other sections of the code that disable interrupts for greater than 90 us.

Because my comm link hardware contains only a one-byte buffer, I cannot run my link at 115,200 bps with my current interrupt latency.  I have slowed my comm link down to 57,600 bps and verified that interrupts are not disabled for longer than 180 us.  So far, so good.

rtel wrote on Tuesday, March 25, 2008:

The current head revision in the SourceForge SVN repository has a new version of the send and receive functions that make less use of critical sections.  These will be in the next official release too.