====== OSA : Критичекие секции ======
===== Введение =====
Критическая секция в программе предназначена для захвата одной задачей какого-либо ресурса, если к этому ресурсу имеют доступ другие задачи. Таким ресурсом может быть, например, модуль периферии, внешняя память, наконец, процессорное время.
Вообще-то, в кооперативной RTOS редко встречаются конфликтые ситуации, т.к. передачей управления планировщику (а следовательно, и остальным задачам, осуществляется вручную), тем не менее такие ситуации возможны. Поэтому в OSA реализована возможность защиты критических секций.
Для того, чтобы пользоваться сервисами работы с критическими секциями, в ##[[osa:ref:appendix:configuration|OSAcfg.h]]## нужно определить константу ##[[osa:ref:appendix:configuration|OS_ENABLE_CRITICAL_SECTION]]##
Для защиты критических секций есть два сервиса:
##[[osa:ref:allservices:OS_EnterCriticalSection|OS_EnterCriticalSection]]## - вход в критическую секцию и ##[[osa:ref:allservices:OS_LeaveCriticalSection|OS_LeaveCriticalSection]]## - выход из критической секции.
При входе в критическую секцию запрещаются все прерывания (их текущее состояние запоминается во внутренних системных флагах), а задача, вызвавшая сервис, становится единственной готовой к выполнению, вне зависимости от ее приоритета. При этом допустима передача управления планировщику.
Входя в критическую секцию, нужно помнить некоторые правила, описанные ниже.
~~UP~~
===== Сохранение текущего состояния флагов GIEx =====
Текущее состояние флагов GIEx сохраняется во внутреннюю переменную системы. После чего флаги GIEx сбрасываются. Поэтому, если два раза подряд вызвать сервис ##[[osa:ref:allservices:OS_EnterCriticalSection|OS_EnterCriticalSection]]##, то в первый раз она сохранит текущее состояние влагов GIEx, а во второй - нулевое состояние, т.е. на момент второго вызова флаги уже были сброшены. В этом случае сервис ##[[osa:ref:allservices:OS_LeaveCriticalSection|OS_LeaveCriticalSection]]## восстановит флаги, сохраненные во втором вызове, т.е. нулевые. Поэтому следует избегать двойного вызова сервисы входа в критическую секцию.
~~UP~~
===== Системный таймер в критической секции =====
Использовать задержки ##[[osa:ref:allservices:OS_Delay|OS_Delay]]##, таймеры, а также ожидать события с таймаутами можно только в том случае, если вызов сервиса ##[[osa:ref:allservices:OS_Timer|OS_Timer]]## находится вне прерывания, иначе система будет ждать до бесконечности. (Либо после входа в критическую секцию разрешать прерывания сервисом ##[[osa:ref:allservices:OS_EI|OS_EI]]##, если логика программы позволяет в данной ситуации оставить прерывания.) Избежать этого можно, продублировав вызов ##[[osa:ref:allservices:OS_Timer|OS_Timer]]## в основном цикле (там же, где вызывается ##[[osa:ref:allservices:OS_Sched|OS_Sched]]##).
void interrupt int_routine (void)
{
    OS_EnterInt();
    if (TMR2IF)       // Эта часть программы не отработает в
    {                 // критической секции
         TMR2IF = 0;
         OS_Timer();  // Этот вызов обрабатывается вне критической секции
    }
    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();    // Этот вызов обрабатывается в критической секции
            }
        }
    }
}
~~UP~~
===== Ожидание событий в критической секции =====
Все задачи, кроме той, которая вызвала сервис ##[[osa:ref:allservices:OS_EnterCriticalSection|OS_EnterCriticalSection]]##(), блокируются, даже если их приоритет выше и они готовы к выполнению. Поэтому, если после перехода в критическую секцию мы начинаем ждать какое-то событие, которое устанавливается  в другой задаче (например, ждем освобождение ресурса, который занят другой задачей), то мы его никогда не дождемся.
Избежать этого можно, если сначала дождаться события, а затем войти в критическую секцию.
void Task1 (void)
{
    for (;;) {
        . . .
        // Неправильный подход
        OS_EnterCriticalSection();
        OS_Bsem_Wait(BS_USART_FREE);
        . . .
        // Правильный подход
        OS_Bsem_Wait(BS_USART_FREE);
        OS_EnterCriticalSection();
        . . .
    }
}
~~UP~~
===== Сервисы для критических секций =====
^  Сервис  ^  Аргументы   ^  Описание  ^  Свойства  ^
| ''bool ''\\ ##[[osa:ref:allservices:OS_IsInCriticalSection|OS_IsInCriticalSection]]##  |  ''''  | Возвращает 1, если одна из задач находится в критической секции.  |   |
| ##[[osa:ref:allservices:OS_EnterCriticalSection|OS_EnterCriticalSection]]##  |  ''''  | Вход в критическую секцию  | {{osa:ref:attr_call_task.png|Разрешен вызов только в контексте задачи}}  |
| ##[[osa:ref:allservices:OS_LeaveCriticalSection|OS_LeaveCriticalSection]]##  |  ''''  | Выход из критической секции  | {{osa:ref:attr_call_task.png|Разрешен вызов только в контексте задачи}}  |
~~UP~~