‟overcq”

Z bloga o OUX/C+

Zadania wątkowane oraz raporty typu “itimer”

Opisane tutaj funkcjonalności są używane, lecz w małej liczbie wystąpień, i dlatego są nie do końca zestandaryzowane. Jednak warto o nich wiedzieć, ponieważ nie można ich zastąpić niczym innym, jeśli się chce napisać program OUX, korzystający z pewnego rodzaju wywołań systemowych.

Są takie wywołania systemu operacyjnego, które uniemożliwiają kooperacyjne przełączanie zadań w programie OUX, ponieważ blokują się w oczekiwaniu na coś. Takie oczekiwanie na coś można by nazwać oczekiwaniem na raport nie zarejestrowany w podsystemie OUX, ponieważ emitowany przez system operacyjny. Przykładami takich wywołań systemu operacyjnego są:

int
poll( struct pollfd *fds
, nfds_t nfds
, int timeout
);

Jeżeli parametr “timeout” jest różny od zera, a w szczególności równy ‒1.

int
get_wch( wint_t *wch
);

Jest to procedura z biblioteki “ncurses”. Czeka na znak przez ustalony przed jej wywołaniem czas.

Jeśli by wywołać w programie OUX taką procedurę w zwykły sposób, to zablokowałaby ona wykonywanie się innych zadań programu do czasu powrotu z tej jednej procedury.

Zadania wątkowane

W celu rozwiązania problemu blokowania się na nieskończony czas powstały zadania wątkowane. Są to specjalnego rodzaju zadania, które podsystem OUX, uruchamia w osobnym wątku systemu operacyjnego, umożliwiając w ten sposób zablokowanie tylko tego jednego wątku w oczekiwaniu na raport systemu operacyjnego. Pozostałe zadania systemu operacyjnego, które wykonują się w startowym wątku programu OUX, nie są blokowane. Podsystem OUX zapewnia, by tylko jedno zadanie wykonywało się jednocześnie: albo zadanie wątkowane, albo inne zadanie, mimo że są uruchomione co najmniej dwa wątki systemu operacyjnego.

Zadanie wątkowane tworzy się i wyrzuca następującymi instrukcjami:

Dh_M( moduł, nazwa_zadania, podidentyfikator_zadania, arg );
Dh_W( moduł, nazwa_zadania, podidentyfikator_zadania );

Trzeci parametr jest identyfikatorem kolejnego zadania o tej samej nazwie uruchomionego w następnym wątku, jednak ta funkcjonalność nie została jeszcze sprawdzona. Czwarty parametr tworzenia zadania wątkowanego zostanie przekazany procedurze odblokowującej, o czym za chwilę.

Natomiast w definicji zadania wątkowanego trzeba zapisać deklarację i oczekiwanie na raport przychodzący z systemu operacyjnego, przykładowo:

void
procedura_odblokowująca( P p
){
}
D( moduł, nazwa_zadania )
{   Xh_A( procedura_odblokowująca );
    I_D
    {   Xh_B_();
        VO1_( n = poll( fds, nfds, -1 ));
        Xh_B()
            break;
    }
}

Instrukcja “Xh_A” deklaruje procedurę odblokowującą wywołanie systemowe (w powyższym przykładzie “poll”), które blokuje się na raporcie systemu operacyjnego, czyli deklaruje procedurę emitującą taki raport systemowy. W pokazanym powyżej przypadku taka procedura może zmieniać stan jednego z deskryptorów, na które czeka “poll”, i wtedy “poll” wraca z raportem o tym deskryptorze.

Instrukcja “Xh_B_” powinna znajdować się tuż przed wywołaniem systemowym czekającym na raport systemowy, a instrukcja “Xh_B” tuż po tym wywołaniu. Ta druga jest instrukcją blokową, której blok (wykonywany przy wyrzucaniu zadania) powinien doprowadzić do opuszczenia pętli “I_D” zadania.

Raporty typu itimer

Drugi rodzaj wywołań, który został podany powyżej na przykładzie procedury “get_wch”, może być obsłużony w zwykłych zadaniach przy pomocy raportów typu “itimer”. W obecnej implementacji podsystemu OUX w programie może być tylko jedno zadanie z raportem tego rodzaju.

Podsystem OUX umożliwia użycie wywołania systemowego, blokującego się na raporcie systemowym, jako oczekiwania na upływ podanego czasu, po którym zadania mają być wznowione. Jakkolwiek obecna implementacja ma jeszcze inne ograniczenia, które nie są eliminowane ze względou na małe zapotrzebowanie na tę funkcjonalność. Dlatego w programach OUX, które mają pracować bezbłędnie, nie powinno się używać raportów typu “itimer”.

Raporty te deklaruje się, tworzy i wyrzuca następującymi instrukcjami:

B
procedura_blokująca( sigset_t *sigset
){
}
void
procedura_ustawiania_czasu_blokowania( Z_clock_time *tv
){
}
D( moduł, nazwa_zadania )
{   Xh1_M( procedura_blokująca, procedura_ustawiania_czasu_blokowania );
    I_D
    {   Xh1_B()
            break;
    }
    Xh1_W();
}

Instrukcja “Xh1_M” deklaruje procedurę blokującą się na wywołaniu systemowym, która ustawia maskę sygnałów systemu operacyjnego na czas zablokowania, oraz deklaruje procedurę ustawiającą czas zablokowania. Instrukcja “Xh1_B” blokuje się w oczekiwaniu na raport systemowy, który wystąpi, gdy procedura blokująca wróci z wartością “yes”. Instrukcja “Xh1_W” wyrzuca raport typu “itimer”.

Przykładowe rozwiązanie raportów “itimer” można znaleźć w programie terminal-text-file-editor.