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 ) { I_D { } }
Gdzie “I_D” 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 ) { I_D { 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 “I_D” 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 ); I_D { 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:
- ‣ “state” dla stanu obiektu
- ‣ “mode” dla wyjątkowego, tymczasowego trybu pracy obiektu
- ‣ “req” dla żądania realizacji funkcji
- ‣ “_ex” jako dopisek, np. do “mode”, jako rozszerzenie trybu
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); I_D { 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 ); I_D { 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.