D言語でMVarしだした

前に参加したHaskellによる並列・並行プログラミング読書会にて、

で、やってみようと思いました。C言語はリソース管理とか色々めんどくなったのでD言語にしました。バージョンはv2.066.1です。mallocさん、さようなら。
まずは、MVar作りました。例外とかはガン無視です。

MVarはProducer-ConsumerパターンのChannelの事です。

f:id:nihma:20150223225540p:plain

以下コードです。増補改訂版Java言語で学ぶデザインパターン入門マルチスレッド編の5章の”Producer-Consumer - わたしが作り、あなたが使う”を見ながら実装しました。

// MVar.d
module MVar;

import core.sync.mutex;
import core.sync.condition;

class MVar(T)
{
  private Mutex m;
  private Condition c;
  private T[] value;

  this()
  {
    value = null;
    m = new Mutex;
    c = new Condition(m);
  }

  void put(T v)
  {
    synchronized(m) {
      while (value != null) {
        c.wait();
      }
      value = [v];
      c.notifyAll();
    }
  }

  T take()
  {
    synchronized(m) {
      while (value == null) {
        c.wait();
      }
      auto v = value[0];
      value = null;
      c.notifyAll();
      return v;
    }
  }
}

とりあえず、Haskellによる並列・並行プログラミングの7.3の”簡単なチャネルとしてのMVar:ログサービス”をやってみました。

// Logger.d
import std.stdio;
import std.variant;
import core.thread;
import MVar;

class Logger
{
  struct Message { string message; }
  struct Stop    { MVar.MVar!(bool) m; }
  alias  Command = Algebraic!(Message, Stop);

  private MVar.MVar!(Command) m;

  this()
  {
    m = new MVar.MVar!(Command);

    auto t = new Thread(
    {
      while (true) {
        auto cmd = m.take();
        if (cmd.type == typeid(Message)) {
          auto msg = cmd.get!Message.message;
          writeln(msg);
        }
        else if (cmd.type == typeid(Stop)) {
          auto s = cmd.get!Stop.m;
          writeln("logger: stop");
          s.put(true);
          break;
        }
      }
    });
    t.start(); 
  }

  void message(string msg)
  {
    Message message = { message:msg };
    Command cmd = message;
    m.put(cmd);
  }

  void stop()
  {
    auto s = new MVar.MVar!(bool);
    Stop stop = { m:s };
    Command cmd = stop;
    m.put(cmd);
    s.take();
  }
}

void main()
{
  auto l = new Logger;
  l.message("hello");
  l.message("bye");
  l.stop();
}

動いたー

$ dmd Logger.d MVar.d
$ ./Logger 
hello
bye
logger: stop