RSS

Tag Archives: async

Long Poll Sally – ASP.NET MVC AsyncController

Just like the rest of us, I use polling occasionally. When I learned of WebSockets, I came across my reading about a term I wasn’t acquainted with: Long Polling (or “Comet“). It can be viewed schematically here. To put it simply, while polling can be explained as a series or recurring requests to the server for updates, Long Polling is more or less the same, only technically, the client opens a request to the server, but the server doesn’t return an immediate response and keeps the connection open. When the server has a response, it will use that open connection to notify the client. The client then has to perform another request to reopen a connection for the next update.

At first, when I realized what Long Polling is, I didn’t like the idea much. I mean, if you end up reopening a request to the server like regular polling, why bother? This comes in handy only for less-frequent notifications from the server side, otherwise its just like Polling. Besides, with the new WebSockets in place – who needs it? But then I did some more reading and decided that it might come in handy after all. The main problem I see for using WebSockets is IE9. The major browsers (Chrome, Firefox and the soon to be released IE10) support WebSockets. Cool. But IE9 does not support it. “Fine” you say, have your users upgrade their browsers to IE10. Ahhhh, but MS has decided to limit IE10 to Windows 7 or above. MS is taking several steps to force us to upgrade to their latest releases: No IE10 for Vista, just like WebSockets isn’t planned for IIS7. Funny enough, Google’s Chrome supports XP and Firefox is also compatible with older Windows (in fact, a short visit to Wikipedia reveals that: “Windows XP SP2, XP SP3 and above is now the minimum requirement for Firefox 13.0 and above”…) I find it quite amazing, that MS’ latest browsers cannot be installed on their own operating systems, while their biggest competitors can. It’s as if Microsoft believes that users will upgrade to their latest operating systems just to get their better browser, when they have major competition which is compatible with legacy Windows. Gregg Keizer rightfully points to an anonymous comment on IE’s blog: “IE9 is the new IE6.” Microsoft is working so hard on renewing IE and kicking out IE6, but it performs such a strategic mistake by leaving IE8 and IE9 for a long long time.

Anyway, that’s a major issue as far as I’m concerned. IE6 to IE9 will probably remain with us for quite sometime, as many users still use Windows XP or Vista. All of these users will not enjoy WebSockets support, and we developers will have to continue to develop alternatives to MS’ browser policy of not allowing their own users to upgrade. I guess that this more or less explains why Polling and Long Polling are still relevant: we simply can’t rely on WebSockets alone in the upcoming years.

“Hello World”

Following several blog posts, especially this one by Clay Lenhart, I ended up with the following “Hello World”-like example. Here is the client. It’s fairly simple and self explanatory:

<!DOCTYPE html>
<html>
<head>
    <meta name="viewport" content="width=device-width" />
    <title>Simple</title>
</head>
<body>
    <script type="text/javascript" src="http://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.7.1.min.js"></script>
    <script type="text/javascript">
        $(function () {
            openConnection();
        });

        function openConnection() {
            $.post("@Url.Action("Simple", "MyAsync")", function (results) {
                if (results.Data) {
                    $('#log').text(results.Data);
                }

                openConnection();
            });
        }
    </script>
    <div id='log'>
    </div>
</body>
</html>

Here is the server side (explanation below):

using System;
using System.Threading;
using System.Threading.Tasks;
using System.Web.Mvc;

namespace LongPolling.Controllers
{
    public class MyAsyncController : AsyncController
    {
        public void SimpleAsync()
        {
            AsyncManager.OutstandingOperations.Increment();

            Task.Factory.StartNew(() =>
            {
                Thread.Sleep(TimeSpan.FromSeconds(10));
                AsyncManager.Parameters["contents"] = DateTime.Now.TimeOfDay.ToString();
                AsyncManager.OutstandingOperations.Decrement();
            });
        }

        public JsonResult SimpleCompleted(string contents)
        {
            return Json(new { Data = contents });
        }
    }
}

The server code works similarly to the Web Services Async pattern: There is a ‘Begin’ (SimpleAsync), and an ‘End’ (SimpleCompleted). Here is what we see here:

  • Line 8: A would-be async controller must derive from AsyncController. This is what gives us the AsyncManager property through which we accomplish async behavior in MVC.
  • Line 10 is where the async begins. Note, that while the client called the “Simple” Action, our method is called SimpleAsync. In Line 22 you will find it’s counterpart callback method which will be called upon completion (“complete” callback).
  • Lines 12 & 18 are completing operations: In line 12 you announce how many “outstanding operations” you have, which is simply a counter you set at the start of the async operation. When the async operation ends you decrement this counter. When the counter reaches zero, the “completed” callback is called and the response returns to the client. This resembles a C++ reference counter for a smart pointer.
  • Lines 14-19 simulate an async task call. This is where you call time consuming logic, or where you register to receive notifications for two-way communication, simulating WebSockets behavior.
  • Line 17 is how you may pass arguments to the “completed” callback (Line 22).

Here’s a screenshot from IE9 running as IE8:

  1. Response from the server.
  2. Interval of the 10 second requests.
  3. Current pending request.
  4. Browser running in IE8 mode – after all, this is what this effort is all about.

There you have it – quite a simple mechanism to perform async MVC operation, which can evolve into a WebSocket-like operation (see below).

Multiple “outstanding operations”

The OutstandingOperations property which returns an OperationCounter class which is a sort of a reference counter for “pending async operations”. This means that if you have multiple async operations scheduled per a single async request, you may set the counter to the number of those operations, and have each operation decrement the counter when it completes. When the counter reaches zero, the “completed” callback will be returned. Here’s an example:

using System;
using System.Threading;
using System.Threading.Tasks;
using System.Web.Mvc;

namespace LongPolling.Controllers
{
    public class MyAsyncController : AsyncController
    {
        public void MultipleAsync()
        {
            AsyncManager.OutstandingOperations.Increment(3);

            Task.Factory.StartNew(() =>
            {
                Thread.Sleep(1000);
                AsyncManager.Parameters["param1"] = DateTime.Now.ToString();
                AsyncManager.OutstandingOperations.Decrement();
            });

            Task.Factory.StartNew(() =>
            {
                Thread.Sleep(2000);
                AsyncManager.Parameters["param2"] = DateTime.Now.ToString();
                AsyncManager.OutstandingOperations.Decrement();
            });

            Task.Factory.StartNew(() =>
            {
                Thread.Sleep(3000);
                AsyncManager.Parameters["param3"] = DateTime.Now.ToString();
                AsyncManager.OutstandingOperations.Decrement();
            });
        }

        public JsonResult MultipleCompleted(string param1, string param2, string param3)
        {
            return Json(new { Data = string.Format("{0} {1} {2}", param1, param2, param3) });
        }
    }
}
  • Line 12 states that there are three async pending operations to be completed before returning a response.
  • Three new threads simulate those async operations. Each completes in a Decrement being called, to signal an async operation completion (lines 18,25,32). Each also provides a different argument with the result of the pending operation, which will be passed on to the “completed” callback.
  • Line 36: the “completed” callback receives the results of the pending operations and returns a response.

WebSocket-like implementation

UPDATE: Before you dig into this code, you might be interested in SignalR. Turns out that this is a broadcasting solution from guys at MS. It supposed to auto-detect and use WebSockets, Long Polling or whichever technology is available on the client. I have not tried this yet, but it looks very promising. You can see Scott Guthrie talk about this here (1:09:38).

Now it comes down to the “main event”: Implementing a WebSocket like implementation. Instead of opening a WebSocket from the browser to the server for two-way communication, the idea is to open a request which the server will maintain asynchronously but not so much for the purpose of supporting a lengthy operation, as much as to maintain the connection for a future response from some business logic component. Here’s an example for accomplishing this. First, the publisher (i.e. class which publishes business logic events to subscribers):

using System;
using System.Timers;

namespace LongPolling.Controllers
{
    public static class MyPublisher
    {
        public class MyEventArgs : EventArgs
        {
            public string Contents { get; set; }
        }

        public static event EventHandler<MyEventArgs> Notify;

        private static Timer timer;
        static MyPublisher()
        {
            timer = new Timer();
            timer.Interval = 10000;
            timer.Elapsed += new ElapsedEventHandler(timer_Elapsed);
            timer.AutoReset = false;
            timer.Start();
        }

        static void timer_Elapsed(object sender, ElapsedEventArgs e)
        {
            try
            {
                timer.Stop();

                if (Notify != null)
                {
                    Notify(null, new MyEventArgs { Contents = DateTime.Now.ToString() });
                }
            }
            finally
            {
                timer.Start();
            }
        }
    }
}

Not much here to tell. The MyPublisher class simulates receiving notifications from the business logic using a simple timer, that every 10 seconds publishes to subscribers the current DateTime. Next comes the AsyncController:

using System;
using System.Web.Mvc;

namespace LongPolling.Controllers
{
    public class MyAsyncController : AsyncController
    {
        public void LogicAsync()
        {
            AsyncManager.OutstandingOperations.Increment();

            MyPublisher.Notify += MyPublisher_Notify;
        }

        private void MyPublisher_Notify(object sender, MyPublisher.MyEventArgs e)
        {
            MyPublisher.Notify -= MyPublisher_Notify;
            AsyncManager.Parameters["contents"] = e.Contents;
            AsyncManager.OutstandingOperations.Decrement();
        }

        public JsonResult LogicCompleted(string contents)
        {
            return Json(new { Data = contents });
        }
    }
}
  • Line 12: Register to receive an event from the business logic class (“MyPublisher”). MyPublisher will raise an event each time it attempts to notify whatever message to registered clients.
  • Line 15 is the event handler which is raised by MyPublisher. Here we set the argument to be passed to the “completed” callback and decrement the Outstanding Operations reference counter.
  • Line 17 is probably the most important here as it’s the easiest part to forget: we have to release the event handler from MyPublisher, or MyPublisher will keep a reference to our AsyncController instance and it will never be Garbage Collected. In other words: a managed code memory leak. This is just like in Winforms: a form registers with a business class to receive events in order to display a progress bar or some other UI feedback. When you are done you call a Close( ) on that form. You mistakenly believe that because you called Close and don’t see the form anymore – it is disposed of and eligible for Garbage Collection: not remotely. The business class maintains a reference to the form instance and it is not removed at all. After a while, you begin to witness your memory growing more and more although you’re certain that you have cleaned up everything, and you end-up “windebugging” with SOS to detect that leak. In the screenshot below you can see how the event’s invocation list gets populated with just several requests from the same client browser. The event handlers are never released, which cause the MyAsyncController instances generated per request to remain in memory. By issuing a ‘-=’ we unregister correctly from the event, and the invocation list is kept clean.

Regardless of the potential memory leak described above, the major difference in this implementation from the previous sample is conceptual, and not necessarily technological: In the previous sample, the async method was used to start an async operation, which actually opened new threads. When the threads complete, each decrements the reference counter thus signaling the end of the lengthy async operation. The entire process was started as a browser initiative and simply freed ASP.NET’s threads for the time of the lengthy operation. However, in the current example, the browser opened a request for no particular operation but in order to wait for a callback from the server. The server on an entirely different logical class (“MyPublisher”) will decide when and what to return to the client. A server may return different operations, such as “display notification” about a new email, or “redirect to another page” and so forth.

Timeout

Moving on to implementing a WebSocket like behavior, one issue that was bothering is the Timout. It’s one thing to use an async server operation which is due in a reasonable time; it’s another thing when you attempt to implement a WebSocket-like behavior that may time-out due to less-frequent updates. For example, if you would like to implement a chat, updates might be frequent, but if you implement a mechanism which is most of the time idle, and just prompts notifications every now and then to the end user, we could be discussing here relatively long interval updates. Consider Gmail. When you are positioned on an email correspondence, if suddenly you receive an email related to that correspondence – a notification appears informing you that there’s a new message and you may click on “show” or “hide” to proceed. This is a good example for when a website may be open for a lengthy time without any operation, but a server may “push” a message when relevant. Therefore I tried to test whether a Timeout occurs on the client or the server, by a simple Thread.Sleep() to lengthy time intervals on the server side. It seems that an AsyncController (MVC 4) defaults to 45 seconds. After that, the client is expected to receive the following timeout exception:

“System.TimeoutException: The operation has timed out.”

I have no idea why, but settings the Timeout property seems to be completely ignored. For example, setting the Timeout to “infinite” (Timeout.Infinite) or to a simple 60000 ms (and extending the server’s Sleep to a much lengthier operation), returned a Timeout exception after 45 seconds. Luckily, I read here that you could set a NoAsyncTimeoutAttribute on the async method to state that there should not be a timeout, and in fact – this actually works (viewing the Timeout property in a debug watch shows -1, which is the value of Timeout.Infinite, but it actually works.)

Now that I have server Timeout set to infinite, I tested different clients’ behavior. On Chrome and Firefox – the browsers did not timeout even when I set the server to respond after 6 hours. This was a good behavior as far what I expected. However, in IE it timed out. This was quite frustrating, because IE is the target of this Long Polling alternative in the first place. If it times out, it means that now I have to detect how long it takes before the client times out, and that I have to implement a mechanism for having the server return a response no longer than that timeout value or I’m risking losing the connection with the client. Testing this several times shows that IE times out at approx 60 minutes (see pics below).

Now comes the harder part, which is what to do when those 60 minutes time-out. Remember: the idea is to return a response to the client browser before reaching the client time-out, and have the client re-open a new connection. I thought I could use a simple Timer for this. The timer has to be set to a predefined interval (e.g. 30/45/60 minutes etc. – depends on how much risk you wish to take here…), and decrement the Outstanding Operations reference counter if it elapses. This way the server returns a response to the client, which the clients reads as “timeout”, meaning that there’s nothing to do but re-open a request.

Here’s the code:

using System;
using System.Web.Mvc;

namespace LongPolling.Controllers
{
    public class MyAsyncController : AsyncController
    {
        private System.Timers.Timer timer;

        [NoAsyncTimeout]
        public void LogicAsync()
        {
            this.timer = new System.Timers.Timer();
            this.timer.Interval = 4000;
            this.timer.AutoReset = false;
            this.timer.Elapsed += delegate
            {
                timer.Stop();
                MyPublisher.Notify -= new MyPublisher_Notify;
                AsyncManager.Parameters["contents"] = "timeout";
                AsyncManager.OutstandingOperations.Decrement();
            };

            this.timer.Start();

            AsyncManager.OutstandingOperations.Increment();

            MyPublisher.Notify += MyPublisher_Notify;
        }

        private void MyPublisher_Notify(object sender, MyPublisher.MyEventArgs e)
        {
            this.timer.Stop();
            MyPublisher.Notify -= MyPublisher_Notify;
            AsyncManager.Parameters["contents"] = e.Contents;
            AsyncManager.OutstandingOperations.Decrement();
        }

        public JsonResult LogicCompleted(string contents)
        {
            return Json(new { Data = contents });
        }
    }
}
  • Line 8 declares a Timer variable. This timer is started in the ‘Begin’ method (LogicAsync) and is set to 4 seconds (just for this example – it’s supposed to be set to a much lengthier interval but less than 60 minutes).
  • Lines 16-22: When this timer’s Elapsed event is raised, this means that the request is timing out and should be renewed, so we unregister from the publisher (we do not want to process the event handler during re-connection), and decrement the Outstanding Operations reference counter in order to immediately return a response to the browser. The browser is supposed to interpret this response as “do nothing” (NOP) and re-open a request.
  • Line 33: If the timer did not elapse but we did get an event from the Publisher, we immediately stop the timer, and execute the returning response as normal. This ensures that the timer won’t elapse till we finish implementing the response logic.

In the example above, assuming that a MyPublisher is set to return a response every 10 seconds, you are expected to see two “NOP” responses per one correct response of the handler being raised:

await/async

This here is just an extra to this post. I was thinking whether I could use the new async lib from C#. I came up with this:

using System;
using System.Threading;
using System.Threading.Tasks;
using System.Web.Mvc;

namespace LongPolling.Controllers
{
    public class MyAsyncController : AsyncController
    {
        public async Task<JsonResult> MyAction()
        {
            string contents = null;
            await Task.Factory.StartNew(() =>
            {
                Thread.Sleep(TimeSpan.FromMinutes(30));
                contents = DateTime.Now.TimeOfDay.ToString();
            });

            return Json(new { Data = contents });
        }
    }
}

The code above seems to be working well but I’m somewhat uncertain whether it’s the correct implementation or best practice for using await/async in MVC.

UPDATE: In Scott Guthrie’s lecture (1:15:25), you can view async/await support which is similar to the example above.

However, when I tried to rewrite the “MyPublisher pattern” using await/async (excluding the timer part), I ended up with something I wasn’t pleased with:

using System;
using System.Threading;
using System.Threading.Tasks;
using System.Web.Mvc;

namespace LongPolling.Controllers
{
    public class MyAsyncController : AsyncController
    {
        public async Task<JsonResult> MyAction()
        {
            ManualResetEvent ev = new ManualResetEvent(false);
            string contents = null;
            EventHandler<MyPublisher.MyEventArgs> d = null;
            d = delegate(object sender, MyPublisher.MyEventArgs e)
            {
                MyPublisher.Notify -= d;
                contents = e.Contents;
                ev.Set();
            };

            await Task.Factory.StartNew(() =>
            {
                MyPublisher.Notify += d;
                ev.WaitOne();
            });

            return Json(new { Data = contents });
        }
    }
}

Nope, I was clearly unhappy with this code.

Summary

To be honest – I’m not thrilled at all to have to use Long Polling. I didn’t like polling either, but polling was simple – the browser makes requests at (almost) regular intervals, checks for updates. End of story. But with IE10 limited to Windows 7 and above, it seems like Long Polling is a possible solution to achieve a “less frequent polling” but still with “real-time” updates. Having said that, one needs to remember that other solutions are available such as using Flash or Silverlight for achieving two-way communications, and to be honest, I’m uncertain how Google Docs document sharing was implemented. You might also be interested in pubsubhubbub which is based on Atom/RSS feeds (at least watch their really cool “what is PubSubHubbub” video). There are other utils which provide reverse-Ajax and server-pushing such as WebSync, but I haven’t used them myself.

UPDATE: As specified above, SignalR is a .NET based solution from guys at MS which you should also probably look up.

Although the majority of my blog posts relate to stuff that I am learning and documenting as I go along, in this particular case I feel like Long Polling is a technique that I have not experienced sufficiently in order to recommend it. Although this is also true for WebSockets, WebSockets are designed to provide a two-way communication so I was feeling a lot more comfortable recommending them. I see Long Polling not as a technology on its own, but rather as a technique which is designated to provide a solution to a problem using existing technology, which isn’t specifically targeting that same problem. However, I am planning on implementing this soon in order to learn whether it proves itself as reliable and comfortable.

UPDATE: I attempted to implement a Publisher which will be able to filter out the browser that initiates updates to the rest of them using the above technique. My solution had one open Long Polling Ajax request and another Ajax request posted to the server from the same browser representing a user’s update. This was supposed to be nothing too clever: just exclude the current user from the Publisher’s event notification list. I thought of using the Session’s ID to make that distinction, when I noticed that my async requests got stuck almost immediately. At first I thought that this was related to the number of requests I had open from several browsers over a Windows 7 workstation, but slowly and painfully I discovered that this was because I inserted something into the Session in order to make the Session ID sticky. Googling revealed a reason: seems like AsyncController locks the Session in order to make it thread-safe between open requests from the same session. In order to use the Session from an AsyncController you probably have to declare it as read-only using the SessionState attribute. I have not found an “official” explanation for that yet although I’m sure that there is, and the best I found so far was this, this and this blog post. If anyone finds a more documented or official blog from MS guys about this, please comment.

Last credits

Little Richard gets the credit for this blog post’s title.

Advertisements
 
2 Comments

Posted by on 19/03/2012 in Software Development

 

Tags: , , , , , , , ,

C# 5 – await and async in ASP.NET

I’ve been doing some reading and experimenting with the new C# 5 Async CTP, available for download from here. I guess that the Async Framework will be a part of .NET 5, although I haven’t found a formal document stating so. There are also some blogs released in the past few months by MS’ people that share information about this new feature. I’ll try to summarize it as I understand it for now, and specifically relate to my findings with regards to ASP.NET.

C# 5 Async Framework seems to focus on easing asynchronous programming. It seems like MS has decided to help us developers write asynchronous code without having to deal with the technical aspect of things, mainly callbacks or thread management, and focus instead on the actual logic we need to implement. The basic idea is that a developer may write code “more or less” in a synchronous fashion, and for the Async Framework to do the rest. In my opinion, this does not mean that a developer won’t have to understand the concept of async programming, nor does it mean that a developer will cease using other “orthodox” methods of async programming.

Having said that, here is an example based on MS Async Whitepapers of how it looks. First, lets review a normal nowadays synchronous method for downloading images and saving them on the local machine, using ASP.NET WebForms Page:


Let’s quickly go over this:

  • Lines 16-19 represent Urls of images for download.
  • Lines 22-25 iterate over the Urls, calling a synchronous method per Url.
  • Lines 30-37 is the method which downloads the given Url synchronously using a simple WebClient (I’ve omitted IDispose handling in this post for simplicity).

The Async CTP allows us to transform this code into asynchronous code by performing minor changes:

For the moment we’ll skip the Page_Load implementation and go directly to the downloading method (lines 27-34). We can see very clearly that the implementation remains almost identical to the original method. However, the Async CTP has some new keywords in C# 5 which are used here, that instruct the compiler to compile our code differently behind the scenes. These are async and await:

  • async: This tells the compiler to compile our method differently, so that it uses Tasks and callbacks to run. Although async is mandatory for using awaits, basically its useless without having any awaits in it. This means that if an asynced method has no await instructions within, the method runs synchronously as usual. Writing async in a method doesn’t cause it to be run on a different thread at all (as you might have expected). All it does is to instruct the compiler to auto-generate hidden classes behind the scenes in order to support awaits.
  • await is what actually tells the compiler to auto-generate code using Tasks in order to run asynchronously. An async method’s code is executed on the same thread it was called upon, until it gets to the first await. Every await (starting with the first one) executes and returns immediately. Once again, this may be confusing with nowadays Wait( ) methods which usually block threads. This isn’t the case here. await does the opposite – it causes the compiler to auto-generate code which runs the code asynchronously, while preserving the context of the method. The rest of the method is compiled as a callback of the async Task. When the Task has completed, the code resumes execution using the synchronization context “right where it left off”. The Async CTP Whitepaper is very clear on this: “At first glance the await keyword looks like it blocks the thread until the task is complete and the data is available, but it doesn’t. Instead it signs up the rest of the method as a callback on the task, and immediately returns. When the awaited task eventually completes, it will invoke that callback and thus resume the execution of the method right where it left off!”

Now that we understand what async and await are, we can understand lines 27-34 better. The highlighted changes are:

  • Line 27 contains two method signature changes: async was added in order to “tell” the compiler that this method is going to use the Async framework; The Task return class is the real deal here, because it’s what allows the calling code to query the status of the async task.
  • Line 30 also contains two changes: await performs an async call to DownloadDataTaskAsync, which returns, that’s right, a Task. In the Whitepaper’s terminology, we “await the task”.
  • Lines 31-33 is compiled as a callback for the Task. This code will not be executed until line 30 completes.

With minor changes to the code, our previously synchronous method has become asynchronous! What’s quite amazing here is that the code still looks as if it is synchronous. This means that in the future, it’s supposed to be much easier to write code asynchronously. Let’s go over the calling code now:

  • Line 14 has a new async which “tells” the compiler we’re using Async framework in this method.
  • Line 22 uses await, which basically what starts the async operation and awaits completion, but what’s more important is that we use a parallel new feature, TaskEx.WhenAll( ), which waits till all the tasks have completed (sort of a Thread.Join for all the Tasks). The TaskEx is the temp CTP class and the WhenAll method is expected to be a part of the Task class upon release.
    • We have to await TaskEx.WhenAll( ) or the Stopwatch will be stopped immediately (line 23). Nevertheless, the Tasks will end as expected during the Page’s life-cycle – this will be explained later on.
  • Lines 23-24 are compiled as callbacks and will not be called till line 22 finishes awaiting the task.

One more thing we need to add here, is that just like in ASP.NET 2, running a code asynchronously requires us to have an Async=”true” in the @Page directive. Failure to do so will result in a: “Asynchronous operations are not allowed in this context. Page starting an asynchronous operation has to have the Async attribute set to true and an asynchronous operation can only be started on a page prior to PreRenderComplete event.“, which hints us not only at the Async=”true” directive, but also that the PreRenderComplete is somehow involved. I’ll discuss this later on.

Let’s have a quick view in Reflector at how this looks (we’ll focus on the Page_Load await):

  1. Notice that the MyForm page contains hidden elements which were auto-generated by the compiler. There’s now a <Page_Load>d__1 class which is used for the asynced Page_Load method (and there’s also a <AsyncDownloadFile>d__6 class for the asynced AsyncDownloadFile method). As you can see, the actual Page_Load method looks entirely different than the original source code.
  2. This statement is basically the what will used to perform the callback when the operation completes.
  3. MoveNext() actually starts execution (see below).

The above code shows us the auto-generated MoveNext() of the <Page_Load>d__1 class:

  1. This is our original code, initializing the Urls to be downloaded, as well as the Stopwatch which times the operation (the code which refers to GetExecutingAssembly was inserted by me in order to be able to easily detect where the compiled assembly is, and to Reflect it – so just ignore it.)
  2. That’s the awaiter.
  3. OnComplete, calls the callback (remember item #2 in the previous Reflector window?), which is the MoveNext() again, till the awaiter’s IsCompleted method returns true.
  4. This is the actual callback code – Stop the Stopwatch and display the elapsed time.

As you can see, our original code was split into 2 parts, one to be executed prior to the await, and the rest as callback code.

Life-cycle

Finally, let’s understand the life-cycle of things. I’ve added some code to log the different operations and thread creations. I also added two life-cycle events (PreRender and PreRenderComplete), because I wanted to learn how the life-cycle handles awaits and asyncs. This is a screenshot of the result:

Short explanation: The “Thread number” column is what’s important. It hints at whenever a new thread was assigned to handle the executing code, which actually means we can better understand the life-cycle of ASP.NET in an async scenario. Here are some highlights:

  • The first thread, which is assigned by ASP.NET to handle the request, is the one stated as “Page_Load thread”. As we can see, this thread executes all the code which runs just prior to the first await. Each await executes and returns immediately, and this can explain why both “before awaits” are on the same thread.
  • Important: OnPreRender wasn’t blocked. So now we know that asynced code which returned from the awaits continues unblocked and does not wait for the Tasks completion in the ASP.NET life-cycle, at least up to PreRender.
  • The first AsyncDownloadFile log message shows us that the callback is running on a different thread, as expected. So we know for sure that the async framework has indeed did what it was supposed to do, which it to split our original code into a block which runs on the calling thread, and a callback which runs on a different ThreadPool thread.
  • Note that the second AsyncDownloadFile log message shows us that a different thread handled the second url callback. That’s also “as expected”, as different Tasks handled each url.
  • As opposed to PreRender, we can see that PreRenderComplete executes after all awaiting Tasks have finished. This will be the behavior even if we remove the await from the TaskEx.WhenAll( ) call. That’s important, because we now understand that ASP.NET is involved in the life-cycle of things and takes care that we won’t finish the Page’s life-cycle before all Tasks are completed. This resembles ASP.NET 2 Async Page pattern (see summary below), and sheds light on the meaning of the detailed Exception specified earlier.

To summarize what we’ve seen so far, using the already existing Task classes, await starts an asynchronous operation and returns immediately. So, the code itself is not blocked at all. The compiler generates code also for the callback, so our code continues only when the Task has completed. In other words, async and await cause the compiler to compile our code into async Tasks and callbacks. ASP.NET integrates well with this environment and “knows” to await all tasks, if you haven’t done it yourself. Needless to say, the asynced version runs in parallel which makes it a lot faster.

If you feel like these async and await keywords are misused, you are welcome to join the arguments which seem to be taking place. As written previously, according to Eric Lippert’s post, developers misinterpret await for a blocking Wait-like operation, whereas it’s exactly the opposite, and tend to believe that writing async in the method signature means that .NET will run it asynchronously. However, as stated in Eric’s blog post, attempts to find better keywords so far have failed.

Summary

I find the suggested framework interesting as it will probably allow a somewhat “default” implementation of async programming, and will allow us developers to achieve async solutions more easily.

I think that many developers who know how to develop async programming to some extent, don’t actually do so. I even “dare” to think that this is a more common situation in server side web programming. Unlike Windows Forms development, where using a BackgroundWorker or some other threading solution is usually required in heavy duty tasks, but somewhat trivial because the forms stay in-memory for as long as we require them to, in server side web programming this is quite different. Most ASP.NET developers rely on IIS and ASP.NET to assign requests to threads, and know that if you initiate a multi-threaded operation yourself in ASP.NET, it is a pain. That’s because when you start a thread, the Page doesn’t stay in-memory or wait for it to finish. In Web Forms, the life-cycle doesn’t wait just because a developer opened a new thread in Page_Load or some other Button_Click event handler. The newly created thread will probably complete after the life-cycle has ended and the response was already sent to the client. More over, if you do implement a threading scenario in ASP.NET and block the page life-cycle using a Thread.Join or some other threading blocking mechanism, the actual ASP.NET main thread processing the request is stuck till the new thread has completed, and is not returned to the ThreadPool. That’s why the people at Microsoft have invented Async Pages in ASP.NET 2. This implementation was built on the idea that somewhere in the Page’s life-cycle (after PreRender), an async operation may start, the thread handling the request will be returned to the ThreadPool, and upon completion of the async thread, the life-cycle is resumed using a different ThreadPool thread (returning to the PreRenderComplete in the life-cycle). While this was some sort of a solution, it was still uncomfortable and forced to developer to be bound to a certain step in the life-cycle in order to achieve async in ASP.NET. So, my guess is that most developers chose not to use this solution as well. In short, while client side async programming evolved thanks to Ajax, server side web programming in ASP.NET lacked the proper infrastructure to evolve. Thanks to the Async framework, it’s possible we’ll see more async web server side development.


 
12 Comments

Posted by on 02/05/2011 in Software Development

 

Tags: , , , ,