Monday, June 02, 2008 11:46 PM bart

Continuations, futures, and whatnot - Thoughts on some asynchronous patterns

This sounds like one of those posts that's about to draw scientifically sound conclusions. It isn't. The other day I had a discussion about various ways to express asynchronisity (or asynchronousness, whatever spell checker upsetting word you prefer) in API designs. The answer might be shocking, but it really depends and there are quite some promising evolutions around. I just want to give you a breakdown of a few possible designs, some sexier than others but that's just a matter of (good) taste. The moral of the story: asynchronous patterns (which become increasingly important in distributed systems programming) is a plural noun.

 

The dinosaur in the room: IAsyncResult

Nothing against those animals - we should appreciate the meaning giants give to life. Obviously, the asynchronous pattern realized by this giant is overly known by the average .NET developer. A fancy way of introducing it is by using a web proxy class generated by the tool of your choice (dinosaurs prefer wsdl.exe, modern age Earth citizens choose svcutil.exe) but ultimately it all boils down to what I'd call a scoped overlapped operation. It's scoped in a VB-style by means of Begin* and End* method pairs that denote the client-side view of the operation, while it's overlapped because obviously everything between Begin and End runs simultaneously with what's happening on the other side of the fence.

IAsyncResult just serves the purpose of a concert ticket: you get one when you enter, but you're not required to stay waiting for the concert to finish, you can just sneak in with the ticket to the after-party to get to hear how great it was (the result of the long-running operation). So do whatever you like in between. Obviously don't take this as my ultimate advise as the ideal way to 'attend' cultural adventures. The cool thing is you can keep waiting for one or more of those asynchronous operations to finish depending on how you want to proceed with the results. In the end, I haven't said anything more than WaitForSingleObject[Ex] and WaitForMultipleObjects[Ex] (or the Msg* equivalents in case you want to process messages during the background concert, contrary to regulations in most theatres today but highly recommended if you want to stay alert to stimuli from outer space while waiting).

 

Continuations

These beasts are more like boomerangs. Just throw them a message threaded to a boomerang and ultimately the answer comes back threaded on the boomerang. For the code-minded out there, here's a sample:

void ThrowIt(string message, Boomerang boomerang);

where the Boomerang class could be defined like

class Boomerang : Action<string> { ... }

Notice the return type of the ThrowIt operation: you don't get anything back. Well, that is, till you get the boomerang against the back of your head some time later when it returns. This just works fine, but thinking of a boomerang as the ultimate mechanism to perform asynchronous work is a little weird at first. In more theoretical terms, you're threading the future (callback, or continuation) to the current (the asynchronous call). Or think of it as the following transformation:

string result = ThrowAndWait("Hello World");
Console.WriteLine(result);

into

ThrowIt("Hello World", result => {
     Console.WriteLine(result);
});

But what's more is the subtlety of the continuation itself, because of the whole plethora of closures implemented by the language. In boomerang terms, you're not just throwing the boomerang but all bacteria on your hand (the caller site environment) travel with it and might come back in some mutated form with the result. It's a bit different from the typical big red warnings trying to protect you from side-effects, since it's nicely disguised with a little "ought to be functional, yeah" lambda-arrow. Just try to reason about this one (and assume that you do know something or do not know anything about how ThrowIt operates):

string message = "Hello World";
ThrowIt(message, result => {
     Console.WriteLine(result);
});
message = "Goodbye World";

There should be a side-effect no matter what: how on earth would a void-returning method do something useful otherwise? In fact, I bet your very first (procedural) program you ever wrote was one of your biggest lies:

static void Main() { ... }

Right, you've just declared something that heats your processor's transistors by passing in nothing and returning nothing. Obviously it get's a bit better when you return an int :-).

 

Futures

After the ancient dinosaur and the today's functional (?) programmer's wet continuation dream, let's switch gears to a more futuristic approach. No more boomerang-style rendez-vous anymore, what about just expressing we want an operation to be carried out, giving us back something somewhere in the future. Or how you go to a store to order something and get a voucher to come and get it when it's ready (or when you decide to just sit and wait because you can't live any longer without it). This is something that get's enabled by the Parallel Extensions to the .NET Framework's TPL library, so consider TPL-inspired syntax below:

Future<string> GetGreetingCard(string to);

which is a function that houses a script monk creating greeting cards. Let's assume our script monk is called String::Concat (not sure the first name is appropriate for a monk though) and submit a request:

var result = GetGreetingCard("World");
// do other shopping
Console.WriteLine(result.Value);

Actually, you get more control over it - you just pass the monk what you want to share but he doesn't get to see anything beyond that. And ultimately when you come back, there's a result that's predictable given what you gave to the monk in the first place (assuming deterministic monks, a rare sort those days). If you just want to sit and wait, you don't do any shopping between sending the request and getting the result back:

Console.WriteLine(GetGreetingCard("World").Value);

Actually, behind the scenes of the monastery there could well be laziness indeed - between receiving a request and carrying out the calligraphic job might well lie lots of secrets hidden behind thick walls. Without further elaboration one could smell the air of a thunk coming up or even SASL style of laziness.

 

Join patterns

If you've had a rendez-vous with Ada in a previous life, you'll be familiar with the concept of join patterns (or shorthand joins). Our Microsoft Research department has done quite some work on this field already, as you can read here: http://research.microsoft.com/~crusso/joins/. It's part of Polyphonic C#, which is by itself part of Cw and based on join calculus. We already got the LINQ inspiration from it, joins might be lurking around the corner too. There actually just two basic concepts to understand here:

  • Asynchronous methods: imagine a keyword async as a modifier on methods (which would imply the method is a procedure, hence the void return type becoming redundant for those type of methods). No longer do you need to create separate versions of a method with or without the Async suffix to work around return type overloading limitations imposed by the runtime. You can think of the method as a wrapper around a task scheduling call, wrapping the entire body. Whether or not this spawns a new thread is another matter.
  • Join patterns: also known as chords, is a declarative way of a WaitAll style of guard mechanism to enter a method.

Our monk would look like:

class Monk
{
     public async RequestCard(string to) { ... }
     public string GetCard() & public async RequestCard(string to) { ... }
}

Basically requests for cards queue up on the monk's GetCard operation, ready to get processed when the monk is awake. Actually this sample is convoluted and our monk suffers from some concurrency diseases (think of the cards it would hand out if there are multiple outstanding requests - any ordering guarantees without "ticketing"?) but I won't get in details for now - it's just (yet) another way of thinking about asychronous processing with today's materials in the room. If you're inspired by join patterns, check out the Polyphonic C# paper.

Del.icio.us | Digg It | Technorati | Blinklist | Furl | reddit | DotNetKicks

Filed under: ,

Comments

# On Polyphonic C#: (aka nohup and Unix Pipes)

Tuesday, June 03, 2008 5:09 AM by O'Reilly XML Blog

It's amazing to me how seemingly complex programming techniques are really not that complex at all if you break them down into their basic Unix CLI equivalence. Take, for example, Polyphonic C# where they introduce the concept of the asynchronous...

# re: Continuations, futures, and whatnot - Thoughts on some asynchronous patterns

Tuesday, June 03, 2008 6:56 AM by shaun

Asynchrony

# re: Continuations, futures, and whatnot - Thoughts on some asynchronous patterns

Tuesday, June 03, 2008 12:33 PM by WillSmith

Check out the CCR in MS robotics.

msdn.microsoft.com/.../cc163556.aspx

# re: Continuations, futures, and whatnot - Thoughts on some asynchronous patterns

Tuesday, June 03, 2008 3:14 PM by Raghu

I read about Cw long time ago. Later I read polyphonic C# as well. Then came the CCR and it did simplify concurrency coding a lot. Now comes along PLINQ+TPL. Do all these technologies converge at some point? Or does PLINQ+TPL going to absorb all of the above?

# re: Continuations, futures, and whatnot - Thoughts on some asynchronous patterns

Wednesday, June 04, 2008 12:35 AM by bart

Hi Raghu,

There are definitely many things going on in the world of concurrency. Not only does the number of different angles to attack the problem increase in a steady state, the elephant in the room (being many-core and multi-core) is expanding as well. To some degree, we're facing a moving dragon with many faces to it, stretching from concurrency on single machines to distributed cloud computing stretching whole loads of machines.

Given these obversations, I'd say this is not yet the right time to start electing winners or provide definite answer as to which technology is superior to others. However, we can start lining up strategies and approaches that have a proven track record from previous intensive research and prior implementations, porting those to general purpose frameworks.

An example is definitely the TPL, which combines the lessons learned from the .NET ThreadPool implementation with inputs from various projects including the CCR and various sources ranging from operating system and database folks to research. It's definitely safe to say that the TPL and PLINQ are the first bigger rocks in the space of the .NET Framework that adress the challenges imposed by next-generation multi-core programming ("multi" pointing at multiple cores in a single box).

On the other side, there are definitely many open questions to be adressed, such as the concept of "purifying" languages to control shared mutable state. Research knows how to do it, product groups know how to productize stuff. It's just a matter of taking the right pieces and pushing them through incubation and productization to yield great results. Samples of such great cross-fertilization are LINQ with comprehensions (from Haskell and back) and recent evolutions in cloud computing (think of Volta tier-splitting as one aspect to it).

Hope this helps,

-Bart

# W&ouml;chentliche Rundablage: Silverlight 2, WPF, ASP.NET MVC, jQuery&#8230; | Code-Inside Blog

Pingback from  W&ouml;chentliche Rundablage: Silverlight 2, WPF, ASP.NET MVC, jQuery&#8230; | Code-Inside Blog

# Weekly Links: Silverlight 2, WPF, ASP.NET MVC, jQuery&#8230; | Code-Inside Blog International

Pingback from  Weekly Links: Silverlight 2, WPF, ASP.NET MVC, jQuery&#8230; | Code-Inside Blog International

# Websites tagged "asynchronous" on Postsaver

Thursday, September 25, 2008 9:02 PM by Websites tagged "asynchronous" on Postsaver

Pingback from  Websites tagged "asynchronous" on Postsaver

# Recent Faves Tagged With "asynchronous" : MyNetFaves

Saturday, January 03, 2009 4:20 AM by Recent Faves Tagged With "asynchronous" : MyNetFaves

Pingback from  Recent Faves Tagged With "asynchronous" : MyNetFaves