bwerf's Blog

It's plukalicious
posts - 14, comments - 12, trackbacks - 0, articles - 0

Friday, January 02, 2009

Blogging seems to be one of those things i feel i should be doing and with the new year I'm giving it some more attention.

Microsoft TechEd EMEA 2008 in Barcelona was interesting for me. Joe Duffy was there talking about his new concurrency book, a topic that I'm very interested in. A very small but fun session with a large group of core visual studio developers also gave some insights into their thought process.

But most of all a small presentation of CCR DSS, a system made for distributed and concurrent programming that isn't receiving nearly enough attention, mostly due to it having been developed within the robotics team.

One of the things i noticed in the presentation was that they used "yield return" in a way it wasn't designed for. (the best way to use a feature :)

"yield return" can be used to make a normal function turn into a generator:

http://msdn.microsoft.com/en-us/library/9k7k7cf0.aspx

The compiler does a lot of transformations for a piece of code that uses a yield return, transformations that can be used to construct continuations without too much pain.

http://en.wikipedia.org/wiki/Continuation

http://blogs.msdn.com/oldnewthing/archive/2008/08/12/8849519.aspx

When writing server-like programs it is often useful to use continuations to handle a protocol or asynchronous code, where the thread yields the continuation when an operation would have to wait for completion. To do this in c# you can yield return a waitable future of the operation that would block, a top level handler could then decide which continuation is runnable based on this future and execute it. This is more powerful than implementing this using reentrancy because the handler is free to run any continuation it can not just the top most, also the stack wont be exhausted because the continuations use the heap to store method scoped variables.

Example of yield return usage:

void Main() {
Handler.Invoke(Save);
Handler.Invoke(()=>Process("save"));
}

IEnumerable Save() {
using (var file = new File()) {
yield return file.BeginWrite();
file.EndWrite();
}
}

IEnumerable Process(string command) {
if (command == "save")
foreach (var y in Save()) yield return y;
}

Simply calling the Save method would construct an Ienumerable representing the continuation, but not actually invoke Save, this is a serious risk with this pattern, one that a language extension for c# could solve.

The use of the foreach with the yield return in Process is used to pump the waitables, a language extension, for example "yield Save();" would make this more usable.

In this implementation of Process the foreach yield pattern isn't needed, but it helps because it places the execution of Process and Save into the some model, if a simple return was used the Proces method would complete before Save would be invoked, now Save gets executed within the flow of Process in the place it is called.

Exception handling and try/finally is somewhat broken when using yield return, using seems to function, which is surprising since it uses try/finally internally too.

Some issues with the ordering, synchronization and locking start to arise when using this pattern, as with any other system that introduces some form of concurrency.

Clear advantage of this pattern over simply using threads is that it is explicit where the flow is interrupted, so it is easy to keep pre/post-conditions consistent.

The future that the continuation yields can be a task executing on another thread, very handy in places where CPU dependant tasks can be shown to be safely executable on multiple cores with little synchronization.

This made me wonder if the OS switches to another thread when a thread hits a hard page-fault and it needs to do disk IO, probably yes.

posted @ 9:56 PM | Feedback (0)