Reputation: 561
On a web page, I am calling a third party who does not allow me to set timeout programatically. I call BeginInvoke and use the AsyncWaitHandle.WaitOne to wait a specified amount of time.
If the call times out, I move on and forget about the thread call I began. My question is, do I still have to call EndInvoke somehow in a timeout situation? The "CAUTION" remark on this MSDN page makes me wonder if I should: http://msdn.microsoft.com/en-us/library/2e08f6yc(VS.71).aspx
If you believe I should, then the next question is if my web page is done processing and returns to the client before the third party comes back, would the callback method even be there listening to run the code? Doesn't the server stop looking for activitiy once my request/response is done?
Here's the code I'm using:
public class RemotePaymentProcessor
{
private delegate string SendProcessPaymentDelegate(string creditCardNumber);
private string SendProcessPayment(string creditCardNumber)
{
string response = string.Empty;
// call web service
SlowResponseService.SlowResponseService srs = new WebServiceTimeout.SlowResponseService.SlowResponseService();
response = srs.GetSlowResponse(creditCardNumber);
return response;
}
public string ProcessPayment(string creditCardNumber, int timeoutMilliseconds)
{
string response = string.Empty;
SendProcessPaymentDelegate sppd = new SendProcessPaymentDelegate(SendProcessPayment);
IAsyncResult ar = sppd.BeginInvoke(creditCardNumber, null, new object());
if (!ar.AsyncWaitHandle.WaitOne(timeoutMilliseconds, false))
{
// Async call did not return before timeout
response = "TIMEOUT";
}
else
{
// Async call has returned - get response
response = sppd.EndInvoke(ar);
}
return response;
}
}
Upvotes: 9
Views: 5507
Reputation: 561
Well, I couldn't ignore the advice I saw everywhere that if I did not make sure to call EndInvoke, I MIGHT leak some resources, so I had to try to get that in to sleep at night and not worry I was approaching a cliff.
The solution I found used an async callback function. If the call returned in time, I call EndInvoke there. If not, I continue my button click, and let the async callback function clean up the mess with EndInvoke.
To answer my own question about a web app and "will anyone be there to listen after I time out and move on", I found that they will - even if I timed out and moved on, if I watched the later output, that async call will return and run the call back function, even if I've already returned output to the client.
I used some of what I found at: http://www.eggheadcafe.com/tutorials/aspnet/847c94bf-4b8d-4a66-9ae5-5b61f049019f/basics-make-any-method-c.aspx
...as well as combining with the callback stuff I found elsewhere. Here's a little sample function of what I did below. It combines some of what I found at Thanks for everyone's input!:
public class RemotePaymentProcessor
{
string currentResponse = string.Empty;
private delegate string SendProcessPaymentDelegate(string creditCardNumber);
private string SendProcessPayment(string creditCardNumber)
{
SlowResponseService.SlowResponseService srs = new WebServiceTimeout.SlowResponseService.SlowResponseService();
string response = srs.GetSlowResponse(creditCardNumber);
return response;
}
public string ProcessPayment(string creditCardNumber, int timeoutMilliseconds)
{
string response = string.Empty;
SendProcessPaymentDelegate sppd = new SendProcessPaymentDelegate(SendProcessPayment);
IAsyncResult ar = sppd.BeginInvoke(creditCardNumber, new AsyncCallback(TransactionReturned), sppd);
if (!ar.AsyncWaitHandle.WaitOne(timeoutMilliseconds, false))
{
// Async call did not return before timeout
response = "TIMEOUT";
}
else
{
// Async call has returned - get response
response = sppd.EndInvoke(ar);
}
currentResponse = response; // Set class variable
return response;
}
private void TransactionReturned(IAsyncResult ar)
{
string response = string.Empty;
// Get delegate back out of Async object
SendProcessPaymentDelegate sppd = (SendProcessPaymentDelegate)ar.AsyncState;
// Check outer class response status to see if call has already timed out
if(currentResponse.ToUpper().Equals("TIMEOUT"))
{
// EndInvoke has not yet been called so call it here, but do nothing with result
response = sppd.EndInvoke(ar);
}
else
{
// Transaction must have returned on time and EndInvoke has already been called. Do nothing.
}
}
}
Upvotes: 0
Reputation: 109140
For the general case of the .NET async pattern, calling EndXXX when you don't want to complete the operation started with BeingXXX will be a mistake because if EndXXX is called before the operation has completed it should block until it completes. Which isn't much help for a timeout.
A specific API might be different (e.g. WinForms explicitly doesn't require EndInvoke).
See §9.2 of "Framework Design Guidelines" (2nd ed). Or msdn.
Upvotes: 0
Reputation: 136663
Update:
Seems like you need to call EndInvoke always for an async call (unless its Control.BeginInvoke) or risk leaking resources.
Here's a discussion that is on the same lines. The solution suggested is to spawn a thread that will wait for the delegate to actually complete and call EndInvoke. However in case of a really Looong timeout I think that thread would just hang.
Its an edge case that doesn't seem to be documented that well... maybe coz timeouts aren't supposed to happen.. exceptional cases
Upvotes: 1
Reputation: 118905
It's possible that if you don't call EndInvoke, you'll leak some resource (allocated by BeginInvoke).
So to be totally safe, always call EndInvoke() (which will block, so do it on some background thread you don't need, or switch to passing a callback so as not to burn a thread while waiting).
In practice I don't know how often it matters (I think many AsyncResults will not leak, so you may get lucky and be safe in this instance).
All of this has almost nothing to do with request-response and web servers and whatnot, this is just how the Begin/End programming model works, regardless of what application you're using it in.
Upvotes: 2