Joe White's Blog Life, .NET, and cats

Coroutines in C# and Delphi: Part 1 #.NET #Delphi

I've been thinking about some of the cool things I could do with coroutines, especially with regards to unit tests. So over the weekend, I wrote a coroutine library in C#.

If you haven't dealt with coroutines ("generators" for the Python crowd), they're interesting beasts. You create a coroutine and call it, much like you would call any other routine; and it runs for a while, and then sends a return value back to its caller. But it doesn't actually return; instead, it just "yields" the value back to the caller. The difference is that after a yield, the coroutine's state — local variables, etc. — still exists, and later, you can tell the coroutine to resume where it left off, and run a while longer, until it yields again (or, in the end, returns for real).

If you've read about C# 2.0 iterators, you've got the basic idea. But coroutines are much more powerful. In particular, I believe that a C# iterator is a single routine, and if you have any yield statements, those have to occur inside that single routine. (Correct me if I'm wrong — I don't actually have Whidbey installed, and haven't tested this. But based on what I've read about them, I believe this is correct.) But if you're using a coroutine, that yield can come anywhere: when you yield a coroutine, it's not just the local variables that are saved for later, it's also the call stack.

Which means, for example, that coroutines can use recursion to their heart's content. If I had something like this:

public class TreeWalker : Coroutine {
    private TreeNode _tree;
    public TreeWalker(TreeNode tree) { _tree = tree; }
    protected override Execute() {
        Walk(_tree);
    }
    private void Walk(TreeNode tree) {
        if (tree != null) {
            Walk(tree.Left);
            Yield(tree);
            Walk(tree.Right);
        }
    }
}

Here I'm doing a recursive in-order traversal of my binary tree... but the call site doesn't need to deal with the recursion — or even care that there's recursion going on. The call site would just look something like this:

foreach (TreeNode node in new TreeWalker(tree)) { ... }

If you've read Raymond Chen's series of articles on using fibers to simplify enumerations, this should all sound very familiar; he was basically using fibers to implement coroutines. I did the same thing, but avoiding fibers (more on this later, but read the dire warnings at the end of Raymond's third article).

I'll post the code a little later, once I've got a few more features added (like the ability to deal with exceptions inside the coroutine). I'll also be doing a Delphi version (probably both Win32 and .NET).