Modelli di filettatura

I metodi contrassegnati come oneway non si bloccano. Per i metodi non contrassegnati come oneway , la chiamata al metodo del client verrà bloccata finché il server non avrà completato l'esecuzione o non avrà richiamato un callback sincrono (a seconda di quale evento si verifica per primo). Le implementazioni del metodo server possono chiamare al massimo un callback sincrono; le chiamate di richiamata aggiuntive vengono scartate e registrate come errori. Se si suppone che un metodo restituisca valori tramite callback e non richiama il proprio callback, questo viene registrato come errore e segnalato come errore di trasporto al client.

Thread in modalità passthrough

In modalità passthrough, la maggior parte delle chiamate sono sincrone. Tuttavia, per preservare il comportamento previsto secondo cui le chiamate oneway non bloccano il client, viene creato un thread per ciascun processo. Per i dettagli, vedere la panoramica HIDL .

Thread negli HAL binderizzati

Per gestire le chiamate RPC in entrata (incluse le richiamate asincrone dagli HAL agli utenti HAL) e le notifiche di morte, un pool di thread è associato a ciascun processo che utilizza HIDL. Se un singolo processo implementa più interfacce HIDL e/o gestori di notifiche di morte, il suo threadpool è condiviso tra tutti loro. Quando un processo riceve una chiamata al metodo in entrata da un client, seleziona un thread libero dal threadpool ed esegue la chiamata su quel thread. Se non è disponibile alcun thread libero, si blocca finché non ne diventa disponibile uno.

Se il server ha un solo thread, le chiamate al server vengono completate in ordine. Un server con più di un thread può completare le chiamate fuori ordine anche se il client ha un solo thread. Tuttavia, per un dato oggetto interfaccia, è garantito che le chiamate oneway siano ordinate (vedere Modello di threading del server ). Per un server multi-thread che ospita più interfacce, le chiamate oneway a diverse interfacce possono essere elaborate contemporaneamente tra loro o con altre chiamate di blocco.

Più chiamate nidificate verranno inviate sullo stesso thread hwbinder. Ad esempio, se un processo (A) effettua una chiamata sincrona da un thread hwbinder al processo (B), e quindi il processo (B) effettua nuovamente una chiamata sincrona al processo (A), la chiamata verrà eseguita sul thread hwbinder originale in (A) che è bloccato sulla chiamata originaria. Questa ottimizzazione rende possibile avere un unico server con thread in grado di gestire chiamate annidate, ma non si estende ai casi in cui le chiamate viaggiano attraverso un'altra sequenza di chiamate IPC. Ad esempio, se il processo (B) ha effettuato una chiamata binder/vndbinder che ha richiamato un processo (C) e quindi il processo (C) richiama in (A), non può essere servito sul thread originale in (A).

Modello di threading del server

Fatta eccezione per la modalità passthrough, le implementazioni server delle interfacce HIDL risiedono in un processo diverso rispetto al client e necessitano di uno o più thread in attesa di chiamate al metodo in entrata. Questi thread costituiscono il pool di thread del server; il server può decidere quanti thread desidera eseguire nel suo pool di thread e può utilizzare una dimensione del pool di thread pari a uno per serializzare tutte le chiamate sulle sue interfacce. Se il server ha più di un thread nel threadpool, può ricevere chiamate in entrata simultanee su qualsiasi delle sue interfacce (in C++, ciò significa che i dati condivisi devono essere attentamente bloccati).

Le chiamate unidirezionali nella stessa interfaccia vengono serializzate. Se un client multi-thread chiama method1 e method2 sull'interfaccia IFoo e method3 sull'interfaccia IBar , method1 e method2 verranno sempre serializzati, ma method3 può essere eseguito in parallelo con method1 e method2 .

Un singolo thread di esecuzione del client può causare l'esecuzione simultanea su un server con più thread in due modi:

  • le chiamate oneway non si bloccano. Se viene eseguita una chiamata oneway e poi viene chiamata una non oneway , il server può eseguire simultaneamente la chiamata oneway e la chiamata non oneway .
  • I metodi server che restituiscono i dati con callback sincroni possono sbloccare il client non appena il callback viene chiamato dal server.

Nel secondo modo, qualsiasi codice nella funzione server che viene eseguito dopo la chiamata del callback può essere eseguito contemporaneamente, con il server che gestisce le chiamate successive dal client. Ciò include il codice nella funzione server e i distruttori automatici che vengono eseguiti alla fine della funzione. Se il server ha più di un thread nel proprio pool di thread, si verificano problemi di concorrenza anche se le chiamate provengono da un solo thread client. (Se un HAL servito da un processo necessita di più thread, tutti gli HAL avranno più thread perché il pool di thread è condiviso per processo.)

Non appena il server chiama la callback fornita, il trasporto può chiamare la callback implementata sul client e sbloccare il client. Il client procede in parallelo con qualunque cosa faccia l'implementazione del server dopo aver chiamato il callback (che può includere l'esecuzione di distruttori). Il codice nella funzione server dopo la richiamata non blocca più il client (purché il pool di thread del server abbia thread sufficienti per gestire le chiamate in entrata), ma può essere eseguito contemporaneamente alle chiamate future dal client (a meno che il pool di thread del server abbia solo un thread ).

Oltre ai callback sincroni, le chiamate oneway da un client a thread singolo possono essere gestite contemporaneamente da un server con più thread nel proprio pool di thread, ma solo se tali chiamate oneway vengono eseguite su interfacce diverse. le chiamate oneway sulla stessa interfaccia vengono sempre serializzate.

Nota: incoraggiamo vivamente le funzioni del server a ritornare non appena hanno chiamato la funzione di callback.

Ad esempio (in 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
};

Modello di threading client

Il modello di threading sul client differisce tra chiamate non bloccanti (funzioni contrassegnate con la parola chiave oneway ) e chiamate bloccanti (funzioni per cui non è specificata la parola chiave oneway ).

Blocco delle chiamate

Per bloccare le chiamate, il client si blocca finché non si verifica una delle seguenti condizioni:

  • Si verifica un errore di trasporto; l'oggetto Return contiene uno stato di errore che può essere recuperato con Return::isOk() .
  • L'implementazione del server chiama il callback (se ce n'era uno).
  • L'implementazione del server restituisce un valore (se non era presente alcun parametro di callback).

In caso di successo, la funzione di callback che il client passa come argomento viene sempre chiamata dal server prima che la funzione stessa ritorni. La richiamata viene eseguita sullo stesso thread su cui viene effettuata la chiamata alla funzione, quindi gli implementatori devono fare attenzione a mantenere i blocchi durante le chiamate alla funzione (ed evitarli del tutto quando possibile). Una funzione senza un'istruzione generates o una parola chiave oneway è ancora bloccante; il client si blocca finché il server non restituisce un oggetto Return<void> .

Chiamate unidirezionali

Quando una funzione è contrassegnata oneway , il client ritorna immediatamente e non attende che il server completi la chiamata di funzione. In superficie (e nel complesso), ciò significa che la chiamata di funzione impiega metà del tempo perché sta eseguendo metà del codice, ma quando si scrivono implementazioni sensibili alle prestazioni, ciò ha alcune implicazioni di pianificazione. Normalmente, l'utilizzo di una chiamata unidirezionale fa sì che il chiamante continui a essere pianificato, mentre l'utilizzo di una normale chiamata sincrona fa sì che lo scheduler trasferisca immediatamente il processo dal chiamante al chiamato. Questa è un'ottimizzazione delle prestazioni nel raccoglitore. Per i servizi in cui la chiamata unidirezionale deve essere eseguita nel processo di destinazione con una priorità elevata, la politica di pianificazione del servizio ricevente può essere modificata. In C++, l'utilizzo del metodo setMinSchedulerPolicy di libhidltransport con le priorità e le politiche dello scheduler definite in sched.h garantisce che tutte le chiamate al servizio vengano eseguite almeno con la politica e la priorità di pianificazione impostate.