週刊nmosh - PDCursesのスレッド対応 / GStreamer SDKに対応

MinGWビルドできない不具合を直しました。。あと、最近のpdcurses + Win32での不具合にも対処。

PDCursesのスレッド対応

nmoshでは、Curses実装としてWin32上ではPDCursesを使用する。特に、GUI上ではwin32aバリアント( http://www.projectpluto.com/win32a.htm )を使用している。
従来、このwin32aスレッドはwindowの面倒を見るスレッドを独自に確保する実装になっていた。これが、June 2バージョンから廃止されinitscr(スクリーンの確保)を呼び出したスレッドでWindowを生成するようになった。
この変化はnmoshに取って都合が悪い。通常のシチュエーションではinitscrするスレッドとキーを読み取るスレッドが異なるため、キーイベントを受け取れなくなってしまう。
Windowsでは、windowに対するイベントを受け取るのはwindowを作成したスレッドに限定されている。nmoshでは、cursesのキーイベントの読み取りはFFI Thread( http://d.hatena.ne.jp/mjt/20120311/p3 )で行なっているので、メインスレッドでinitscrすると、キーイベントを読み取るスレッドとwindowを確保するスレッドが異なってしまう。
今回は、最初にキーイベントを読み取る時に同時にinitscrするようにして回避したが、生成したオブジェクトのイベントを受け取るのがオブジェクトを作成したスレッドに限定されているケースは意外と多いのでFFI Thread(= イベントの受け取りだけを他のスレッドにやらせてAPIを非同期化するというアイデア)の弱点を露呈する結果になってしまった。

結果コールバックパターンとletrec

nmoshでは先のcursesやsocket APIで、"ハンドルをコールバックで返し、イベントコールバックを呼び始める"パターンがかなり多い。
現在の非同期構文(yuni async)では、このパターンを綺麗に書けない。

(define self #f)
(seq (=> screen-create
         (^[type arg0 arg1 arg2 arg3] ;; Cursesスクリーンのイベントハンドラ
           (screen-put self (format "~w\n"
                                    (list type arg0 arg1 arg2 arg3))))
         => screen)
     (set! self screen)) ;; この行が実行されるまで(= 初期化中に)イベントハンドラが実行されないことは保証されている

screen-create手続きは、screenの準備が出来るとコールバックを呼んでそのハンドルを伝える。しかし、ハンドル自体はイベントコールバックの中で呼びたいケースもある。
APIの改善は2通り考えられる:

  • screenオブジェクトをcalleeに作成させ、createに渡す

これはあまりstraight forwardでない気がする。また呼び出さないといけないAPIが増えるという問題もある。

  • イベントコールバックにハンドルも含む

こちらは、本当に必要なのは一度だけなのにずっと含め続けるのはあまり頭の良い方法では無いような気がしている。もっとも、socket APIではコールバックにfdも含むようにしている。これは特に狙いはなく、単純に統一感がない。
seq構文にletrecに相当するものを足して隠蔽するのが良いように思えるが、いまのところうまい記法を思いついていない。
最初からletrec風のスコープにしてしまうのも悪くない気がする。(↑の例では、screenのスコープがscreen-createに渡しているlambdaの中にも続くことになり、selfの代わりにscreenと書ける)

preキャッシュの分割

preキャッシュをcoreと通常の2種類に分割しました。coreはnmosh標準ライブラリのために予約、一般のアプリケーションは通常キャッシュを使うことを推奨ということで。
Windowsディストリビューションコンパイル済のcoreキャッシュを同梱する方向に。まぁ通常は標準ライブラリを書き換えることは無いし。。
標準ライブラリをコンパイルすべきかは絶妙なところで、従来は基本的に必要最低限のものだけをprecompileして持つという方向にしていた。ただ、実用性という観点から行くと、可能な限り標準ライブラリはコンパイルする必要がある。(特にWindows上だと、アプリケーションをネットワークドライブに入れてそこから起動する(!)というユースケースが有るので、起動時のstat()を減らすのはHDDに比べてより重要になってしまう)

GStreamer SDKに対応

CMakeビルドではGStreamer SDKを自動的に検出するように。cairoやGObjectで使用する。現在のところWin32のみ対応。
GStreamer SDK自体にはMac OS X版もあるので、そちらにも対応したいところ。
Win32版のnmoshはPFFIを使用するときに自動的に自分のpluginsディレクトリにパスを通すので、その動作を使用してGStreamerを同梱したnmoshを配布することができる。