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 aoneway
y, luego, se llama a una que no esoneway
, el servidor puede ejecutar la llamada aoneway
y la que no esoneway
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 conReturn::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.