Auxiliares de implementação de servidor fora do processo
Quatro funções auxiliares que podem ser chamadas por servidores fora do processo estão disponíveis para simplificar o trabalho de escrever código do servidor. Clientes COM e servidores COM em processo normalmente não os chamariam. Essas funções são projetadas para ajudar a impedir condições de corrida na ativação do servidor quando os servidores têm vários apartamentos ou vários objetos de classe. Eles também podem, no entanto, ser usados facilmente para servidores de objeto de thread único e de classe única. As funções são as seguintes:
Para desligar corretamente, um servidor COM deve controlar quantas instâncias de objeto ele instanciou e quantas vezes seu método IClassFactory::LockServer foi chamado. Somente quando ambas as contagens chegarem a zero é que um servidor poderá ser desligado. Em servidores COM de thread único, a decisão de desligar era coordenada com solicitações de ativação de entrada, que eram serializadas pela fila de mensagens. O servidor, ao receber uma liberação em sua instância de objeto final e decidir desligar, revogaria seus objetos de classe antes que mais solicitações de ativação fossem despachadas. Se uma solicitação de ativação chegasse após esse ponto, o COM reconheceria que os objetos de classe foram revogados e retornaria um erro ao Gerenciador de Controle de Serviço (SCM), o que faria com que uma nova instância do processo do servidor local fosse executada.
No entanto, em um servidor modelo de apartamento, no qual diferentes objetos de classe são registrados em apartamentos diferentes e em todos os servidores de thread livre, essa decisão de desligar deve ser coordenada com solicitações de ativação em vários threads para que um thread do servidor não decida desligar enquanto outro thread do servidor está ocupado distribuindo objetos de classe ou instâncias de objeto. Uma abordagem clássica, mas complicada, para resolver isso é fazer com que o servidor, depois de revogar seus objetos de classe, verifique novamente sua contagem de instâncias e permaneça ativo até que todas as instâncias tenham sido liberadas.
Para tornar mais fácil para os gravadores de servidor lidar com esses tipos de condições de corrida, o COM fornece duas funções de contagem de referência:
- CoAddRefServerProcess incrementa uma contagem de referência global por processo.
- O CoReleaseServerProcess diminui a contagem de referência global por processo.
Quando a contagem de referência global por processo atinge zero, o COM chama automaticamente CoSuspendClassObjects, o que impede a entrada de novas solicitações de ativação. O servidor pode então cancelar o registro de seus vários objetos de classe de seus vários threads sem se preocupar que outra solicitação de ativação possa entrar. Todas as novas solicitações de ativação passam a ser tratadas pelo SCM que inicia uma nova instância do processo do servidor local.
A maneira mais simples para um aplicativo de servidor local fazer uso dessas funções é chamar CoAddRefServerProcess no construtor para cada um de seus objetos de instância e em cada um de seus métodos IClassFactory::LockServer quando o parâmetro fLock é TRUE. O aplicativo de servidor também deve chamar CoReleaseServerProcess no destruidor de cada um de seus objetos de instância e em cada um de seus métodos IClassFactory::LockServer quando o parâmetro fLock é FALSE.
Finalmente, o aplicativo de servidor deve prestar atenção ao código de retorno de CoReleaseServerProcess e, se retornar 0, o aplicativo de servidor deve iniciar sua limpeza, o que, para um servidor com vários threads, normalmente significa que ele deve sinalizar seus vários threads para sair de seus loops de mensagem e chamar CoAddRefServerProcess e CoReleaseServerProcess. Se as funções de gerenciamento do tempo de vida do processo do servidor forem usadas, elas deverão ser usadas nas instâncias do objeto e no método LockServer , caso contrário, o aplicativo do servidor poderá ser desligado prematuramente.
Quando uma solicitação CoGetClassObject é feita, COM entra em contato com o servidor, marshals a interface IClassFactory do objeto de classe, retorna ao processo do cliente, desmarshals a interface IClassFactory e retorna isso para o cliente. Neste ponto, os clientes normalmente chamam LockServer com TRUE para impedir que o processo do servidor seja desligado. No entanto, há uma janela de tempo entre quando o objeto de classe é empacotado e quando o cliente chama LockServer no qual outro cliente poderia se conectar ao mesmo servidor, obter uma instância e liberar essa instância, fazendo com que o servidor seja desligado e deixando o primeiro cliente alto e seco com um ponteiro IClassFactory desconectado. Para evitar essa condição de corrida, COM adiciona uma chamada implícita para LockServer com TRUE para o objeto de classe quando ele marshals a interface IClassFactory e uma chamada implícita para LockServer com FALSE quando o cliente libera a interface IClassFactory. Portanto, não é necessário fazer chamadas remotas do LockServer de volta para o servidor, e o proxy para o LockServer simplesmente retorna S_OK sem realmente remoting a chamada.
Há outra condição de corrida relacionada à ativação durante a inicialização de um processo de servidor fora do processo. Um servidor COM que registra várias classes normalmente chama CoRegisterClassObject com REGCLS_LOCAL_SERVER para cada CLSID que ele suporta. Depois de ter feito isso para todas as classes, o servidor entra em seu loop de mensagem. Para um servidor COM de thread único, todas as solicitações de ativação são bloqueadas até que o servidor entre no loop de mensagens. No entanto, para um servidor modelo de apartamento que registra objetos de classe diferentes em apartamentos diferentes e para todos os servidores de thread livre, as solicitações de ativação podem chegar antes disso. No caso de servidores modelo de apartamento, as solicitações de ativação podem chegar assim que qualquer thread entrar em seu loop de mensagens. No caso de servidores de thread livre, uma solicitação de ativação pode chegar assim que o primeiro objeto de classe é registrado. Como uma ativação pode acontecer tão cedo, também é possível que a versão final ocorra (e, portanto, faça com que o servidor comece a desligar) antes que o resto do servidor tenha tido a chance de concluir a inicialização.
Para eliminar essas condições de corrida e simplificar o trabalho do gravador do servidor, qualquer servidor que deseja registrar vários objetos de classe com COM deve chamar CoRegisterClassObject com REGCLS_LOCAL_SERVER | REGCLS_SUSPENDED para cada CLSID diferente que o servidor suporta. Depois que todas as classes tiverem sido registradas e o processo do servidor estiver pronto para aceitar solicitações de ativação de entrada, o servidor deverá fazer uma chamada para CoResumeClassObjects. Essa função diz ao COM para informar o SCM sobre todas as classes registradas e começa a permitir solicitações de ativação no processo do servidor. O uso dessas funções oferece as seguintes vantagens:
- Apenas uma chamada é feita para o SCM, independentemente de quantos CLSIDs estão registrados, reduzindo assim o tempo geral de registro (e, portanto, o tempo de inicialização do aplicativo servidor).
- Se o servidor tiver vários apartamentos e diferentes CLSIDs estiverem registrados em apartamentos diferentes, ou se o servidor for um servidor de thread livre, nenhuma solicitação de ativação será recebida até que o servidor chame CoResumeClassObjects, dando ao servidor a chance de registrar todos os seus CLSIDs e ser configurado corretamente antes de ter que lidar com solicitações de ativação e possíveis solicitações de desligamento.
Tópicos relacionados