‟overcq”

Z bloga o OUX/C+

Przełączanie zadań w programie OUX / C+

Podsystem OUX zawiera wbudowaną obsługę zadań wewnętrznych programu pisanego w technologii OUX / języku C+. Zadania są podobne do tzw. wątków (threads), ale nie wymagają obsługi (zewnętrznej) przez środowisko systemu operacyjnego. Ponadto zadania podpadają pod inny model użycia oraz są zintegrowane z raportami, cyklerami i impulsatorami.

W każdym czasie podczas wykonywania się programu napisanego w technologii OUX / języku C+ aktywne jest tylko jedno zadanie, tzn. podsystem OUX używa kooperacyjnego przełączania zadań. Zapewnia to szybkość wykonania programu i brak rywalizacji o zasoby.

Zadania

Pierwszym zadaniem, które nie musi być odrębnie definiowane, jest to, które wykonuje się przy starcie programu procedurą main. Inne zadania potrzebują być zdefiniowane jako procedura ogólnie w następujący sposób:

D( moduł, nazwa_zadania )
{
    O{
    }
}

Gdzie “O” to jest pętla główna zadania wykonywana aż do żądania przez system jego zakończenia, gdy jest wyrzucane (czyli zatrzymywane) z innego miejsca programu.

Tak zdefiniowane zadania tworzy się (uruchamia) następującą instrukcją:

D_M( moduł, nazwa_zadania );

A wyrzuca się (zatrzymuje) instrukcją:

D_W( moduł, nazwa_zadania );

W pętli głównej zadania musi znajdować się co najmniej (i zwykle) jedna instrukcja przełączenia do innego zadania (wybranego automatycznie przez podsystem OUX).

Taką instrukcją jest na przykład instrukcja bezwarunkowego przełączenia “I_B” jak w przykładzie:

D( moduł, nazwa_zadania )
{
    O{  I_B()
            break;
    }
}

Instrukcja ta (podobnie jak każda instrukcja przełączenia) zawiera blok wykonywany wtedy, gdy zadanie ma być wyrzucone. Tutaj następuje zwykłe opuszczenie pętli głównej zadania bez wykonywania jakichkolwiek innych działań. Jakkolwiek na żądanie zakończenia zadania zawsze musi nastąpić opuszczenie tej pętli w jakikolwiek sposób.

Instrukcja bezwarunkowego przełączenia powoduje, że zadanie jest wykonywane przez cały czas, chyba że w tym zadaniu znajdzie się jakiekolwiek oczekiwanie na coś. Czyli by nie marnować czasu procesora na bezustanne wykonywanie zadania należy użyć instrukcji warunkowego przełączenia zadania: w oczekiwaniu na raport albo na określony czas cyklera lub impulsatora.

Raporty

Raport jest to taki obiekt, który sygnalizuje zaistnienie czegoś. Zadanie może czekać na nazwany raport. Raport jest tworzony i wyrzucany przez zadanie, które na niego czeka, jak w przykładzie:

D( moduł, nazwa_zadania )
{   X_M( moduł, nazwa_raportu );
    O{  X_B( moduł, nazwa_raportu, 0 )
            break;
    }
    X_W( moduł, nazwa_raportu );
}

Instrukcja “X_M” tworzy raport, a instrukcja “X_W” wyrzuca go.

“X_B” jest instrukcją warunkowego przełączenia zadania w oczekiwaniu na raport. Jeśli nadejdzie tak nazwany raport, to zadanie będzie wznowione od tej instrukcji.

Instrukcja ta zawiera trzeci parametr, gdzie można podać wskaźnik zmiennej typu N, by zachowana została w niej liczba zgubionych tak nazwanych raportów przed wznowieniem tego zadania.

W procedurze, w której raport będzie emitowany, deklaruje się go instrukcją “X_A” jak w przykładzie:

X_A( moduł, nazwa_raportu );
X_F( moduł, nazwa_raportu );

Instrukcja “X_F” emituje raport.

Znaczniki stanu

Jakkolwiek wygodniej jest deklarować stany programu, które decydują o emisji bądź nieemisji nazwanego raportu.

W trakcie przetwarzania danych pojawiają się stany, które decydują, że raport powinien być wyemitowany bądź nie. Wtedy należy użyć znaczników stanu. Znaczniki stanu są zwykle nazywane tak samo jak raport, o którym decydują. Na przykład:

B U_L( moduł, nazwa_raportu );
if()
    U_F( moduł, nazwa_raportu );
if()
    U_L( moduł, nazwa_raportu );
X_U( moduł, nazwa_raportu );

Instrukcja “U_L” ustawia znacznik stanu na stan wyłączony, a w powyższym przykładzie jest użyta łącznie z deklaracją zmiennej typu B. Instrukcja “U_F” ustawia znacznik na stan włączony. Natomiast instrukcja “X_U” emituje raport, jeśli tak samo nazwany znacznik jest włączony.

Istnieje również instrukcja “U_R”, która czyta stan znacznika, oraz instrukcja “U_E”, która czyta go, a następnie ustawia na wyłączony. Na przykład:

if( U_R( moduł, nazwa_raportu ))
    ;
if( U_E( moduł, nazwa_raportu ))
    ;

Znaczniki stanu można używać nie tylko do warunkowego emitowania raportu, ale również do oznaczania stanów cząstkowych w obiektach, stanów zapisywanych w postaci pól bitowych jak w przykładzie:

struct
{ unsigned U_R( state, visible ) :1;
}q;
U_L( q.state, visible );
U_F( q.state, visible );

Do tego celu wymyślone zostały standardowe nazwy takie jak:

Cyklery

Cykler wylicza czas, z którym został utworzony. Po wyliczeniu całego czasu instrukcja “Y_B” warunkowego przełączenia zadania w oczekiwaniu na czas cyklera wznawia zadanie od tej instrukcji i wylicza czas od nowa. Na przykład:

D( moduł, nazwa_zadania )
{   I timer = Y_M(czas);
    O{  Y_B( timer, 0 )
            break;
    }
    Y_W(timer);
}

Instrukcja “Y_M” tworzy cykler z czasem podanym milisekundach, a instrukcja “Y_W” wyrzuca go.

Instrukcja “Y_B” zawiera drugi parametr, gdzie można podać wskaźnik zmiennej typu N, by zachowana została w niej liczba zgubionych upływów czasów przed wznowieniem tego zadania.

Cykler służy do synchronizowania czasu pracy zadania, gdy jest to wyraźnie wymagane, na przykład w programie sekundnika zegara, a nie do losowego spowalniania programu.

Impulsatory

Impulsator jest połączeniem raportu z cyklerem: wylicza czas, z którym został wyemitowany, i wtedy wznawia zadanie od instrukcji “Yi_B” warunkowego przełączenia zadania w oczekiwaniu na czas impulsatora. Na przykład:

D( moduł, nazwa_zadania )
{   Yi_M( moduł, nazwa_impulsatora );
    O{  Yi_B( moduł, nazwa_impulsatora );
            break;
    }
    Yi_W( moduł, nazwa_impulsatora );
}

Instrukcja “Yi_M” tworzy impulsator, a instrukcja “Yi_W” wyrzuca go.

Natomiast przykład emisji impulsatora jest następujący:

Yi_A( moduł, nazwa_impulsatora );
if()
    Yi_F( moduł, nazwa_impulsatora, czas );
if()
    Yi_L( moduł, nazwa_impulsatora );

Instrukcja “Yi_F” emituje impulsator po czasie podanym w milisekundach. Instrukcja “Yi_L” usuwa emisję impulsatora.