Requisitos de tempo de vida do objeto NDKPI
Como os objetos NDK são criados, usados e fechados
Um consumidor do NDK inicia uma solicitação de criação para um objeto NDK chamando a função de criação do provedor NDK para esse objeto.
Quando o consumidor chama uma função create, ele passa um NdkCreateCompletion (NDK_FN_CREATE_COMPLETION) como um parâmetro.
O consumidor inicia várias solicitações chamando funções de provedor na tabela de expedição do objeto, passando um retorno de chamada de conclusão NdkRequestCompletion (NDK_FN_REQUEST_COMPLETION) como um parâmetro.
Quando um objeto não é mais necessário, o consumidor chama a função NdkCloseObject (NDK_FN_CLOSE_OBJECT) do provedor para iniciar uma solicitação de fechamento para o objeto, passando um retorno de chamada NdkCloseCompletion (NDK_FN_CLOSE_COMPLETION) como um parâmetro.
O provedor chama a função de retorno de chamada do consumidor para concluir a solicitação de forma assíncrona. Essa chamada indica ao consumidor que o provedor concluiu a operação (por exemplo, fechando o objeto) e está retornando o controle para o consumidor. Se o provedor concluir a solicitação de fechamento de forma síncrona, com êxito ou com erro, ele não chamará a função de retorno de chamada do consumidor.
As regras para retornos de chamada de conclusão
Quando um provedor tiver criado um objeto a pedido de um consumidor, o provedor chamará o retorno de chamada NdkCreateCompletion do consumidor para indicar que o objeto está pronto para uso.
O consumidor pode chamar outras funções de provedor para o mesmo objeto sem esperar que o primeiro retorno de chamada retorne.
O consumidor não chamará a função NdkCloseObject para um objeto até que todas as funções de provedor desse objeto tenham retornado.
No entanto, se a função de provedor iniciar uma solicitação de conclusão, o consumidor estará livre para chamar NdkCloseObject de dentro desse retorno de chamada de conclusão, mesmo que a função de provedor não tenha retornado.
Uma função de provedor pode iniciar uma solicitação de conclusão antes de retornar de um retorno de chamada fazendo um dos seguintes procedimentos:
- Chamando o retorno de chamada de conclusão diretamente
- Enfileirando a solicitação de conclusão para outro thread
Ao iniciar uma solicitação de conclusão, o provedor retorna efetivamente o controle ao consumidor. O provedor deve assumir que o objeto pode ser fechado a qualquer momento após o provedor iniciar a solicitação de conclusão.
Nota Para evitar deadlock após iniciar uma solicitação de conclusão, o provedor deve:
- Não execute outras operações no objeto até que o retorno de chamada de conclusão retorne.
- Tome as medidas necessárias para manter o objeto intacto, se o provedor precisar absolutamente tocar no objeto.
Exemplo: interação Consumer-Provider
Considere o cenário a seguir.
- O consumidor cria um conector (NDK_CONNECTOR) e chama NdkConnect (NDK_FN_CONNECT).
- O provedor processa a solicitação de conexão, atinge uma falha e chama o retorno de chamada de conclusão do consumidor no contexto da chamada NdkConnect (em vez de retornar uma falha embutida devido a uma opção de implementação interna).
- O consumidor chama NdkCloseObject no contexto desse retorno de chamada de conclusão, embora a chamada de NdkConnect ainda não tenha retornado ao consumidor.
Para evitar deadlock, o provedor não deve tocar no objeto do conector após a etapa 2 (o ponto em que iniciou o retorno de chamada de conclusão dentro da chamada NdkConnect ).
Fechando objetos antecedentes e sucessores
O provedor deve estar preparado para que o consumidor chame a função NdkCloseObject para fechar um objeto antecedente antes que o consumidor chame NdkCloseObject para objetos sucessores. Se o consumidor fizer isso, veja o que o provedor deve fazer:
- O provedor não deve fechar o objeto antecedente até que todos os objetos sucessores sejam fechados, ou seja, o provedor deve retornar STATUS_PENDING da solicitação de fechamento e concluí-lo (chamando a função NdkCloseCompletion registrada para a solicitação de fechamento) depois que todos os objetos sucessores forem fechados.
- O consumidor não usará o objeto antecessor depois de chamar NdkCloseObject nele, portanto, o provedor não precisa adicionar nenhum tratamento para falhas de funções de provedor adicionais no objeto antecedente (mas pode ser se ele optar por fazê-lo).
- O provedor pode tratar a solicitação próxima como uma simples desreferência que não tem outro efeito colateral até que o último objeto sucessor seja fechado, a menos que seja necessário de outra forma (consulte o caso de fechamento do ouvinte do NDK abaixo, que tem um efeito colateral necessário).
O provedor não deve concluir a solicitação de fechamento em um objeto antecessor (incluindo o NDK_ADAPTER solicitação de fechamento) antes que qualquer retorno de chamada de conclusão de fechamento em andamento em qualquer objeto sucessor retorne ao provedor. Isso é para permitir que os consumidores do NDK descarreguem com segurança.
Um consumidor do NDK não chamará NdkCloseObject para um objeto NDK_ADAPTER (que é uma chamada de bloqueio) de dentro de uma função de retorno de chamada do consumidor.
Fechando objetos do adaptador
Considere o cenário a seguir.
- O consumidor chama NdkCloseObject em um objeto CQ (fila de conclusão).
- O provedor retorna STATUS_PENDING e, posteriormente, chama o retorno de chamada de conclusão do consumidor.
- Dentro desse retorno de chamada de conclusão, o consumidor sinaliza um evento de que agora está tudo bem fechar o NDK_ADAPTER.
- Outro thread é ativado nesse sinal e fecha o NDK_ADAPTER e continua a ser descarregado.
- No entanto, o thread no qual o retorno de chamada de conclusão do CQ do consumidor foi chamado ainda pode estar dentro da função de retorno de chamada do consumidor (por exemplo, o epílogo da função), portanto, não é seguro para o driver do consumidor descarregar.
- Como o contexto de retorno de chamada de conclusão é o único contexto em que o consumidor pode sinalizar o evento, o driver do consumidor não pode resolver o problema de descarregamento seguro em si.
Deve haver um ponto em que o consumidor possa ter certeza de que todos os seus retornos de chamada retornaram o controle. No NDKPI, esse ponto é quando a solicitação de fechamento em um NDK_ADAPTER retorna o controle . Observe que NDK_ADAPTER solicitação de fechamento é uma chamada de bloqueio. Quando uma solicitação de fechamento NDK_ADAPTER retorna, é garantido que todos os retornos de chamada em todos os objetos descendentes desse objeto NDK_ADAPTER retornaram o controle para o provedor.
Concluindo solicitações de fechamento
O provedor não deve concluir uma solicitação de fechamento em um objeto até:
- Todas as solicitações assíncronas pendentes no objeto foram concluídas (em outras palavras, seus retornos de chamada de conclusão retornaram ao provedor).
- Todos os retornos de chamada de evento do consumidor (por exemplo, NdkCqNotificationCallback (NDK_FN_CQ_NOTIFICATION_CALLBACK) em um CQ, NdkConnectEventCallback (NDK_FN_CONNECT_EVENT_CALLBACK) em um ouvinte) retornaram ao provedor.
O provedor deve garantir que nenhum retorno de chamada ocorrerá depois que o retorno de chamada de conclusão de fechamento for chamado ou depois que a solicitação de fechamento retornar STATUS_SUCCESS. Observe que uma solicitação próxima também deve iniciar qualquer liberação ou cancelamento necessário de solicitações assíncronas pendentes.
Nota Segue logicamente do acima que um consumidor do NDK não deve chamar NdkCloseObject para um objeto NDK_ADAPTER (que é uma chamada de bloqueio) de dentro de uma função de retorno de chamada do consumidor.