====== OSA : Critical sections ======
===== Intro =====
Critical sections are used to provide a task with unshared access to a resource when this resource is used by other tasks. For example: a task uses EEPROM and enters a critical section to avoid EEPROM data modification by other tasks.
In fact, this is a rare situation for a cooperative RTOS because the programmer switches context manually, but it is still possible. So OSA has services to protect critical sections of the program.
To allow usage of critical sections you must define the ##[[en:osa:ref:appendix:configuration|OS_ENABLE_CRITICAL_SECTION]]## constant in ##[[en:osa:ref:appendix:configuration|OSAcfg.h.]]##
There are two services to protect program's critical sections:
* ##[[en:osa:ref:allservices:OS_EnterCriticalSection|OS_EnterCriticalSection]]## - enter critical section
* ##[[en:osa:ref:allservices:OS_LeaveCriticalSection|OS_LeaveCriticalSection]]## - leave critical section
When entering a critical section, all interrupts are disabled (their current value is stored in internal system flags). Only the task that called the service ##[[en:osa:ref:allservices:OS_EnterCriticalSection|OS_EnterCriticalSection]]## can get control independently of priority, and all other tasks will be blocked until ##[[en:osa:ref:allservices:OS_LeaveCriticalSection|OS_LeaveCriticalSection]]## is called. When in the critical section it still possible to return to the scheduler.
To work with critical sections you need to know some rules written below.
~~UP~~
===== GIEx saving =====
The service ##[[en:osa:ref:allservices:OS_EnterCriticalSection|OS_EnterCriticalSection]]## saves current GIEx values, and then clears them. Thus if you call this service twice in a row, the GIEx value saved by the first call will be overwritten with zeroes by the second call, since after the first call GIEx has been cleared. In this situation ##[[en:osa:ref:allservices:OS_LeaveCriticalSection|OS_LeaveCriticalSection]]## will restore the zeroed GIEx saved by the second call.
**Avoid calling service ##[[en:osa:ref:allservices:OS_EnterCriticalSection|OS_EnterCriticalSection]]## twice in a row.**
~~UP~~
===== Timers =====
You will be able to use task delays (##[[en:osa:ref:allservices:OS_Delay|OS_Delay]]##) and waiting with timeout services only if ##[[en:osa:ref:allservices:OS_Timer|OS_Timer]]## is not called from an interrupt (since interrupts are disabled while in a critical section). There are two ways of solving this problem:
- Enable interrupts manually after entering the critical section (if the program architecture allows the use of interrupts while in the critical section)
- Duplicate the call to ##[[en:osa:ref:allservices:OS_Timer|OS_Timer]]## in the main loop (near ##[[en:osa:ref:allservices:OS_Sched|OS_Sched]]##). See example below:
void interrupt int_routine (void)
{
OS_EnterInt();
if (TMR2IF) // This code will not work in critical section
{
TMR2IF = 0;
OS_Timer(); // This service will be called only outside
// critical section
}
OS_LeaveInt();
}
void Task1 (void)
{
for (;;) {
. . .
OS_EnterCriticalSection();
OS_Delay(100);
OS_LeaveCriticalSection();
. . .
}
}
void main (void)
{
OS_Init();
OS_Task_Create(0, Task1);
for (;;)
{
OS_Sched();
if (OS_IsInCriticalSection()) {
if (TMR2IF) {
TMR2IF = 0;
OS_Timer(); // This service will be called within critical section
}
}
}
}
~~UP~~
===== Wait for events =====
All task except one (the one that called ##[[en:osa:ref:allservices:OS_EnterCriticalSection|OS_EnterCriticalSection]]##) are blocked even if their priority is higher and they are ready to run. Thus if a task enters a critical section and then waits for an event that occurs in another task, the system will loop. To avoid this you can first wait for the event and then enter the critical section.
void Task1 (void)
{
for (;;) {
. . .
// Incorrect
OS_EnterCriticalSection();
OS_Bsem_Wait(BS_USART_FREE);
. . .
// Correct
OS_Bsem_Wait(BS_USART_FREE);
OS_EnterCriticalSection();
. . .
}
}
~~UP~~
===== Services =====
^ Service ^ Arguments ^ Description ^ Properties ^
| ''bool ''\\ ##[[en:osa:ref:allservices:OS_IsInCriticalSection|OS_IsInCriticalSection]]## | '''' | Return true if any task is in critical section | |
| ##[[en:osa:ref:allservices:OS_EnterCriticalSection|OS_EnterCriticalSection]]## | '''' | Enter critical section | {{osa:ref:attr_call_task.png|Allowed only in task}} |
| ##[[en:osa:ref:allservices:OS_LeaveCriticalSection|OS_LeaveCriticalSection]]## | '''' | Leave critical section | {{osa:ref:attr_call_task.png|Allowed only in task}} |
~~UP~~