Subprocesos de modelos

Los métodos marcados como oneway no se bloquean. En el caso de los métodos que no están marcados como oneway, la llamada al método de un cliente se bloquea hasta que el servidor completa la ejecución o llama a una devolución de llamada síncrona (lo que ocurra primero). Las implementaciones de métodos del servidor pueden llamar a una devolución de llamada síncrona como máximo. Las llamadas de devolución de llamada adicionales se descartan y se registran como errores. Si se supone que un método debe mostrar 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.

Subprocesos en el modo de transferencia

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

Subprocesos en HALs vinculados

Para entregar llamadas de RPC entrantes (incluidas las devoluciones de llamada asíncronas de los HAL a los usuarios de HAL) y notificaciones de error, se asocia un grupo de subprocesos con cada proceso que usa HIDL. Si un solo proceso implementa varias interfaces HIDL o controladores de notificaciones de falla, su grupo de subprocesos se comparte entre todos ellos. Cuando un proceso recibe una llamada de método entrante de un cliente, elige un subproceso libre del grupo de subprocesos y ejecuta la llamada en ese subproceso. Si no hay un subproceso libre disponible, se bloquea hasta que haya uno.

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 fuera de orden, incluso si el cliente solo tiene un subproceso. Sin embargo, para un objeto de interfaz determinado, se garantiza que las llamadas a oneway estén ordenadas (consulta el modelo de subprocesos del servidor). En el caso de un servidor multiproceso que alberga varias interfaces, las llamadas de oneway a diferentes interfaces se pueden procesar de forma simultánea entre sí o con otras llamadas de bloqueo.

Se envían varias llamadas anidadas en el mismo subproceso de hwbinder. Por ejemplo, si un proceso (A) realiza una llamada síncrona desde un subproceso de hwbinder al proceso (B) y, luego, el proceso (B) realiza una llamada síncrona al proceso (A), la llamada se ejecuta en el subproceso de hwbinder original en (A), que está bloqueado en la llamada original. Esta optimización permite tener un servidor de subproceso único capaz de controlar llamadas anidadas, pero no se extiende a los casos en los que las llamadas pasan por otra secuencia de llamadas a 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 por el modo de transferencia, las implementaciones de servidor de las interfaces HIDL se ejecutan en un proceso diferente al del cliente y necesitan uno o más subprocesos que esperan llamadas de método entrantes. Estos subprocesos son el grupo de subprocesos del servidor. El servidor puede decidir cuántos subprocesos quiere que se ejecuten 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 con cuidado).

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

Un solo subproceso de ejecución del cliente puede provocar una ejecución simultánea en un servidor con varios subprocesos de dos maneras:

  • Las llamadas oneway no se bloquean. Si se ejecuta una llamada a oneway y, luego, se llama a una que no es oneway, el servidor puede ejecutar la llamada a oneway y la que no es oneway de forma simultánea.
  • Los métodos del servidor que pasan datos con devoluciones de llamada síncronas pueden desbloquear el cliente en cuanto se llama a la devolución de llamada desde el servidor.

En el segundo caso, cualquier código de la función del servidor que se ejecute después de que se llame a la devolución de llamada se puede ejecutar de forma simultánea, y el servidor controla las llamadas posteriores del cliente. Esto incluye el código en la función del servidor y los 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 simultaneidad, incluso si las llamadas provienen de un solo subproceso de cliente. (Si algún HAL que entrega un proceso necesita varios subprocesos, todos los HAL tienen varios subprocesos porque el grupo de subprocesos se comparte por proceso).

En cuanto el servidor llame 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 continúa en paralelo con 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 que la devolución de llamada ya no bloquea al cliente (siempre que el grupo de subprocesos del servidor tenga suficientes subprocesos para controlar las llamadas entrantes), pero se puede ejecutar de forma simultánea 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 síncronas, un servidor con varios subprocesos en su grupo de subprocesos puede controlar de forma simultánea las llamadas oneway de un cliente de un solo subproceso, pero solo si esas llamadas oneway se ejecutan en diferentes interfaces. Las llamadas a oneway en la misma interfaz siempre se serializan.

Nota: Te recomendamos que las funciones del servidor se muestren apenas llamen 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 is called,
    // and the client resumes execution.
    ...
    return Void(); // is basically a no-op
};

Modelo de subprocesos del cliente

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

Bloquear llamadas

En el caso de las llamadas de bloqueo, el cliente bloquea hasta que ocurre una de las siguientes acciones:

  • 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 hay).
  • La implementación del servidor muestra un valor (si no había un 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 se muestre la función. La devolución de llamada se ejecuta en el mismo subproceso en el que se realiza la llamada a la función, por lo que los implementadores deben tener cuidado con la retención de bloqueos durante las llamadas a funciones (y evitarlas por completo cuando sea posible). Una función sin una sentencia generates o una palabra clave oneway sigue bloqueando; el cliente bloquea hasta que el servidor muestra un objeto Return<void>.

Llamadas unidireccionales

Cuando una función se marca como oneway, el cliente se muestra de inmediato y no espera a que el servidor complete la invocación de la llamada a función. En la superficie (y en conjunto), esto significa que la llamada a función tarda la mitad del tiempo porque ejecuta la mitad del código, pero cuando se escriben implementaciones sensibles al rendimiento, esto tiene algunas implicaciones de programación. Por lo general, el uso de una llamada unidireccional hace que el llamador siga programándose, mientras que el uso de una llamada síncrona normal hace que el programador transfiera inmediatamente del llamador al proceso del llamado. Esta es una optimización de rendimiento en el vinculador. En el caso de los servicios en los que la llamada unidireccional se debe ejecutar en el proceso de destino con una prioridad alta, se puede cambiar la política de programación del servicio receptor. En C++, usar el 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, en la política y prioridad de programación establecidas.