Modelos de roscado

Los métodos marcados como oneway no bloquean. Para los métodos no marcados como oneway , la llamada al método del cliente se bloqueará hasta que el servidor haya completado la ejecución o haya llamado a una devolución de llamada sincrónica (lo que ocurra primero). Las implementaciones de métodos de servidor pueden llamar como máximo a una devolución de llamada sincrónica; Las llamadas de devolución de llamada adicionales se descartan y se registran como errores. Si se supone que un método debe devolver valores a través de una devolución de llamada y no llama a su devolución de llamada, esto se registra como un error y se informa como un error de transporte al cliente.

Hilos en modo passthrough

En el modo de transferencia, la mayoría de las llamadas son sincrónicas. Sin embargo, para preservar el comportamiento previsto de que las llamadas oneway no bloqueen al cliente, se crea un hilo para cada proceso. Para obtener más información, consulte la descripción general de HIDL .

Hilos en HAL enlazados

Para atender llamadas RPC entrantes (incluidas devoluciones de llamadas asincrónicas de HAL a usuarios de HAL) y notificaciones de muerte, se asocia un grupo de subprocesos con cada proceso que utiliza HIDL. Si un solo proceso implementa múltiples interfaces HIDL y/o controladores de notificación de muerte, su grupo de subprocesos se comparte entre todos ellos. Cuando un proceso recibe una llamada entrante a un método de un cliente, elige un subproceso libre del grupo de subprocesos y ejecuta la llamada en ese subproceso. Si no hay ningún hilo libre disponible, se bloquea hasta que haya uno disponible.

Si el servidor tiene un solo subproceso, las llamadas al servidor se completan en orden. Un servidor con más de un subproceso puede completar llamadas desordenadas incluso si el cliente tiene un solo subproceso. Sin embargo, para un objeto de interfaz determinado, se garantiza que las llamadas oneway estarán ordenadas (consulte Modelo de subprocesamiento del servidor ). Para un servidor multiproceso que aloja múltiples interfaces, las llamadas oneway a diferentes interfaces pueden procesarse simultáneamente entre sí o con otras llamadas de bloqueo.

Se enviarán varias llamadas anidadas en el mismo hilo de hwbinder. Por ejemplo, si un proceso (A) realiza una llamada sincrónica desde un subproceso hwbinder al proceso (B), y luego el proceso (B) realiza una llamada síncrona al proceso (A), la llamada se ejecutará en el subproceso hwbinder original. en (A) que está bloqueado en la llamada original. Esta optimización hace posible tener un servidor de subproceso único capaz de manejar llamadas anidadas, pero no se extiende a los casos en los que las llamadas viajan a través de otra secuencia de llamadas IPC. Por ejemplo, si el proceso (B) realizó una llamada a binder/vndbinder que llamó a un proceso (C) y luego el proceso (C) volvió a llamar a (A), no se puede entregar en el subproceso original en (A).

Modelo de subprocesos del servidor

Excepto en el modo de paso a través, las implementaciones de servidor de interfaces HIDL viven en un proceso diferente al del cliente y necesitan uno o más subprocesos esperando llamadas entrantes a métodos. Estos subprocesos son el grupo de subprocesos del servidor; el servidor puede decidir cuántos subprocesos desea ejecutar en su grupo de subprocesos y puede usar un tamaño de grupo de subprocesos de uno para serializar todas las llamadas en sus interfaces. Si el servidor tiene más de un subproceso en el grupo de subprocesos, puede recibir llamadas entrantes simultáneas en cualquiera de sus interfaces (en C++, esto significa que los datos compartidos deben bloquearse cuidadosamente).

Las llamadas unidireccionales a la misma interfaz se serializan. Si un cliente multiproceso llama method1 y method2 en la interfaz IFoo y method3 en la interfaz IBar , method1 y method2 siempre se serializarán, pero method3 puede ejecutarse en paralelo con method1 y method2 .

Un único subproceso de ejecución de cliente puede provocar una ejecución simultánea en un servidor con múltiples subprocesos de dos maneras:

  • Las llamadas oneway no se bloquean. Si se ejecuta una llamada oneway y luego se llama una no oneway , el servidor puede ejecutar la llamada oneway y la llamada no oneway simultáneamente.
  • Los métodos de servidor que devuelven datos con devoluciones de llamada sincrónicas pueden desbloquear al cliente tan pronto como se llama a la devolución de llamada desde el servidor.

Para la segunda forma, cualquier código en la función del servidor que se ejecute después de llamar a la devolución de llamada puede ejecutarse simultáneamente, y el servidor manejará las llamadas posteriores del cliente. Esto incluye código en la función del servidor y destructores automáticos que se ejecutan al final de la función. Si el servidor tiene más de un subproceso en su grupo de subprocesos, surgen problemas de concurrencia incluso si las llamadas provienen de un solo subproceso de cliente. (Si cualquier HAL servido por un proceso necesita varios subprocesos, todos los HAL tendrán varios subprocesos porque el grupo de subprocesos se comparte por proceso).

Tan pronto como el servidor llama a la devolución de llamada proporcionada, el transporte puede llamar a la devolución de llamada implementada en el cliente y desbloquearlo. El cliente procede en paralelo con todo lo que hace la implementación del servidor después de llamar a la devolución de llamada (que puede incluir la ejecución de destructores). El código en la función del servidor después de la devolución de llamada ya no bloquea al cliente (siempre que el grupo de subprocesos del servidor tenga suficientes subprocesos para manejar las llamadas entrantes), pero puede ejecutarse simultáneamente con llamadas futuras del cliente (a menos que el grupo de subprocesos del servidor tenga solo un subproceso). ).

Además de las devoluciones de llamada sincrónicas, las llamadas oneway desde un cliente de un solo subproceso pueden ser manejadas simultáneamente por un servidor con múltiples subprocesos en su grupo de subprocesos, pero solo si esas llamadas oneway se ejecutan en diferentes interfaces. Las llamadas oneway en la misma interfaz siempre se serializan.

Nota: Recomendamos encarecidamente que las funciones del servidor regresen tan pronto como hayan llamado a la función de devolución de llamada.

Por ejemplo (en 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
};

Modelo de subprocesos del cliente

El modelo de subprocesamiento en el cliente difiere entre llamadas sin bloqueo (funciones que están marcadas con la palabra clave oneway ) y llamadas con bloqueo (funciones que no tienen la palabra clave oneway especificada).

Bloquear llamadas

Para bloquear llamadas, el cliente bloquea hasta que ocurra una de las siguientes situaciones:

  • Se produce un error de transporte; El objeto Return contiene un estado de error que se puede recuperar con Return::isOk() .
  • La implementación del servidor llama a la devolución de llamada (si la hubiera).
  • La implementación del servidor devuelve un valor (si no había ningún parámetro de devolución de llamada).

En caso de éxito, el servidor siempre llama a la función de devolución de llamada que el cliente pasa como argumento antes de que regrese la función. La devolución de llamada se ejecuta en el mismo hilo en el que se realiza la llamada a la función, por lo que los implementadores deben tener cuidado al mantener bloqueos durante las llamadas a la función (y evitarlos por completo cuando sea posible). Una función sin una declaración generates o una palabra clave oneway todavía está bloqueando; el cliente bloquea hasta que el servidor devuelve un objeto Return<void> .

llamadas unidireccionales

Cuando una función está marcada oneway , el cliente regresa inmediatamente y no espera a que el servidor complete su invocación de llamada a función. En la superficie (y en conjunto), esto significa que la llamada a la función toma la mitad del tiempo porque ejecuta la mitad del código, pero cuando se escriben implementaciones que son sensibles al rendimiento, esto tiene algunas implicaciones de programación. Normalmente, el uso de una llamada unidireccional hace que la persona que llama continúe programada, mientras que el uso de una llamada sincrónica normal hace que el programador se transfiera inmediatamente del proceso de la persona que llama al destinatario. Esta es una optimización del rendimiento en Binder. Para servicios donde la llamada unidireccional debe ejecutarse en el proceso de destino con alta prioridad, se puede cambiar la política de programación del servicio receptor. En C++, el uso del método setMinSchedulerPolicy de libhidltransport con las prioridades y políticas del programador definidas en sched.h garantiza que todas las llamadas al servicio se ejecuten al menos con la política y prioridad de programación establecidas.