週刊mosh - 非同期IO整理中 / CMakeプロジェクトの読取サポート

ちょっと忙しくて隔週刊になりつつある週刊mosh
いろいろ方向性が迷走している気がするんですが、非同期I/O APIは色々なところで使えたほうが便利そうということでLinux/Cygwinも最初からサポートすることにしました。

Githubのpull request

Githubのpull requestをmergeしました。https://github.com/higepon/mosh/pull/3
運用ルールはそのうちid:higeponと話し合わないとなぁとは思ってますが、基本的にはモリモリお願いします。

非同期I/O

非同期I/Oバックエンドはlibuvやlibeventを検討したんですが、結局mosh独自のものを持つことにしました。細かいロジックをschemeで書けるのがメリット。。
yuniの構文を使って型検査をしているので、初期の実装は異常に遅いです。まずはプログラミングモデルを検討するのが目的。
最終的(0.3.x ??)には、

  • Full R6RS をサポートしたmosh VM
  • R6RSの一部を省略するかわりにメモリを節約したRealtime I/O VM
    • 暗黙のbignumやO(1) string access

の独立した2つのVMを用意して常識的なパフォーマンスを目指す方向性。

非同期I/OバックエンドがC++でなく(BoehmGCに依存しない)Cで書かれているのはこの分割を行うためです。まずはバックエンドをMosh VMで完成させて実際のアプリケーションを書いた上で、I/O VMの仕様を検討する予定。
まだ実際に遊べるだけのサポートライブラリを準備してないですが、0.2.8ではwebrick風のhttp serverのような遊べるライブラリを装備してデビューできる見込。

Cygwinサポートが地味に難しい

実は非同期I/OのAPIMacOS XFreeBSD、WinNTを基準に考えています。つまり、これらのOSではfd(= ファイルハンドル)以外に、タイマやプロセス、ファイル更新イベントも独立したI/Oプリミティブなので、非同期 I/OのAPIもこれら特有のAPIを持ちます。
ところが、LinuxCygwinでは、基本的にfdしか待ち受けることが出来無いので、タイマやプロセスのイベント受信は何らかの方法でエミュレートする必要があります。
Linuxの場合、signalfdやtimerfdやeventfd、inotifyのようなそのものズバリのインターフェース - つまり、ファイル更新イベント等のイベントをfdへの書き込みに見せるAPI - が存在するのでそれらを使えばOK。
が、Cygwinの場合はそういう気の利いたAPIは実装されていないので、self-pipeと呼ばれるテクニックでエミュレートしています。つまり、signalfdでSIGCHLDを受信するのをエミュレートするなら、

  1. pipe()でパイプを作る
  2. sigactionで"SIGCHLDが来たらパイプに1バイト書きこむ"というハンドラを登録する
  3. パイプの他端をpoll()する

これを実装する過程で、"Linuxのパイプは一方通行"という事実をすっかり忘れてて地味に苦労しました。。BSDでは両方向なので、BSDで開発したものをLinuxに持っていったときに動かなくて焦った。

CMakeプロジェクトの読取

"CMakeの出力したMakefile集合をパースしてターゲットとコマンドを抽出するライブラリ"を追加しました(R6RS汎用)。
まだ抽出されたターゲットとコマンドを実行する機構が無いので役には立たないですが、clangとlibgit2を統合したmoshを作って、

  • ビルドのコマンドラインを記録
  • ビルドに使用されたファイルのgit hashと生成された.oのhashを記録
  • Cフロントエンドへのオプションを解釈し、クエリに応じて改変(最適化レベルの変更等)

するシステムを実装しようとしています。ビルドログをソース行や.oをキーにしてクエリしre-playすることで、

  • 一部の関数だけデバッグオプションを有効にしてプロジェクトをビルド
  • ビルド時に記録していなかったLLVM-IRをビルド後に生成
  • LLVMのコード変換passを、条件にマッチした関数にだけ適用
  • 特定のwarningが消えたリビジョンを検索する

といったことを可能にするのが目的。
このContent Addressed Build Query(と呼んでいるシステム)は結構昔から構築してるんですがdeployが複雑でなかなか支持を集められない(し、個々のプロジェクト専用なので公開もできない)ので一念発起してCMakeで書かれたプロジェクトなら一般的に使えるようにしよう。と。
今回gitとclang+llvmにはfirst classで対応しようとしているので、"ある関数が最適化によって消滅したrevision"のような非常に強力なクエリも原理的には対応できるはず。。ただ、clang+llvm自体で試したところ2ヶ月分のビルド結果ログを保持するだけで40GB近いストレージが必要になってしまったのでLevelDBのような圧縮KVSの導入を検討中。
このライブラリはまだ最適化していないので、clang+llvmのプロジェクトをパースするのに12分くらい掛かります。。また、mingw32-make向けのMakefileがまだ読めないのでその辺を修正予定。