Modele gwintowania

Metody oznaczone jako oneway nie blokują. W przypadku metod nieoznaczonych jako oneway wywołanie metody klienta zostanie zablokowane do czasu zakończenia wykonywania przez serwer lub wywołania synchronicznego wywołania zwrotnego (w zależności od tego, co nastąpi wcześniej). Implementacje metod serwerowych mogą wywoływać co najwyżej jedno synchroniczne wywołanie zwrotne; dodatkowe wywołania zwrotne są odrzucane i rejestrowane jako błędy. Jeśli metoda ma zwracać wartości poprzez wywołanie zwrotne, a nie wywołuje swojego wywołania zwrotnego, jest to rejestrowane jako błąd i zgłaszane klientowi jako błąd transportu.

Wątki w trybie przekazującym

W trybie przekazowym większość wywołań jest synchronicznych. Aby jednak zachować zamierzone zachowanie, zgodnie z którym wywołania oneway nie blokują klienta, dla każdego procesu tworzony jest wątek. Aby uzyskać szczegółowe informacje, zobacz przegląd HIDL .

Gwinty w spoiwowych HAL

Aby obsługiwać przychodzące wywołania RPC (w tym asynchroniczne wywołania zwrotne z HAL do użytkowników HAL) i powiadomienia o śmierci, z każdym procesem korzystającym z HIDL jest powiązana pula wątków. Jeśli pojedynczy proces implementuje wiele interfejsów HIDL i/lub procedur obsługi powiadomień o śmierci, jego pula wątków jest współdzielona pomiędzy nimi wszystkimi. Kiedy proces otrzymuje od klienta przychodzące wywołanie metody, wybiera wolny wątek z puli wątków i wykonuje wywołanie w tym wątku. Jeśli żaden wolny wątek nie jest dostępny, blokuje się do czasu, aż będzie dostępny.

Jeśli serwer ma tylko jeden wątek, wywołania serwera są realizowane w kolejności. Serwer z więcej niż jednym wątkiem może wykonywać wywołania w niewłaściwej kolejności, nawet jeśli klient ma tylko jeden wątek. Jednakże dla danego obiektu interfejsu gwarantowane jest uporządkowanie wywołań oneway (patrz Model wątków serwera ). W przypadku serwera wielowątkowego obsługującego wiele interfejsów, wywołania oneway do różnych interfejsów mogą być przetwarzane jednocześnie lub powodować inne wywołania blokujące.

Wiele zagnieżdżonych wywołań zostanie wysłanych w tym samym wątku hwbinder. Na przykład, jeśli proces (A) wykonuje wywołanie synchroniczne z wątku hwbinder do procesu (B), a następnie proces (B) wykonuje wywołanie synchroniczne z powrotem do procesu (A), wywołanie zostanie wykonane w oryginalnym wątku hwbinder w (A), które jest blokowane w przypadku pierwotnego połączenia. Optymalizacja ta umożliwia posiadanie pojedynczego wątku serwera zdolnego do obsługi zagnieżdżonych wywołań, ale nie rozciąga się na przypadki, gdy wywołania przechodzą przez inną sekwencję wywołań IPC. Na przykład, jeśli proces (B) wykonał wywołanie binder/vndbinder, które wywołało proces (C), a następnie proces (C) wywołał z powrotem do (A), nie może zostać obsłużony w oryginalnym wątku w (A).

Model wątków serwera

Z wyjątkiem trybu przekazywania, implementacje interfejsów HIDL na serwerze znajdują się w innym procesie niż klient i wymagają jednego lub więcej wątków oczekujących na przychodzące wywołania metod. Te wątki stanowią pulę wątków serwera; serwer może zdecydować, ile wątków chce uruchomić w swojej puli wątków i może użyć puli wątków o rozmiarze jednego do serializacji wszystkich wywołań na swoich interfejsach. Jeśli serwer ma więcej niż jeden wątek w puli wątków, może odbierać współbieżne wywołania przychodzące na dowolnym ze swoich interfejsów (w C++ oznacza to, że współdzielone dane muszą być starannie zablokowane).

Wywołania jednokierunkowe do tego samego interfejsu są serializowane. Jeśli klient wielowątkowy wywoła method1 i method2 na interfejsie IFoo oraz method3 na interfejsie IBar , method1 i method2 będą zawsze serializowane, ale method3 może działać równolegle z method1 i method2 .

Pojedynczy wątek klienta może spowodować jednoczesne wykonanie na serwerze z wieloma wątkami na dwa sposoby:

  • Połączenia oneway nie są blokowane. Jeżeli zostanie wykonane połączenie oneway , a następnie wywołanie oneway , serwer może jednocześnie wykonać wywołanie oneway i oneway .
  • Metody serwera, które przekazują dane z powrotem za pomocą synchronicznych wywołań zwrotnych, mogą odblokować klienta natychmiast po wywołaniu wywołania zwrotnego z serwera.

W drugim przypadku dowolny kod funkcji serwera, który jest wykonywany po wywołaniu wywołania zwrotnego, może być wykonywany współbieżnie, a serwer obsługuje kolejne wywołania od klienta. Obejmuje to kod funkcji serwera i automatyczne destruktory wykonywane na końcu funkcji. Jeśli serwer ma więcej niż jeden wątek w swojej puli wątków, problemy ze współbieżnością pojawiają się nawet wtedy, gdy wywołania przychodzą tylko z jednego wątku klienta. (Jeśli jakakolwiek warstwa HAL obsługiwana przez proces wymaga wielu wątków, wszystkie warstwy HAL będą miały wiele wątków, ponieważ pula wątków jest współdzielona dla poszczególnych procesów).

Gdy tylko serwer wywoła dostarczone wywołanie zwrotne, transport może wywołać zaimplementowane wywołanie zwrotne na kliencie i odblokować klienta. Klient postępuje równolegle ze wszystkim, co robi implementacja serwera po wywołaniu wywołania zwrotnego (co może obejmować uruchomienie destruktorów). Kod w funkcji serwera po wywołaniu zwrotnym nie blokuje już klienta (o ile w puli wątków serwera jest wystarczająca liczba wątków do obsługi połączeń przychodzących), ale może być wykonywany równolegle z przyszłymi wywołaniami klienta (chyba że pula wątków serwera ma tylko jeden wątek ).

Oprócz synchronicznych wywołań zwrotnych, oneway wywołania klienta jednowątkowego mogą być obsługiwane współbieżnie przez serwer z wieloma wątkami w swojej puli wątków, ale tylko wtedy, gdy te wywołania oneway są wykonywane na różnych interfejsach. wywołania oneway na tym samym interfejsie są zawsze serializowane.

Uwaga: Gorąco zachęcamy, aby funkcje serwera powróciły natychmiast po wywołaniu funkcji wywołania zwrotnego.

Na przykład (w C++):

Return<void> someMethod(someMethod_cb _cb) {
    // Do some processing, then call callback with return data
    hidl_vec<uint32_t> vec = ...
    _cb(vec);
    // At this point, the client's callback will be called,
    // and the client will resume execution.
    ...
    return Void(); // is basically a no-op
};

Model wątków klienta

Model wątków na kliencie różni się między wywołaniami nieblokującymi (funkcjami oznaczonymi słowem kluczowym oneway ) i wywołaniami blokującymi (funkcjami, które nie mają określonego słowa kluczowego oneway ).

Blokowanie połączeń

W przypadku blokowania połączeń klient blokuje, dopóki nie wydarzy się jedna z poniższych sytuacji:

  • Występuje błąd transportu; obiekt Return zawiera stan błędu, który można pobrać za pomocą Return::isOk() .
  • Implementacja serwera wywołuje wywołanie zwrotne (jeśli takie istnieje).
  • Implementacja serwera zwraca wartość (jeśli nie było parametru wywołania zwrotnego).

W przypadku powodzenia funkcja wywołania zwrotnego przekazana przez klienta jako argument jest zawsze wywoływana przez serwer, zanim sama funkcja zwróci. Wywołanie zwrotne jest wykonywane w tym samym wątku, w którym wykonywane jest wywołanie funkcji, więc realizatorzy muszą zachować ostrożność przy trzymaniu blokad podczas wywołań funkcji (i całkowicie ich unikać, jeśli to możliwe). Funkcja bez instrukcji generates lub oneway słowa kluczowego nadal blokuje; klient blokuje, dopóki serwer nie zwróci obiektu Return<void> .

Połączenia w jedną stronę

Gdy funkcja jest oznaczona oneway , klient natychmiast powraca i nie czeka, aż serwer zakończy wywołanie funkcji. Na pozór (i łącznie) oznacza to, że wywołanie funkcji zajmuje o połowę mniej czasu, ponieważ wykonuje połowę kodu, ale w przypadku pisania implementacji wrażliwych na wydajność ma to pewne konsekwencje w zakresie planowania. Zwykle użycie wywołania jednokierunkowego powoduje, że obiekt wywołujący kontynuuje planowanie, podczas gdy użycie normalnego wywołania synchronicznego powoduje, że program planujący natychmiast przekazuje połączenie od wywołującego do procesu wywoływanego. Jest to optymalizacja wydajności w spoiwie. W przypadku usług, w których wywołanie jednokierunkowe musi zostać wykonane w procesie docelowym z wysokim priorytetem, można zmienić politykę planowania usługi odbierającej. W C++ użycie metody setMinSchedulerPolicy libhidltransport z priorytetami i polityką programu planującego zdefiniowanymi w sched.h gwarantuje, że wszystkie wywołania usługi będą uruchamiane co najmniej z ustawioną polityką i priorytetem planowania.