Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Task fromAsync timeout

I have this piece of code to make an asynchronous HTTP request:

    public static void MakeRequest(Uri uri, Action<RequestCallbackState> responseCallback)
    {
        WebRequest request = WebRequest.Create(uri);
        request.Proxy = null;

        Task.Factory.FromAsync<WebResponse>(request.BeginGetResponse, request.EndGetResponse, null).ContinueWith(task =>
            {
                WebResponse response = task.Result;
                Stream responseStream = response.GetResponseStream();
                responseCallback(new RequestCallbackState(response.GetResponseStream()));
                responseStream.Close();
                response.Close();
            });
    }

It works, but I need to set a request timeout. I tried to use request.Timeout but don't seem to do anything. Is there a way to set up a task timeout in this code?

Edited to add a new timeout callback. New code:

    public static void MakeRequest(Uri uri, Action<RequestCallbackState> responseCallback)
    {
        WebRequest request = WebRequest.Create(uri);
        request.Proxy = null;

        IAsyncResult t = Task.Factory.FromAsync<WebResponse>(request.BeginGetResponse, request.EndGetResponse, null).ContinueWith(task =>
            {
                WebResponse response = task.Result;
                Stream responseStream = response.GetResponseStream();
                responseCallback(new RequestCallbackState(response.GetResponseStream()));
                responseStream.Close();
                response.Close();
            });
        ThreadPool.RegisterWaitForSingleObject(t.AsyncWaitHandle, new WaitOrTimerCallback(TimeoutCallback), request, 1000, true);
    }

    private static void TimeoutCallback(object state, bool timedOut)
    {
        if (timedOut)
        {
            Console.WriteLine("Timeout");
            WebRequest request = (WebRequest)state;
            if (state != null)
            {
                request.Abort();
            }
        }
    }

Testing with:

HttpSocket.MakeRequest(new Uri("http://www.google.comhklhlñ"), callbackState =>
    {
        if (callbackState.Exception != null)
            throw callbackState.Exception;
        Console.WriteLine(GetResponseText(callbackState.ResponseStream));
    });
Thread.Sleep(10000);
like image 224
David Moreno García Avatar asked Sep 10 '11 00:09

David Moreno García


1 Answers

From the Timeout documentation:

The Timeout property has no effect on asynchronous requests made with the BeginGetResponse or BeginGetRequestStream method.

This is because the framework forces you to handle the timeout yourself. You should be able to use the example code here, except pass the Task returned from the FromAsync call to the ThreadPool.RegisterWaitForSingleObject method.


Edit:

You need to put the registration on the original task, not the continuation:

public static void MakeRequest(Uri uri, Action<Stream> responseCallback)
{
    WebRequest request = WebRequest.Create(uri);
    request.Proxy = null;
    const int TimeoutPeriod = 1000;

    Task<WebResponse> t = Task.Factory.FromAsync<WebResponse>(request.BeginGetResponse, request.EndGetResponse, null);
    ThreadPool.RegisterWaitForSingleObject((t as IAsyncResult).AsyncWaitHandle, TimeoutCallback, request, TimeoutPeriod, true);
    t.ContinueWith(task =>
    {
        WebResponse response = task.Result;
        Stream responseStream = response.GetResponseStream();
        responseCallback(response.GetResponseStream());
        responseStream.Close();
        response.Close();
    });
}

This works for me (if I set the timeout duration very short, and hit this, I always timeout appropriately).

like image 56
Reed Copsey Avatar answered Sep 25 '22 08:09

Reed Copsey