Sunday, November 4, 2012

Concurrent Hello World in Go, Erlang and C++

This week I started taking a deeper look at the Go programming language since I am somewhat addicted to scalability, reliability, concurrency and message passing :-). The first thing I always do when playing around with a new software platform is to write a concurrent "Hello World" program. The program works as follows: One active entity (e.g. thread, Erlang process, Goroutine) has to print "Hello " and another one "World!\n" with the two active entities synchronizing with each other so that the output always is "Hello World!\n". Here is the concurrent Hello World program in Go, Erlang and in C++ using the Mindroid framework.

Go
package main

import "fmt"

func main() {
    sayHello := make(chan string)
    sayWorld := make(chan string)
    quitter := make(chan string)

    go func() {
        for i := 0; i < 1000; i++ {
            fmt.Print("Hello ")
            sayWorld <- "Do it"
            <-sayHello
        }
        sayWorld <- "Quit"
    }()

    go func() {
        for {
            var reply string
            reply = <- sayWorld
            if (reply == "Quit") {
                break
            }
            fmt.Print("World!\n")
            sayHello <- "Do it"
        }
        quitter <- "Done"
    }()
 
    <-quitter
}

Erlang
-module(hello_world).

-export([start/0, say_hello/2, say_world/0]).

say_hello(0, WorldPid) ->
    WorldPid ! quit;

say_hello(N, WorldPid) ->
    io:format("Hello ", []),
    WorldPid ! {sayWorld, self()},
    receive
        sayHello ->
            say_hello(N - 1, WorldPid)
    end.

say_world() ->
    receive
        quit ->
            quit;
        {sayWorld, HelloPid} ->
            io:format("World!~n", []),
            HelloPid ! sayHello,
            say_world()
    end.

start() ->
    WorldPid = spawn(hello_world, say_world, []),
    spawn(hello_world, say_hello, [1000, WorldPid]).

C++ (Mindroid)
#include <stdio.h>
#include "mindroid/os/Message.h"
#include "mindroid/os/Handler.h"
#include "mindroid/os/LooperThread.h"

using namespace mindroid;

static const int SAY_HELLO = 0;
static const int SAY_WORLD = 1;
static const int QUIT = 2;

class HelloHandler : public Handler
{
public:
  HelloHandler(const sp<Handler>& worldHandler) : 
    mWorldHandler(worldHandler), mCounter(0) {}

  virtual void handleMessage(const sp<Message>& msg) {
    switch (msg->what) {
      case SAY_HELLO:
        mCounter++;
        if (mCounter <= 1000) {
          printf("Hello ");
          sp<Message> sayWorld =
            mWorldHandler->obtainMessage(SAY_WORLD);
          sayWorld->metaData()->putObject("SayHello",
            obtainMessage(SAY_HELLO));
          sayWorld->sendToTarget();
        } else {
          mWorldHandler->obtainMessage(QUIT)
            ->sendToTarget();
          Looper::myLooper()->quit();
        }
        break;
    }
  }

private:
  sp<Handler> mWorldHandler;
  int mCounter;
};

class WorldHandler : public Handler
{
public:
  virtual void handleMessage(const sp<Message>& msg) {
    switch (msg->what) {
      case SAY_WORLD:
        printf("World!\n");
        msg->metaData()
          ->getObject<Message>("SayHello")
          ->sendToTarget();
        break;
      case QUIT:
        Looper::myLooper()->quit();
        break;
    }
  }
};

int main() {
  sp< LooperThread<WorldHandler> > wlt =
    new LooperThread<WorldHandler>();
  wlt->start();

  Looper::prepare();
  sp<Handler> hh =
      new HelloHandler(wlt->getHandler());
  hh->obtainMessage(SAY_HELLO)->sendToTarget();
  Looper::loop();

  return 0;
}

I think I really like Go (although Erlang is a bit more crafty :-)). Go eases developing scalable and reliable distributed systems while being a good to read language. Since you can also handle much more concurrent activities with one machine using Goroutines (compared to plain operating system threads) it also helps in saving energy.

Links