Tasks and the APM Pattern

The Asynchronous Programming Model (APM) in the .NET Framework has been around since .NET 1.0 and is the most common pattern for asynchrony in the Framework. Even if you’re not familiar with the name, you’re likely familiar with the core of the pattern. For a given synchronous operation Xyz, the asynchronous version manifests as BeginXyz and EndXyz: BeginXyz starts the operation and the EndXyz method joins with it (completes it). There are several mechanisms by which the results can be joined with, such as by polling for completion using the IsCompleted property on the IAsyncResult returned from BeginXyz, blocking until the operation has completed by waiting on the IAsyncResult’s AsyncWaitHandle, simply calling EndXyz and passing it the IAsyncResult (which will block until the operation has completed), or passing to BeginXyz a callback which will be invoked when the operation has completed: that callback should then call EndXyz to retrieve the results.

This pattern is so common that we’ve opted to incorporate it as a first-class citizen into the Task Parallel Library. One way we’ve done this is by having the Task class itself implement IAsyncResult: this means that Task can be used as the core of a Begin/End implementation, easing the implementation for common scenarios. One feature new to .NET 4 Beta 1 since previous CTPs, however, is that we now support the inverse, easy creation of tasks from an implementation of the APM pattern. This new functionality shows up through the FromAsync methods on TaskFactory and TaskFactory<TResult>.

Under the covers, FromAsync utilizes the TaskCompletionSource<TResult> type. Let’s say you wanted to create a Task that represents an asynchronous read on a Stream. You could write something like the following:

You could of course write a similar wrapper for every Begin/End pair you wanted to utilize, but that could get tedious and error-prone. To solve that, we’ve provided the FromAsync method, which takes advantage of the common structure of the APM pattern, along with delegate inference, to provide generic overloads that work with most APM implementations.

For example, let’s say I have a Stream and a byte buffer, and I want to read from that stream into the buffer. Synchronously, I could do something like:

Under the covers, we follow a pattern very similar to the one shown earlier as a specific implementation for Stream.Read. Combine this support with the ability to do Task.WaitAll, Task.WaitAny, Task.Factory.ContinueWhenAll, and Task.Factory.ContinueWhenAny, and you can achieve some very useful coordination functionality in very little code.

Of course, we don’t have any magic at our disposal; all of this functionality is available through standard .NET libraries. That means that we achieve the above with a method with the following signature:

You’ll notice then that this overload is coded specifically for APM implementations that take three input parameters (of types TArg1, TArg2, and TArg3). The vast majority of the APM implementations in the .NET Framework take three or less input parameters, with only a trickling accepting more than that. As such, we’ve included overloads in the Task Parallel Library that follow this pattern for up to and including three parameters. Of course, as with all corner cases, there will certainly be scenarios that require usage with more than three parameters, or with slightly different forms. For those cases, we’ve also added overloads that accept an IAsyncResult as the first parameter, rather than accepting a beginMethod delegate. This way, you can pass in any IAsyncResult you want, and we’ll call back to the endMethod when we find that the IAsyncResult has completed. This approach typically isn’t as efficient as the beginMethod approach, but it can be quite handy in a pinch.

Func<> has actually been expanded in .NET 4 such that there are variants which take up to 16 parameters. Something like 95% of all APM implementations in the core Framework use three parameters or less, and we had to draw the line somewhere. However, if you do have more than three, you still have options. One is that you can relatively easily implement your own variants of this with as many parameters as you like (and with the FromAsync methods being instance methods on TaskFactory, you can even make your methods extension methods so they show up in IntelliSense and the syntax along with the rest of the built-in overloads). Another is that the overloads we provide which accept an IAsyncResult as the first parameter rather than a beginMethod delegate allows you to work with anything that returns an IAsyncResult, including begin methods that accept any number of parameters.

Thanks for the feedback. We don’t currently have plans to provide a version where the asynchronous operation hasn’t been started when the method returns, though it’s certainly something we can look into doing. In what scenarios would this be useful to you?

First, I like self descriptive names and I really think FromAsync miss the already started aspect (Task.Wrap and Task.WrapAndStart or something like this should be better).

Second, I always separates object creation and execution start. It’s not same stuff.

Third, I work on a StateMachine engine where state activities must be described during initialization of state machine. Tasks can be used to implement states activities but Start process must be done only on state entry during execution step (long away from initialization).

In fact, the initial problem is having your Task implements IAsyncResult. When you start a standard BeginXyz APC, you only get IAsyncResult implementation instance when process have already been started. IAsyncResult is a kind of token describing a currently running asynchronious execution. But Task can be started after construction. So, during time time between construction and Start, your Task is not an IAsyncResult.

Hmm… in addition to Begin and End async methods, it might be nice if you added helpers for synchronous methods.

Twisted Python does something like this, and it’s very handy. Oftentimes you have a method that could be asynchronous, but is synchronous in some instances. In those cases, you’d (with TPL) end up doing

var f = Future<blah>.Create();

f.Value = value;

return f;

It’d be nice if there were a helper, such that I could just return Future<blah>.Succeed(value); or in the case of setting Exception, Future<blah>.Fail(value);

Thanks for the suggestion. We’ve considered adding such a helper, e.g. Task.Factory.FromResult(value) and Task.Factory.FromException(exc), and may do so at some point in the future. Could you elaborate on the scenarios where this would be useful to you?

Scenarios… Hmm. Sure thing. Suppose you have a server with some methods that can return from immediately, and some that require file access, database access, etc.

Now, in Twisted Python, there’s a function called defer.maybeDeferred that will take a normal value or a Deferred (essentially a Future<T> in your case), and if it’s a normal value call defer.succeed on the value to make it a Deferred. Since .NET is strongly typed this is not necessary. Instead the server would pass back a Future<T>.

Then, the method calling the server’s request function will receive the Future<T> back, and attach callbacks that take in the response and encode it. The encoder returns a Future<T>. The I/O level then attaches callbacks to send it to the other side. Each level modifies the return value.

Anyway, what the .succeed/.fail feature does is allow this whole process, in the event that there’s nothing to wait on, to occur without any switching or scheduling at all. .callback (.NET .ContinueWith) would execute immediately, since the Future<T> returned is already finished. This is a rather common case.

And yeah, right now I have it inside a FutureHelper I wrote (using the CTP, there is no Factory, so nowhere to put an extension method).

By the way, have you considered talking to the CLR team? They need to make it possible to use T as void in generics. This would save you an enormous amount of duplication, and could be implemented as an empty struct plus optimizations (i.e. return a; -> return; if a is void, b = a -> no op if b and a are void, etc.). The Action/Func duality is entirely an artifact of this lack, and it’s propagating into your API as well. Just a thought 🙂

What the documentation never states is that FromAsync will also throw whatever exception the BeginOperation method that is passed as an argument might throw. E.g. if you are passing as an argument a .Net APM operation that creates a network connection and the endpoint is not found you will get an exception right then and there when you call the FromAsync method and the task will not be created. This would merit a mention somewhere on msdn, but I couldn't find anything.

I was trying to catch exceptions and return a faulted task (so that I only have to do error handling in one place) but that doesn't work: