How should async TAPI request timeouts be exposed to ATAPI users?

Sep 26, 2013 at 12:30 AM
I'm doing some work with Cisco's UCM TAPI provider involving the XML IP Phone services.
The model for using the CISCO TAPI provider goes something like this:
  • phone.NegotiateExtensions
  • phone.Open
  • Request the ability to receive PHONE_DEVSPECIFIC responses to XML requests to the phone. (via phone.DevSpecific)
  • Send the actual message via phone.DevSpecific()
    NOTE: The PHONE_REPLY message for async completion of the TAPI API call here may never occur.
  • Receive a PHONE_DEVSPECIFIC message indicating the need to call phoneGetStatus() in an OnPhoneDevSpecific event handler.
  • Examine phone.Status.DeviceSpecificData to decode a possible XML response.
What fun this is! :)
Additional ATAPI correctness note:
ProcessTapiMessages doesn't garbage collect PendingRequests that take too long. (i.e. uncontrolled memory growth).
This means that IAsyncResults never get completed. (which is worse than the uncontrolled memory growth since entire threads will be blocked forever).

My initial take on this was to extend PendingRequest to add:
  • A TimedOut property that returns true if the request timed out.
    NOTE; The TimedOut property can also be exposed via a subclass of IAsyncResults "ITapiAsyncResult".
  • A TimeoutDuration TimeSpan property that is the length of the timeout.
  • An internal CancelRequest() method that:
    • sets TimedOut to true,
    • sets .Result to TAPIERR_REQUESTFAILED (for lack of a better error code)
    • stops the Stopwatch
    • Signals the WaitHandle
    • Calls the callback
ProcessTapiMessages would be altered to:
  • Every 5 seconds call HandleCancellations() which would call CancelRequest() on timed out requests and removing it from the list of pending requests.
This would allow folks to decide on how they would like to expose timeouts to callers of methods like phone.DevSpecific() or what have you.

One option would be an overload pattern of: objectInstance.Method(<args>, <msTimeout>);
If the operation times out you could throw a new TapiTimeoutException instance.

This is what I've done for simplicity for the use case I described above. However, I could have just as easily added BeginDevSpecific EndDevSpecific methods instead.

Thoughts?

See the patch I just uploaded to show you what I had in mind. The patch defaults TAPI operation timeouts to 1 minute, but it could just as easily be -1 milliseconds to block forever.

Thanks,
Bill