Os métodos marcados como oneway
não são bloqueados. Para métodos não marcados como
oneway
, a chamada de método de um cliente é bloqueada até que o servidor tenha
concluído a execução ou tenha chamado um callback síncrono (o que ocorrer primeiro).
As implementações de método do servidor podem chamar no máximo um callback síncrono. As chamadas
de callback extras são descartadas e registradas como erros. Se um método precisar
retornar valores por callback e não chamar o callback, isso será registrado como um
erro e informado como um erro de transporte ao cliente.
Linhas de execução no modo de passagem
No modo de transferência, a maioria das chamadas é síncrona. No entanto, para preservar o
comportamento esperado de que as chamadas oneway
não bloqueiem o cliente, uma
linha de execução é criada para cada processo. Para mais detalhes, consulte a
visão geral de HIDL.
Linhas de execução em HALs de vinculação
Para atender chamadas RPC recebidas (incluindo callbacks assíncronos de HALs para usuários de HAL) e notificações de morte, um threadpool é associado a cada processo que usa HIDL. Se um único processo implementar várias interfaces HIDL e/ou gerenciadores de notificação de morte, o threadpool dele será compartilhado entre todos eles. Quando um processo recebe uma chamada de método de entrada de um cliente, ele escolhe uma linha de execução disponível do conjunto de linhas de execução e executa a chamada nessa linha de execução. Se nenhuma linha de execução sem custo financeiro estiver disponível, ela será bloqueada até que uma esteja.
Se o servidor tiver apenas uma linha de execução, as chamadas para o servidor serão concluídas
na ordem. Um servidor com mais de uma linha de execução pode concluir chamadas fora de ordem,
mesmo que o cliente tenha apenas uma linha de execução. No entanto, para um determinado objeto de interface,
as chamadas oneway
são garantidas para serem ordenadas (consulte
Modelo de linha de execução do servidor). Para um servidor multithread que
hospeda várias interfaces, as chamadas oneway
para interfaces diferentes
podem ser processadas simultaneamente ou outras chamadas de bloqueio.
Várias chamadas aninhadas são enviadas na mesma linha de execução do hwbinder. Por exemplo, se um processo (A) faz uma chamada síncrona de uma linha de execução do hwbinder para o processo (B), e o processo (B) faz uma chamada síncrona de volta para o processo (A), a chamada é executada na linha de execução original do hwbinder em (A), que é bloqueada na chamada original. Essa otimização possibilita que um servidor de linha única seja capaz de processar chamadas aninhadas, mas não se estende aos casos em que as chamadas passam por outra sequência de chamadas IPC. Por exemplo, se o processo (B) fez uma chamada binder/vndbinder que chamou um processo (C) e, em seguida, o processo (C) chamou de volta para (A), ele não pode ser atendido na linha de execução original em (A).
Modelo de encadeamento do servidor
Exceto no modo de transferência, as implementações do servidor de interfaces HIDL ficam em um processo diferente do cliente e precisam de uma ou mais linhas de execução aguardando chamadas de método recebidas. Essas linhas de execução são o pool de linhas de execução do servidor. O servidor pode decidir quantas linhas de execução ele quer executar no pool e pode usar um tamanho de pool de linhas de execução de um para serializar todas as chamadas nas interfaces. Se o servidor tiver mais de uma linha de execução no pool de linhas de execução, ele poderá receber chamadas entrantes simultâneas em qualquer uma das interfaces. Em C++, isso significa que os dados compartilhados precisam ser bloqueados com cuidado.
As chamadas unidirecionais para a mesma interface são serializadas. Se um cliente multithread
chamar method1
e method2
na interface
IFoo
e method3
na interface IBar
,
method1
e method2
serão sempre serializados, mas
method3
poderá ser executado em paralelo com method1
e
method2
.
Uma única linha de execução do cliente pode causar execução simultânea em um servidor com várias linhas de duas maneiras:
- As chamadas para
oneway
não são bloqueadas. Se uma chamadaoneway
for executada e uma chamada nãooneway
for chamada, o servidor poderá executar a chamadaoneway
e a chamada nãooneway
simultaneamente. - Os métodos do servidor que transmitem dados de volta com callbacks síncronos podem desbloquear o cliente assim que o callback é chamado pelo servidor.
Na segunda maneira, qualquer código na função do servidor que é executado após a chamada do callback pode ser executado simultaneamente, com o servidor processando chamadas posteriores do cliente. Isso inclui o código na função do servidor e os destrutores automáticos que são executados no final da função. Se o servidor tiver mais de uma linha de execução no thread pool, problemas de simultaneidade vão surgir mesmo que as chamadas estejam chegando de apenas uma única linha de execução do cliente. Se qualquer HAL atendido por um processo precisar de várias linhas de execução, todas as HALs terão várias linhas de execução porque o pool de linhas de execução é compartilhado por processo.
Assim que o servidor chamar o callback fornecido, o transporte poderá chamar o callback implementado no cliente e desbloqueá-lo. O cliente prossegue em paralelo com o que a implementação do servidor faz depois de chamar o callback, o que pode incluir a execução de destrutores. O código na função do servidor depois que o callback não estiver mais bloqueando o cliente (desde que o pool de linhas de execução do servidor tenha linhas de execução suficientes para processar chamadas de entrada), mas pode ser executado simultaneamente com chamadas futuras do cliente, a menos que o pool de linhas de execução do servidor tenha apenas uma linha de execução.
Além de callbacks síncronos, as chamadas oneway
de um
cliente de linha de execução única podem ser processadas simultaneamente por um servidor com várias
linhas de execução no pool de linhas de execução, mas somente se essas chamadas oneway
forem
executadas em interfaces diferentes. As chamadas oneway
na mesma
interface são sempre serializadas.
Observação:recomendamos que as funções do servidor sejam retornadas assim que chamarem a função de callback.
Por exemplo (em 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 encadeamento do cliente
O modelo de linhas de execução no cliente difere entre chamadas não bloqueantes
(funções marcadas com a palavra-chave oneway
) e chamadas
de bloqueio (funções que não têm a palavra-chave oneway
especificada).
Bloquear chamadas
Para chamadas de bloqueio, o cliente bloqueia até que uma das seguintes situações ocorra:
- Ocorre um erro de transporte. O objeto
Return
contém um estado de erro que pode ser recuperado comReturn::isOk()
. - A implementação do servidor chama o callback (se houver um).
- A implementação do servidor retorna um valor (se não houver um parâmetro de callback).
Em caso de sucesso, a função de callback que o cliente transmite como um argumento é
sempre chamada pelo servidor antes que a própria função seja retornada. O callback é
executado na mesma linha de execução em que a chamada de função é feita. Portanto, os implementadores
precisam ter cuidado ao manter bloqueios durante as chamadas de função e evitá-los
por completo quando possível. Uma função sem uma instrução generates
ou uma palavra-chave oneway
ainda está bloqueando. O cliente é bloqueado até que o
servidor retorne um objeto Return<void>
.
Chamadas unidirecionais
Quando uma função é marcada como oneway
, o cliente retorna imediatamente
e não espera que o servidor conclua a invocação da chamada de função. Na
superfície (e no agregado), isso significa que a chamada de função leva metade do
tempo porque está executando metade do código, mas, ao escrever implementações que
são sensíveis à performance, isso tem algumas implicações de programação. Normalmente,
usar uma chamada unidirecional faz com que o autor da chamada continue sendo programado, enquanto
usar uma chamada síncrona normal faz com que o programador seja transferido imediatamente
do autor da chamada para o processo de chamada. Essa é uma otimização de desempenho no
binder. Para serviços em que a chamada unidirecional precisa ser executada no processo de destino
com alta prioridade, a política de programação do serviço de recebimento pode ser
alterada. Em C++, o uso do método
setMinSchedulerPolicy
de libhidltransport
com as prioridades e políticas do programador
definidas em sched.h
garante que todas as chamadas para o serviço sejam executadas
pelo menos na política e prioridade de programação definidas.