イベントループの結合手法

nmoshは非同期I/Oを実現するためにネイティブのイベントループを持っている。POSIXではpoll()ループであり、Win32ではIOCPを利用する。
通常のSocket通信やファイル/ストリーム I/Oはfd(Win32ではハンドル)によって行われるので、イベントループにはそれを待つ機能しか無い。
しかし、Gtk外部のGUIライブラリやlibusbのような抽象化レイヤは、fdやハンドル自体を抽象化してしまうため、ネイティブのイベントループにそのままでは統合できない。

FFI Thread

nmoshでは、"イベントを待つ専用スレッド"を作ることでこの問題を解決している。
nmoshのFFIにはこのために2つのインターフェースがある:

queue-invoke-ffithreadはイベントループの種類ごと - つまりpoll()版とIOCP版で同じAPIを実装している。nmoshには今後Darwin/FreeBSD向けにkevent()によるイベントループを追加するが、kqueueでもこのquerue-invoke-ffithreadを実装する必要がある。
queue-invoke-ffithreadはC関数ポインタとSchemeの関数オブジェクトを取り、新しいスレッドを作成してC関数を繰り返し呼び出し、そのC関数が結果を返すたびにScheme関数を*queue-invoke-ffithreadを呼び出したスレッドのコンテキストで*コールバックする。
FFIスレッドはイベントとして整数2つをcallback手続きに渡すことができる。poll()版ではpipeを使って単純にデータとして書き込んでいるが、IOCP版ではIOCPのポインタを渡す機能(PostQueuedCompletionStatus)を直接利用している。
スレッドイベントとして整数2つを利用できるのは、PostQueuedCompletionStatusが2つ整数を渡せることに由来する。
glibのイベントループ(GtkやGstreamerで使用される)の場合、イベントループ( http://developer.gnome.org/glib/stable/glib-The-Main-Event-Loop.html )は

  1. Polling
  2. Dispatching
  3. Initial
  4. Prepared

の4状態に分割されていて、FFI Threadはこのうちpolling状態だけを取り扱うことになる。glibはこのようにイベントループの機能を分割提供しているおかげで、コールバックをVMのスレッドコンテキストで動かすことができる。
このFFIスレッドのインターフェースは全く直感的でないが、moshのスレッドインターフェースを使ってVMスレッドを増やすよりは省コストで、スレッド間でやり取りするのもSchemeヒープオブジェクトでないのでオーバヘッドが少ない。

fdを使う手法

もっとも、常識的なライブラリではイベントループのためのfdを外部に提供することが普通と言える。glibやlibusbにはこのインターフェースがある。
ただ、fdはI/Oのキャンセル等の特殊なイベントにおいての扱いがOSごとにまちまちなので、微妙に使いづらい。
また、この手のインターフェースはWindows向けの考察を欠いていることが多いのでnmoshでは使えない。