Critical section entry/exit - which facility to use where

dcrocker wrote on Thursday, April 05, 2018:

I’m adding protection against multiple tasks accessing/modify mutating data structures (e.g. linked lists) concurrently and I’m wondering what mechanisms it is best practice to use and when. I see the following possibilities:

  1. Take and give a recursive mutex.
  2. vTaskSuspendAll and xTaskResumeAll. The documentation doesn’t state whether this is recursive, but I can wrap it to make it so if necessary.
  3. taskENTER_CRITICAL and task EXIT_CRITICAL.
  4. cpu_irq_save and cpu_irq_restore from the ARM Cortex library.

My thinking is along these lines:

  • use #4 if the data is access/modified by an ISR with a higher priority than configMAX_SYSCALL_INTERRUPT_PRIORITY;
  • use #3 if the data is accessed/modified by an ISR with a priority less than or equal to configMAX_SYSCALL_INTERRUPT_PRIORITY;
  • otherwise use #2 if the critical section is short and guaranteed not to block;
  • otherwise use #1.

Is that right? Are there any other considerations I shold be aware of?

rtel wrote on Thursday, April 05, 2018:

  1. Take and give a recursive mutex.

Probably a bit heavy for linked list, assuming the linked list is fast.

  1. vTaskSuspendAll and xTaskResumeAll. The documentation doesn’s state
    whether this is recursive, but I can wrap it to make it so if necessary.

Yes, this is recursive. Suspending the scheduler is fast and
deterministic, resuming the scheduler can take longer because, if tasks
were moved out of the blocked state while the scheduler was suspended
then they will have been placed into a pending list, and moved from the
pending list into their respective ready lists when the scheduler is
resumed. So this method is fast in most cases, but could be slightly
slower occasionally if tasks are unblocked while the scheduler is
suspended. Note that suspending the scheduler does NOT mask interrupts,
they keep executing, so this does not protect the linked list if
interrupts also access the list. It also means that only interrupts can
ever move tasks out of the Blocked state (and into the above mentioned
pending list) while the scheduler is suspended. If non of your
interrupts can do that then this is a good method.

  1. taskENTER_CRITICAL and task EXIT_CRITICAL.

This is the fastest method - very fast to enter a critical section and
very fast to exit, so is the ideal method if the section being protected
is very short. It does however mask interrupts up to
configMAX_SYSCALL_INTERRUPT_PRIORITY.

  1. cpu_irq_save and cpu_irq_restore from the ARM Cortex library.

Would be very wary about using that method if it modifies the BASEPRI
register as BASEPRI is under the control of FreeRTOS. If it only
updates the global interrupt enable bit then that should be ok but keep
in mind it will mask all your interrupts, whereas FreeRTOS critical
sections only mask some.

Given all the above, I think your statements are basically correct.

dcrocker wrote on Thursday, April 05, 2018:

Barry, thanks for your response. One bit I didn’t quite understand:

It also means that only interrupts can ever move tasks out of the Blocked state (and into the above mentioned pending list) while the scheduler is suspended. If non of your interrupts can do that then this is a good method.

I do have interrupts that unblock tasks, e.g. when a DMA transfer to/from the SD card interface completes. As I understand it, this doesn’t stop me using vTaskSuspendAll and xTaskResumeAll to implement critical sections. The (only?) drawback is that a task that is unblocked while the scheduler is suspended will have to be moved once to the pending list (when the ISR calls vTaskNotifyGiveFromISR) and then from the pending list to the ready list when xTaskResumeAll is called. So not quite as fast as if it moved direct to the ready list. Is that right?

rtel wrote on Thursday, April 05, 2018:

Yes, that is right.

The reason I said the only thing that can unblock tasks while the
scheduler is suspended is interrupts is because, if the scheduler is
suspended, then no task other than the task that suspended the scheduler
will ever be running.