Node#

The node.js project is a rapidly evolving effort to build a runtime for writing event-driven servers in JavaScript. It’s part of a general JS renaissance, growing from a widening appreciation of the positive aspects of JS compared with other well-established languages such as C/C++ and Java. These include closures and dynamic typing, though I’d argue the former is much more important than the latter.

Due to the fact that server-side JavaScript is still in its infancy, node.js has to specify everything from scratch. For example, there is currently no widely accepted standard for linking together multiple independent JavaScript source files into one program (though it may be on the way). Nor is there a standard library for accessing files or network resources.

But the node.js project regards this blank slate as a crisitunity to lay down the law. No operations will be allowed to block – instead they return an object called a promise, which is basically a bundle of standard events (such as “success” and “error”) that you can listen for.

Event-driven programming is certainly not going to come as a big shock to those who’ve written GUI applications on Windows or the Mac. But in server-side programming it’s perhaps less widely used. One key restriction of JavaScript right now is that there should only be one thread executing the script, so thread-based parallelism is effectively ruled out. But this is not necessarily a problem, because web servers spend most of their time waiting for an OS service to complete, whether it’s sending/receiving on a socket or reading from a file. Given this, a single-threaded state machine is likely to be more efficient and scalable than a system that uses a thread to manage each client. As long as the CPU work is fairly minimal, it may never be necessary to share it between multiple threads. So it’s just another crisitunity: no threading, but maybe that’s a good thing. (It certainly is in my opinion; I’ve long regarded shared-memory concurrency as a practical joke that has backfired with tragic consequences.)

There appears to be some active debate on the node.js mail list about whether promises are the right way to go (they certainly don’t make composition particularly smooth). But nevertheless node.js can already make some working examples appear to be delightfully clear and simple.

Right now if you want to play with node.js on a Windows system, the easiest approach is to start it up in a VM. The reason is that they are not really ready with a Windows port. The non-JS parts of the source are written to the POSIX API which is (of course) different in almost every detail from Win32.

But this is not a problem, because maybe the spirit of node.js can still be valuable even if the specific implementation is not the same. On Windows there is a well-established language, C#, that has closures just like JS.

JS:

var counter = 0;
var increase = function(amount) { counter += amount; };
increase(3);
increase(4);
// counter now has value 7

C#:

var counter = 0;
Action<int> increase = amount => counter += amount;
increase(3);
increase(4);
// counter now has value 7

(Note that we have to specify the type of the increase variable – C# has pretty good type inference, but it’s deliberately simple in some ways to keep error messages from becoming incomprehensible).

In fact it goes one better because it also has a built-in concept of events (lists of closures that can be executed with a single call so they all get the same arguments). This raises the possibility of supporting the creation of event-driven servers in C# in which the code will have the same pattern as node.js servers. The .NET platform certainly has all the asynchronous and thread-pool APIs we need.

So mostly for fun I’ve written a small library which is inevitably called Node#.

Here’s a very simple web server – actually it’s barely a web server at all, but it’s close enough to fool a browser:

Process.Run(p =>
    p.Listen(8726).Connect += client =>
    {
        client.ReadLine().Success += line =>
        {
            string body = "Zoidberg says: \"Screw you!\"\r\n" +
                          "(responding to " + line + ")";

            client.Write(
                "HTTP/1.0 200 OK\n" +
                "Content-Type: text/plain\n" +
                "Content-Length: " + body.Length + "\n" +
                "\n" +
                body);
        };
    });

You can download all the code here.

Build it and start NodeSharpCmd.exe, and then hit this URL in your browser:

http://localhost:8726/wiggles

Your browser will display:

Zoidberg says: "Screw you!"
(responding to GET /wiggles HTTP/1.1)

The first important concept from node.js is that our application code is single-threaded, so we never need to implement locks to control access to shared data. And yet in the background the library is using background threads like crazy. The solution to this contradiction is that there is one thread running an event loop, which reads arbitrary units of work from a queue and executes them. When any background thread wants to raise an event, it must post it into the queue so that it will be extracted and executed in the event loop thread.

To allow this, the guts of the library must have a channel for cross-thread communication:

public class Channel<T>
{
    private readonly Queue<T> _queue = new Queue<T>();

    public void Enqueue(T item)
    {
        lock (_queue)
        {
            _queue.Enqueue(item);
            if (_queue.Count == 1)
                Monitor.PulseAll(_queue);
        }
    }

    public T Dequeue()
    {
        lock (_queue)
        {
            while (_queue.Count == 0)
                Monitor.Wait(_queue);

            return _queue.Dequeue();
        }
    }
}

This could be used to pass any kind of value between threads, but in our case we want to pass units of work. In C# an arbitrary unit of work is an Action delegate:

var channel = new Channel<Action>();

An event loop is then extremely simple:

for (;;)
    Channel.Dequeue()();

That is, we repeatedly pull an action from the queue and then execute it, which is why we have another pair of parentheses on the end. The Dequeue method blocks while the channel is empty, so when another thread posts an action to the channel, the event loop will immediately wake up and execute it.

So from a background thread, we can easily “move” an operation into the event loop thread:

channel.Enqueue(() => Console.WriteLine("Hi!));

Another useful idea is the promise, which is the typical return value of a non-blocking operation. An operation either succeeds, in which case it produces zero or more useful results, or it fails, in which case it produces an Exception object. As C# is a statically typed language (and doesn’t yet have an automatic syntax for declaring variadic generics) we need a few versions of a Promise class with different numbers of arguments for the Success event. For example, here’s a simplified version of the two-arg promise:

public class Promise<TArg1, TArg2> : PromiseBase
{
    public event Action<TArg1, TArg2> Success = delegate { };

    public void EmitSuccess(TArg1 arg1, TArg2 arg2)
    {
        Success(arg1, arg2);
    }
}

So, armed with promises and an event loop, we can define the IO system. For simplicity I’ve gone for a more homogeneous approach than node.js, in that I use the same interface for files and sockets:

public interface IFileDescriptor
{
    Promise<byte[]> Read(int length);
    Promise Write(byte[] data);
    Promise Close();

    Process Process { get; }
}

(Note that the Process object is available via a property; we need to pass it around so that we can post to the event loop.)

Another difference with node.js is that we defer the question of text encoding and just use the native byte array type for the data in streams. We then have extension methods for convenience:

public static Promise Write(this IFileDescriptor fd, string text, Encoding encoding)
{
    return fd.Write(encoding.GetBytes(text));
}

public static Promise Write(this IFileDescriptor fd, string text)
{
    return fd.Write(text, Encoding.UTF8);
}

Extension methods augment an interface, making it appear to have additional methods. So now we can write a string to any file descriptor and it will by default become UTF-8:

fd.Write("Hello, world");

Of course, we still need to provide an implementation for each low-level method, but this is a fairly simple pattern of wrapping a call to an asynchronous method on a .NET stream object:

public Promise<byte[]> Read(int length)
{
    var buffer = new byte[length];
    var promise = new Promise<byte[]>(_process);

    _fileStream.BeginRead(
        buffer,
        0,
        length,
        a =>
        {
            try
            {
                _process.Channel.Enqueue(() =>
                     promise.EmitSuccess(buffer.Trim(_fileStream.EndRead(a))));
            }
            catch (IOException x)
            {
                _process.Channel.Enqueue(() =>
                    promise.EmitError(x));
            }
        }, null);

    return promise;
}

The asynchronous methods in .NET always start with a BeginXXXX method, to which a callback is passed. But the callback executes in some random thread, which is not what we want. So we supply a “middleman” callback that just posts the real callback (the raising of the Success event) to the event loop. We also catch any IO-related exception and use it to raise the Error event.

That is the basic pattern that is always followed to wrap an asynchronous operation in a promise. But note that a promise is suppose to only be used once. When you have a callback that should be called many times, define a different class with specific events. For example, the Process class has a Listen method that returns a Server object, which in turn has a Connect event that fires whenever a TCP/IP client connects to the listening socket managed by the Server object.

The only other thing used in the simplified web server example is ReadLine, another extension method. It’s an example of a composition on top of other promises, and I’ve given it a really inefficient (but working) implementation, reading one character at a time until I’ve built up a whole line:

public static Promise<string> ReadLine(this IFileDescriptor fd, Encoding encoding)
{
    var promise = new Promise<string>(fd.Process);
    var bytes = new List<byte>();
    Action readChar = null;

    readChar = () =>
    {
        var read = fd.Read(1);
        read.Error += x => promise.EmitError(x);
        read.Success +=
            data =>
            {
                if (data.Length != 0)
                {
                    if (data[0] != '\n')
                    {
                        bytes.Add(data[0]);
                        readChar();
                        return;
                    }
                }

                fd.Process.Channel.Enqueue(() =>
                    promise.EmitSuccess(
                    encoding.GetString(bytes.ToArray()).TrimEnd('\r', '\n')));
            };
    };

    readChar();
    return promise;
}

To do composition, you have to forward on the events to the outer promise. In my simplified implementation there are only two, so it doesn’t seem so bad. I define a local function readChar that repeatedly calls itself until it hits a newline.

And that’s all there is to it. Probably not directly useful in production code, but maybe it has pedagogical value, translating the concepts of event driven IO programming into yet another language.

Advertisements
  1. Eric Siroker
    May 14, 2010 at 1:20 am

    Nice work, Daniel!

  2. Arun
    November 14, 2013 at 2:46 pm

    Nice work, but the link to the source is broken Can you update it?

  1. No trackbacks yet.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: