Critical sections - nradulovic/esolid-kernel GitHub Wiki

Intro

In concurrent programming, a critical section is a piece of code that accesses a shared resource (data structure or device) that must not be concurrently accessed by more than one thread of execution. A critical section will usually terminate in fixed time, and a thread will have to wait for a fixed time to enter it (aka bounded waiting). Some synchronization mechanism is required at the entry and exit of the critical section to ensure exclusive use, for example a semaphore.

eSolid RT Kernel internal critical sections

In contrast to application code in kernel code there is no other mechanism to protect critical code except disabling interrupts. Fortunately, some ports have ability to mask certain interrupts with low priority and allow interrupts with higher priority. By masking low priority interrupts the kernel can protect its critical sections. However for this scheme to work its forbidden to call any OS service function from a high priority interrupt. If this rule is not followed then the high priority interrupt with an OS service function call can preempt the kernel low priority interrupt which will in that case corrupt the kernel internal data structures.

1) It is forbidden to call any OS service function from an interrupt with the priority higher than the kernel interrupt priority.

2) On some ports the kernel never completely disables interrupts.

Implementation

There are multiple ways how are critical sections implemented:

  • The simplest method is to prevent interrupts on entry into the critical section, and restoring interrupts to their previous state on exit from critical section. Any thread of execution entering any critical section anywhere in the system, with this implementation, will prevent any other thread, including an interrupt, from being executed on the CPU.
  • This approach can be improved upon by using semaphores. To enter a critical section, a thread must obtain a semaphore, which it releases on leaving the section. Other threads are prevented from entering the critical section at the same time as the original thread, but are free to gain control of the CPU and execute other code, including other critical sections that are protected by different semaphores.

Disabling interrupts

In order to properly disable interrupts the application must follow these steps:

  • declare an auto variable which will hold interrupt state
  • save interrupt status into auto variable and disable interrupts
  • access the shared resource
  • restore previously saved interrupt state

For auto variable declaration macro ES_CRITICAL_DECL() is used. Then by using the macro ES_CRITICAL_ENTER() the state of enabled interrupts will be saved in auto variable declared earlier. Immediately after saving of interrupt state the macro will lock interrupts. Now the code can safely access and use the shared resource. When code finishes using the resource it will call ES_CRITICAL_EXIT() macro. This macro will restore interrupts from the previously saved interrupt state.

ES_CRITICAL_DECL();                 /* Declare an interrupt status variable */
    :
    :
    :   
ES_CRITICAL_ENTER();                /* Save state and lock interrupts */
/*
 * Access the shared resource
 */
ES_CRITICAL_EXIT();                 /* Restore previous state unlocking the interrupts */

Disabling Kernel

Using semaphores