Read/Writeロック、ブロックするQueue、色付き非同期Logger

分散ファイルシステムはマルチスレッドで作っているわけですが、マルチスレッドなら必ず使うであろう諸クラスを作成。作成と言うか、V-FIELDを作ったときに必要なものは大方作ったので、改良が主。

Read/Writeロックは、普通のRead/Writeロック。C++と言えばRead/Writeロックくらいboostにあると思ったら、無いので作成。

ブロックするQueueというのは、popするときに中身が空だったら、何か入ってくるまで待つ、というもの。V-FIELDには、キューの実装に、STLのqueue+boostのshared_ptrを使っていたのですが、どうもboost::ptr_dequeの方が効率が良いらしいので、変更。これでptr_blocking_queue。int型などはポインタにすると逆に効率が悪いので直にコピーした方が良い、ということで、キューの実装に普通のstd::dequeを使ったblocking_queueも作成。



で、本題は色付き非同期Loggerであります。

プリエンプティブなマルチスレッドはデバッグが難しいと言われますが、やはり基本はログだと思っています。ログと言うかprintfデバッグと言うか。そんなに違わないと思っていますが。
ブレークポイントを設定するより、調べたい値を全部printfしてしまえばいいわけで。さらに良いのは、いつかここにブレークポイントを設定したくなるだろうな、という場所があれば、とりあえずそこにprintfを大量に入れておくこと。何か起こってから、問題がありそうな場所を探して、ブレークポイントを設定して、もう一回実行して、再現しない!とやるよりも、最初からprintfしていればいい。

もちろん、リリース用のビルドではプリプロセッサでコードから抹消します。ついでにこのプリプロセッサでファイル名と行番号も出力するようにしておくと便利。

それになにより、ログが怒濤のごとくダダーッと流れてくると、なかなか爽快です。


というわけでひたすらログを吐くわけですが、そうすると性能が目に見えるくらいまで落ちます。やっぱりこれくらい吐かないとダメです。
しかしデバッグ時でもここまで遅くなってしまうと、再現する確立が低いバグがあったときに、いつまで経っても発現しなくて困ります。
そこで、ログは非同期にしようというもの。あらゆるスレッドからどんどんキューに追加していきつつ、独立したスレッドを回してコンソール(と、一応syslogにも対応)にひたすら書き出す。


非同期ともう一つのポイントは、ログに色を付けること。コンソールのエスケープシーケンスを使って、ログのレベルに応じて赤、マゼンタ、黄、白、灰と色を付けます。重要なログはすぐに目にとまるようにして、逆にどうでもいいログは目にとまらないようにするところがポイント。
この色づけは非常に良くて、VIVERでもRubyで実装しています。ひたすらログを吐いているので、赤かマゼンタの文字が出てきたら前後のログを調べる、という作業で、だいたい挙動はつかめます。ログが多すぎて分かりにくいときは、grep -vでもすればOK。ログを吐きすぎて悪いことはまず無いはず。
ただし非同期なので、Segmentation Faultされるとログが出力されません。そういうところはデバッガやcatchsegvなどを使ってカバー。


それから、boost::formatが非常に有用です。std::iostreamはどうにもいちいち面倒でいけない。それにboost::formatはprintfよりも良くて、%dにするか%lldにするか%sにするかも考える必要がない。単に表示させるだけなら、%1%などと指定するだけ。独自定義のクラスも、少し手をかけてoperator<<を定義しておけば、そのまま表示できます。複雑な書式も何も悩まず1行で書けるので、普通にマクロにいれてプリプロセッサで処理できます。