.NET has lots of support for asynchronous processing, which ASP.NET packages up very nicely in PageAsyncTasks. And yet I almost always find an excuse not to use them - they're hard to test, it's not instantly obvious what the logic of the page is when you have delegates and handlers all over the place, and, like, most of my code is so amazing that all I need second thread to do is watch and applaud.
But I thought I'd try something slightly more usable. I like the idea of Future Values - basically they're a placeholder for a value that sits there nervously pretending to be the result of an asynchronous call. When you finally get round to using it, and if it's ready, the future will give you an answer and relax. If not, it'll block the thread and make excuses until it's finished. What's nice about this is that in many cases, the time between first getting the future and actually using it will be enough for the operation to complete, which means no blocking will happen and you won't notice anything. This is the logic behind PageAsyncTasks - there's lots for the ASP.NET runtime and you to do between Page_Load and Page_PreRender, so there's no point twiddling your thumbs. And if it does need to block, that's fine, but you haven't had to mess about so much with callbacks and stuff.
I wanted this to be as transparent as possible, and I'd never played with DynamicProxy before, so I thought it would be a good experiment. I basically wanted to come up with a solution to the following type of problem:
public class SlowService
{
public Result DoSomething()
{
Thread.Sleep(5000);
return new Result("Done!");
}
}
You know the sort of thing, some web service that goes off to calculate the answer to life, the universe and everything and all you've got is a spinny thing going ape in your browser while you wait. No more! Behold the AsyncProxyness:
public class AsyncProxy : IInterceptor
{
private readonly ProxyGenerator _generator = new ProxyGenerator();
public static T Create<T>(T target)
{
ProxyGenerator generator = new ProxyGenerator();
return (T)generator.CreateProxy(typeof(T), new AsyncProxy(), target);
}
public object Intercept(IInvocation invocation, params object[] args)
{
InvocationDelegate asyncInvocation = invocation.Proceed;
Type returnType = invocation.Method.ReturnType;
if (returnType == typeof(void))
{
asyncInvocation.BeginInvoke(args, delegate(IAsyncResult result) { asyncInvocation.EndInvoke(result); }, null);
return null;
}
else
{
IAsyncResult result = asyncInvocation.BeginInvoke(args, null, null);
Future future = new Future(asyncInvocation, result);
return returnType.IsInterface
? _generator.CreateProxy(returnType, future, Activator.CreateInstance(returnType, true))
: _generator.CreateClassProxy(returnType, future);
}
}
}
internal delegate object InvocationDelegate(params object[] args);
This allows you to create a proxy for a type, that turns all its methods (or at least the virtual or interface defined ones DynamicProxy lets you monkey with) into asynchronous calls. And instead of returning the result of the call, it gives you back a Future object that'll stand in for the result of the call until it's finished:
internal class Future : IInterceptor
{
private readonly InvocationDelegate _asyncInvocation;
private readonly IAsyncResult _result;
private object _invocationTarget;
internal Future(InvocationDelegate asyncInvocation, IAsyncResult result)
{
_asyncInvocation = asyncInvocation;
_result = result;
}
public object Intercept(IInvocation invocation, params object[] args)
{
if (_invocationTarget == null)
{
lock (this)
{
if (_invocationTarget == null)
{
object target = _asyncInvocation.EndInvoke(_result);
Thread.MemoryBarrier();
_invocationTarget = target;
}
}
}
invocation.InvocationTarget = _invocationTarget;
return invocation.Proceed(args);
}
}
Hopefully that double-checked lock is correct. Looks clever though, right? Anyway, all this says is that when someone finally needs to do something with the result of our slow call, we'll wait for the asynchronous delegate to finish (blocking if necessary) and then pass the method invocation through to the actual result of that call. Once that's done, the Future is just a completely transparent proxy for the returned object. This all allows you to do things like this:
class Program
{
static void Main(string[] args)
{
ISlowService service = AsyncProxy.Create<ISlowService>(new SlowService());
Console.WriteLine("Look, ma!");
Result r1 = service.DoSomething();
Result r2 = service.DoSomething();
Result r3 = service.DoSomething();
Console.WriteLine("No blocking!");
Console.WriteLine("Results: {0}, {1} and {2}", r1, r2, r2);
Console.ReadLine();
}
}
public interface ISlowService
{
Result DoSomething();
}
public class SlowService : ISlowService
{
public Result DoSomething()
{
Thread.Sleep(5000);
return new Result("Done!");
}
}
public class Result
{
private string _message;
protected Result() { }
public Result(string message)
{
_message = message;
}
public override string ToString()
{
return _message;
}
}
Which all seems to work. I've not tested or benchmarked anything, so don't trust me just yet. I should also point out that in DynamicProxy2, this won't work, because it relies on creating a delegate to point at the Proceed method of the interceptor. In the new version, an invocation and its return value are spread across bunch of properties instead of the easy-to-use one liner we currently have. But at the very least, we have here a really simple API to get you thinking about a cool pattern.