Say you invoke a cross-process COM method, and then you decide to cancel it, say, because it’s taking too long.
What you definitely don’t want to do is call TerminateÂThread
. That way lies madness.
What you can do is enable call cancellation by calling CoÂEnableÂCallÂCancellation
, then make the potentially-hanging cross-process COM method call, and then after the call returns, call CoÂDisableÂCallÂCancellation
to return things to the way they were.
Meanwhile, you can set a timer, and if the timer fires, the handler can call CoÂCancelÂCall
with the thread ID of the thread that is making the potentially-hanging cross-process COM method call. If the potentially-hanging cross-process COM method call returns, then cancel the timer, since you don’t need it any more.
But wait, the documentation for the ICancelMethodCalls::
Cancel
method says, “The behavior of the cancel object on receiving a cancel request is entirely at the discretion of the implementer.” What if the server never calls CoÂTestÂCancel
, or it pointedly ignores cancel requests? Is this whole exercise pointless?
While it’s true that the Cancel
method could do whatever the implementation wants, it’s also the case that CoÂGetÂCancelÂObject
will return a standard cancellation object for a cross-process call. And the system provides that implementation.
The system implementation issues a cancel request to the remote process and waits up to the specified timeout for the remote process to complete the call. Issuing the cancel request is what makes CoÂTestÂCancel
report that a cancellation was requested, and if you’re lucky, the remote process will abandon whatever it was doing and return back to you quickly.
If you’re not lucky, the remote process will ignore the cancellation request, and after the timeout period elapses, the CoÂCancelÂCall
function will force the original COM method call to return some cancellation error code, I forget exactly which. (You can pass a time of zero to issue the cancel request and not bother waiting for any sort of acknowledgement.)
If the remote server never calls CoÂTestÂCancel
, that’s fine. The server just keeps on going, unaware that its caller has given up waiting for the result.
Supplementary reading: Ready… cancel… wait for it!
Bonus chatter: There is a race condition if the timer fires before your original thread can manage to initiate the call at all. In that case, you’ll try to cancel a nonexistent operation. You probably want to wait a little while and retry the cancellation, in the hope that the main thread has finally gotten around to issuing the call that you are trying to cancel.
What I do is schedule a recurring timer, and each time the timer fires, attempt to cancel. Eventually, one of them will succeed, and then the main thread can cancel the periodic timer. Even after the successful cancellation, I keep cancelling, because the main thread might be performing a series of operations, and I need to cancel all of them.
I can understand the temptation to call TerminateThread if you know it’s going to consume resources unnecessarily for an indefinite period. The proper solution is always periodically checking for a cancel and not using a library that doesn’t, but sometimes mistakes happen, and compounding them may seem like the only option.
Do it from extra process then, not from extra thread.
Perhaps this logic should be wrapped up in a system provided function or perhaps support for asynchronous calls along the lines of IAsync from WinRT.