I'm not sure about DCOM's ping system, but one option for you is to simply turn off notifications to a separate thread pool. This will help mitigate the effect of having a small number of blocking clients - you will have problems if there are too many of them, of course.
An easy way to do this is to use QueueUserWorkItem - this will call the passed callback in the application thread pool. Assuming you are using MTA, this is all you need to do:
static InfoStruct { IRemoteHost *pRemote; BSTR someData; }; static DWORD WINAPI InvokeClientAsync(LPVOID lpInfo) { CoInitializeEx(COINIT_MULTITHREADED); InfoStruct *is = (InfoStruct *)lpInfo; is->pRemote->notify(someData); is->pRemote->Release(); SysFreeString(is->someData); delete is; CoUninitialize(); return 0; } void InvokeClient(IRemoteHost *pRemote, BSTR someData) { InfoStruct *is = new InfoStruct; is->pRemote = pRemote; pRemote->AddRef(); is->someData = SysAllocString(someData); QueueUserWorkItem(InvokeClientAsync, (LPVOID)is, WT_EXECUTELONGFUNCTION); }
If your main thread is in an STA, this is a little more complicated; you just need to use CoMarshalInterThreadInterfaceInStream and CoGetInterfaceAndReleaseStream to pass the interface pointer between the apartments:
static InfoStruct { IStream *pMarshalledRemote; BSTR someData; }; static DWORD WINAPI InvokeClientAsync(LPVOID lpInfo) { CoInitializeEx(COINIT_MULTITHREADED); // can be STA as well InfoStruct *is = (InfoStruct *)lpInfo; IRemoteHost *pRemote; CoGetInterfaceAndReleaseStream(is->pMarshalledRemote, __uuidof(IRemoteHost), (LPVOID *)&pRemote); pRemote->notify(someData); pRemote->Release(); SysFreeString(is->someData); delete is; CoUninitialize(); return 0; } void InvokeClient(IRemoteHost *pRemote, BSTR someData) { InfoStruct *is = new InfoStruct; CoMarshalInterThreadInterfaceInStream(__uuidof(IRemoteHost), pRemote, &is->pMarshalledRemote); is->someData = SysAllocString(someData); QueueUserWorkItem(InvokeClientAsync, (LPVOID)is, WT_EXECUTELONGFUNCTION); }
Please note that error checking has been eliminated for clarity - you, of course, want the error to check all calls - in particular, you want to check for RPC_S_SERVER_UNAVAILABLE and other similar network errors and remove offensive clients.
Some of the more complex options that you might want to consider include providing only one in-flight request per client at a time (thus further reducing the impact of a stuck client) and caching the MTA marshalling interface pointer (if your main thread - it STA) - as I believe that CoMarshalInterThreadInterfaceInStream can perform network requests, you ideally want to take care of it ahead of time, when you know that the client is connected, rather than risking your main lock on stream.
bdonlan
source share