How is IAsyncResult returned from HttpWebRequest.BeginGetResponsesupposed to be used?

  • Thread starter Thread starter Brad Wood
  • Start date Start date
B

Brad Wood

The sample code on MSDN for BeginGetResponse uses a
ManualResetEvent.WaitOne() for detecting when the asynchronous call is
finished. This works fine, but I'd like to make multiple calls.

Before delving into the complexities of ManualResetEvent.WaitAll(), I
tried making use of the properties of the IAsyncResult that
BeginGetResponse returns, but:
- CompletedSynchronously never returns true
- IsComplete returns true immediately (before it actually is true)
- IAsyncResult.AsyncWaitHandle.WaitOne() doesn't wait on anything

Can anyone tell me why the result of BeginGetResponse doesn't seem to work?
 
Brad said:
The sample code on MSDN for BeginGetResponse uses a
ManualResetEvent.WaitOne() for detecting when the asynchronous call
is finished. This works fine, but I'd like to make multiple calls.

Before delving into the complexities of ManualResetEvent.WaitAll(), I
tried making use of the properties of the IAsyncResult that
BeginGetResponse returns, but: - CompletedSynchronously never
returns true - IsComplete returns true immediately (before it
actually is true) - IAsyncResult.AsyncWaitHandle.WaitOne() doesn't
wait on anything

Can anyone tell me why the result of BeginGetResponse doesn't seem to
work?

Can you post your code?

Cheers,
 
Joerg said:
Can you post your code?

Here goes; it starts with button1_Click below.

///////////// TEST FORM ////////////////////////////////
private void button1_Click(object sender, System.EventArgs e)
{
string err = string.Empty;
listBox1.Items.Clear();

HTTPCaller caller = new HTTPCaller( txtURL.Text );
RequestState result = caller.beginRequest( ref err, new
AsyncCallback(this.httpCallback) );
if ( result == null )
{
MessageBox.Show( string.Format( "error calling begin request: {0}",
err) );
}
else
{
// Never true
while ( result.CompletedSynchronously ) ;

// Returns true immediately (before httpCallback is called )
while ( result.IsComplete ) ;

// Doesn't wait on anything (same as result.IsComplete)
//result.AsyncWaitHandle.WaitOne();
}

MessageBox.Show( "done" );
}

private void httpCallback( IAsyncResult ar )
{
RequestState requestState = (RequestState)ar.AsyncState;
HttpWebRequest webRequest = requestState.request;

try
{
using( HttpWebResponse response = webRequest.EndGetResponse(ar) as
HttpWebResponse )
{
using( StreamReader sr = new
StreamReader(response.GetResponseStream()) )
{
string temp = sr.ReadToEnd();
Monitor.Enter( listBox1 );
try
{
// just put the first few chars in a list box.
if ( temp.Length > 5 )
{
listBox1.Items.Add( temp.Substring(0, 5) );
}
}
finally
{
Monitor.Exit( listBox1 );
}
//Interlocked.Increment( ref callBackCount );
}
}
}
catch (Exception ex)
{
listBox1.Items.Add( ex.Message );
}

}
//////////////////////////////////////////////////////////

///////////// TEST OBJECT ////////////////////////////////
public class HTTPCaller
{
private string mUrl;

public HTTPCaller( string url )
{
mUrl = url;
}

public RequestState beginRequest( ref string err, AsyncCallback
callback )
{
RequestState result = null;
HttpWebRequest httpWebRequest = null;

try
{
httpWebRequest = HttpWebRequest.CreateDefault( new Uri(mUrl) ) as
HttpWebRequest;
RequestState requestState = new RequestState();
requestState.request = httpWebRequest;
if ( httpWebRequest.BeginGetResponse( callback, requestState ) !=
null ) result = requestState;
}
catch (Exception ex)
{
err = ex.Message;
}

return result;
}
}
//////////////////////////////////////////////////////////

///////////// HELPER OBJECT (MSDN) ///////////////////////
public class RequestState
{
const int BUFFER_SIZE = 1024;
public StringBuilder requestData;
public byte[] BufferRead;
public HttpWebRequest request;
public HttpWebResponse response;
public Stream streamResponse;
public RequestState()
{
BufferRead = new byte[BUFFER_SIZE];
requestData = new StringBuilder( string.Empty );
request = null;
streamResponse = null;
resetEvent = new AutoResetEvent(false);
}
}
 
Hello Brad,
Here goes; it starts with button1_Click below.

///////////// TEST FORM ////////////////////////////////
private void button1_Click(object sender, System.EventArgs e)
{
string err = string.Empty;
listBox1.Items.Clear();
HTTPCaller caller = new HTTPCaller( txtURL.Text );
RequestState result = caller.beginRequest( ref err, new
AsyncCallback(this.httpCallback) );
if ( result == null )
{
MessageBox.Show( string.Format( "error calling begin request:
{0}",
err) );
}
else
{
// Never true
while ( result.CompletedSynchronously ) ;
// Returns true immediately (before httpCallback is called )
while ( result.IsComplete ) ;
// Doesn't wait on anything (same as result.IsComplete)
//result.AsyncWaitHandle.WaitOne();
}
MessageBox.Show( "done" );
}
[snip]

You've already found the explanation: By the time your code tries to block
on the WaitHandle, the asynchronous operation has already signaled completion.
Thus, you cannot wait on the completion of an AsyncCallback this way.

I completely agree that this behaviour is not very intuitive, and the documentation
of the Async Pattern isn't very clear about it (for fun, compare http://msdn2.microsoft.com/en-us/library/ms228967.aspx
with http://msdn2.microsoft.com/en-us/library/ms228962.aspx).

You're better off using your own ManualResetEvent here :-S

Cheers,
 
Joerg said:
You've already found the explanation: By the time your code tries to
block on the WaitHandle, the asynchronous operation has already signaled
completion. Thus, you cannot wait on the completion of an AsyncCallback
this way.

The code I posted wouldn't have compiled (attempted rollback screwed up).
Anyway, I meant to show that the button click event handler would have
received an IAsyncResult from the call to the
HttpWebRequest.BeginGetResponse wrapper object. Calling WaitOne() on
the AsyncWaitHandle property of the IAsyncResult object completes before
the httpCallback() method (which should be called as part of the
BeginGetResponse) completes.
Thus, I'm not sure how "the asynchronous operation" (which should finish
after the httpCallback() method completes) could have "already signaled
completion." Hope that makes sense.
You're better off using your own ManualResetEvent here :-S
Successfully tested that.
 
Joerg said:
The code I posted wouldn't have compiled (attempted rollback screwed
up).

I didn't even use it, I just compared it with the Simplest Sample That Could
Possibly Work ;-)
Anyway, I meant to show that the button click event handler would have
received an IAsyncResult from the call to the
HttpWebRequest.BeginGetResponse wrapper object. Calling WaitOne() on
the AsyncWaitHandle property of the IAsyncResult object completes
before
the httpCallback() method (which should be called as part of the
BeginGetResponse) completes.
Thus, I'm not sure how "the asynchronous operation" (which should
finish
after the httpCallback() method completes) could have "already
signaled
completion." Hope that makes sense.

That's what I said: You cannot wait on the *callback's* completion using
the AsyncWaitHandle. Once the callback is called, the asynchronous operation
is completed -- that's why IsComplete is true as well.

I have no idea if this is a bug or a misunderstood feature. For the record,
the AsyncWaitHanlde does wait, if BeginGetResponse() takes time. You can
test this easily by accessing an ASP.NET page with a Thread.Sleep() in its
Page_Load handler.

Cheers,
 
Joerg Jooss said:
That's what I said: You cannot wait on the *callback's* completion using
the AsyncWaitHandle. Once the callback is called, the asynchronous operation
is completed -- that's why IsComplete is true as well.

OK, now I'm sure I understand you. This is what I suspected; I just
couldn't see how this could be by design since it seems strange to wait on
only one part of what is conceptually an atomic operation. Although now that
I think about it, it is waiting for the *Begin* to complete.
I have no idea if this is a bug or a misunderstood feature. For the record,
the AsyncWaitHanlde does wait, if BeginGetResponse() takes time.

Probably a misunderstood feature, but as you mentioned, reading your prior
documentation urls would lead a person to conclude it's a bug (probably a
"bug" in the documentation).

Thanks!
 
Back
Top