...ing logging 4.0

はてなブログに移行しました。D言語の話とかいろいろ。

スレッドセーフでない銀行クラス

import std.stdio;
import core.thread;

class Bank
{
	private int money_;
	
	void payin(int m)
	{
		money_ += m;
	}
	void payout(int m)
	{
		money_ -= m;
		if (money_ < 0)
		{
			// クラスの利用者が注意していればここには到達しないはず?
			throw new Exception("pay out exception.");
		}
	}
	int money() @property
	{
		return money_;
	}
}

void main()
{
	auto bank = new Bank;
	auto tg = new ThreadGroup;
	bool isRunning = true;
	
	// 100ずつ出金する人
	tg.create = {
		while (isRunning)
		{
			try
			{
				if (bank.money >= 100) // 残金を確認して
				{
					foreach (_; 0..10000){} // 異常が起こりやすいように待つ
					// お金があるときだけ出金するはず?
					bank.payout(100);
				}
			}
			catch (Exception e)
			{
				isRunning = false; // 両方のスレッドを終了する
				throw e;
			}
		}
	};
	
	// 100ずつ入金する人
	tg.create = {
		while (isRunning)
		{
			bank.payin(100);
		}
	};
	
	// スレッドを回す
	tg.joinAll();
}

D言語で「Java言語で学ぶデザインパターン入門【マルチスレッド編】」を学ぶ

本を読みながらコードを書いていきたい.


Mutexは単にひとつのスレッドからしか実行できないようにするもの.
Conditionは明示的に通知をしてスレッド間で同期を取ることができるもの.
Conditionの方がMutexよりも細かい制御ができる.


コードが間違っていたので訂正.
wait/notifyを呼ぶときにはロックを取得していなければならない.
これはどういうことかと言うと,Conditionに渡したMutex mをロックするためにsynchronized (m)ブロックを作って,その中でwait/notifyを呼ばなければならないということ.


また,notifyを呼び出しても,waitしているスレッドがすぐに m のロックを再取得して処理を継続するわけではない.
notifyを呼び出したときには「notify を呼び出したスレッド」がmをロックしているので,wait側は m をロックすることができないから.
notify側が m のロックを解放するタイミングをちゃんと用意してあげること.

import std.stdio;
import core.thread;
import core.sync.mutex;
import core.sync.condition;

// 別のスレッドでカウントを3回表示するまで待ってから終了する
void main()
{
	auto m = new Mutex; // 排他処理用のmutexを作って
	auto c = new Condition(m); // conditionに渡す
	void f()// スレッドのエントリポイント
	{
		foreach (i; 0..3)
		{
			writeln("\t", i); // カウントを3回表示して
		}
		synchronized (m) // cじゃなくてmのロックを取る
			c.notify(); // mに対するwaitを起こす
		writeln("\tend"); // このスレッドを終了
	}
	auto t = new Thread(&f); // スレッドを作成
	t.start(); // スレッド開始
	writeln("0");
	synchronized (m) // cじゃなくてmのロックを取る
		c.wait(); // mに対してnotifyが呼ばれるまで停止する
	writeln("END");
}

本来これだけの処理ならばメインスレッドで t.join() を呼び出してスレッドの終了を待てば十分だろう.


マルチスレッドを勉強しながらなのでこれからも間違える可能性が高いでしょう.

D言語会議#1

京都から遠路はるばる行ってきました.

全体の流れ

最初に,@repeatedly さんが唯一スライドを使ってアジェンダをプレゼンして,D言語の現状的なものを説明.
Phobosには通信処理を書くための機能が不足しているけど,etc.c.curlが入ったのでそれを使えばもうちょっとマシな通信処理が書けるんじゃないかとか.
その後は @kinaba さんが toHash 辺りの問題を取り上げてた.
あとPhobos I/OやGCについて話してるグループもいたみたい.


僕は同じDFLユーザである @mono_shoo さんと隣同士になったので,DFLとdllのあれこれについて話してた.
DFLのコントロールをdllから返すとか考えたこともなかった.
でもdllから例外を投げるとmain側でcatchできなくて困るらしい.
それとVisualDが結構使えるとか.流石Visual Studioさんやで!
デバッグにはバリバリ使っているけど,キーワード補完があったら打ち込みに使ってもいいかなと思ってる.
あとはTangoにあるしPhobosにもcryptが欲しいよね,って話になったのでTangoで実装されるのを待ってたことがある VFS が欲しいと思ってると言ったらなんか割と好感触だったのでよし.


I/Oについて少し話を聴けましたが,あまりまとまらなかったように思いました.
I/Oは他の言語でも迷走しているらしく,D言語もそういうところまで追いついてしまったんですよね.


GCについては Andrei が投稿されてるパッチを放置せずに当ててくれたらマシになるのに,なんて話が上がってた気がします.
パッチを当てるにはテストが足りないとも言っていたような.


std.datetime くらいでかいものになると,unittestをソースの近くに書けるのはメリットじゃなくなってきてしまうね,とか.
やっぱりまともなIDEが欲しくなりました.
dmdjsonファイルを吐くようになっているので,それを使ってメソッド一覧を出すIDEがもうあるんじゃないのかな.


あとはぐだぐだ話してたらいい時間になったので2次会で晩ご飯を食べに行って,電車の時間になったので僕は途中で抜けて帰ってきました.
お疲れ様でした.

個人的な感想

僕は構文木から意味解析する辺りの処理がどうなってるのか全然わかってないので,toHash 辺りの問題をさらっと直してた方々がいたみたいなのは流石だなーと思った.
僕も読めるようになりたいですはい.
GUI界隈はどうなってるの?と何度か質問されましたがDFLしか使ってないのでGUIライブラリ全体のことはよくわからず.
まともなノートPCを持って行けなかったのが痛恨のミスで,dmdを使っているのにコンパイルにめちゃ時間かかるし,外部ディスプレイ出力端子がないという有様.
まじめにプレゼンする準備くらいして行けばよかったですね!

構造体クイズ(2)

構造体クイズ1のときにはstatic opCallを含めていたのですが,もうこの仕様はないと考えていいのでそれなしで試してみました.
ちょうど,一時構造体のデストラクトが行われないバグが修正されたことですし.

import std.stdio;
struct X
{
	this(int i){writeln("X.this(int)");}
	this(this){writeln("X.postblit");}
	ref X opAssign(X){writeln("X.opAssign(X)");return this;}
	ref X opAssign(Y){writeln("X.opAssign(Y)");return this;}
	~this(){writeln("X.~this");}
}
struct Y
{
	this(int i){writeln("Y.this(int)");}
	this(this){writeln("Y.postblit");}
	ref Y opAssign(Y){writeln("Y.opAssign(Y)");return this;}
	ref Y opAssign(X){writeln("Y.opAssign(X)");return this;}
	~this(){writeln("Y.~this");}
}

void main()
{
	{
		writeln("-1-");
		Y a;
	}
	writeln();
	{
		writeln("-2-");
		Y a = Y();
	}
	writeln();
	{
		writeln("-3-");
		Y a = 1;
	}
	writeln();
	{
		writeln("-4-");
		Y a = Y(1);
	}
	writeln();
	{
		writeln("-5-");
		Y a, b;
		a = b;
	}
	writeln();
	{
		writeln("-6-");
		Y a;
		a = X(1);
	}
	writeln();
	{
		//Y a = X(); // not compilable
		//Y a = X(1) // not compilable
	}
}

実行結果はこうなりました.

-1-
Y.~this

-2-
Y.~this

-3-
Y.this(int)
Y.~this

-4-
Y.this(int)
Y.~this

-5-
Y.postblit
Y.opAssign(Y)
Y.~this
Y.~this
Y.~this

-6-
X.this(int)
Y.opAssign(X)
X.~this
Y.~this

1と2が同じ,3と4が同じ.
あれ? こんなにシンプルだったっけ? って思うくらいになってます.
でも5で3回デストラクトされているのは何だろう?

D言語用GUIライブラリDFLをgithubにマイグレーションしました

本家はdsource.orgのsvnで管理されているのですが,しばらく更新がないのでgithubリポジトリを作りました.

基本ポリシーとしては,

  1. なるべく早くD2 trunkに追随する
  2. D2ののリリース版が出た時点でtagを付ける
  3. D1のサポートはしない

あたりを考えています.

今のところソースはとりあえず動けばいいやってレベルになってます.
あしからず.

dsource.orgのプロジェクト活性度ランキング(2011.02.06版)

http://dsource.org/projects/ の全プロジェクトの最終更新日時から作ったプロジェクト活性度ランキングの2011.02.06版です.
ちなみに,dmd, druntime, およびphobosは既にgithubで管理されており,dsource.orgでの更新は止まっています.
少し古いコード: http://ideone.com/qX6ce

続きを読む

-debug=の挙動

そういえばデバッグレベルを設定したことなかった.

import std.stdio;
void main()
{
	debug    writeln("void");
	debug(0) writeln(0);
	debug(1) writeln(1);
	debug(2) writeln(2);
}

これを色々な debug オプションを付けて実行すると・・・

>dmd a -debug && a
void
0
1

>dmd a -debug=0 && a
0

>dmd a -debug=1 && a
void
0
1

>dmd a -debug=2 && a
void
0
1
2

デバッグレベル 0 でコンパイルすると,デバッグレベルを指定していない debug{} のコードがコンパイルされない.

まとめ

  • debug(N){} は debug=N に対応する.
  • ただし (N) がないとき -debug=1 と等価,また,N=0 のとき debug{} も除かれて debug(0){} だけがコンパイルされる.

疑問

もしかして,-debug=0 の 0 は Identifier として解釈されている?