pFFIライブラリ規約

現状のnmosh pFFI呼び出し(!= mosh本来のFFI)はかなり非効率的で、呼び出しの度にアロケーションが発生する。これをC stack側に取ってアロケーションを減らし、ついでにもうちょっとマシなエラーセマンティクスを持たせたい。

C APIに要求するポイント

nmosh pFFIではOS本来のsyscallを直接exposeするのではなく、一旦Cのライブラリを噛ませている。これは他のSchemeに移植しやすくするため - 本来I/O VMとして独立したVMを用意する予定で始めたものなので - で、Gaucheのようなscheme側にsyscall I/Fを見せているSchemeとは微妙に異なる方針と言える。
なので、以下の条件を満たすようなラッパを手で書く必要がある。これは一見面倒だが、複数のVMで同一のC stubを使った場合に、stubの界面でC用のパフォーマンスツールが使えるという地味なメリットが有る。(例えばC APIの呼び出し回数をCプロファイラでカウントする等)

  • 大域脱出をしないこと

nmosh pFFIにはC継続の概念がない。これはWin32のAPC(Asynchronous Procedure Calls - callbackの一種)やFibersのように既存の実行コンテキストに干渉しないものだけに対象を絞ることで単純化している。言い換えれば、全てのFFI呼び出しは呼び出しコンテキストを高々1回のみ消費する
Win32のSEHのような例外を除けば、これは大概のC APIで原則的に守られている。C++ APIはちゃんとcatchを書いて保護する必要がある。

  • 結果とエラーを同時に返すこと

UNIXやそれに影響を受けたWin32のsyscallは一般にエラーと結果を同時に返さない。つまり、POSIXのwriteは失敗したら単に -1 を返し、詳細なエラー情報を得るにはスレッドローカル変数 errno を参照する必要がある。
このとき、1) syscallの呼び出し 2) errnoの取得 のように操作を2つに分けるのは得策でない。1と2の間にデバッグ情報出す→writeされる→errnoが上書きされる のような危険がある。
エラー情報を返す可能性のあるsyscallは全て多値を返すことになる。C言語において多値を返すイディオムは、int*等返り値を返すためのバッファを渡して関数を呼ぶことで、単純にこのバッファをbytevectorで確保することを避けたい。

  • エラーは(基本的に)数値であること

これを行うことで、(エラーファシリティ, エラーフェーズ, エラー番号)の組みでAPI中に発生したエラーを識別(& 文字列化)することができる。
これは難しいポイントで、意外とこの原則を守れないライブラリが有る。
いわゆるerrnoでエラーを返すsyscallは簡単にこの原則を守ることができる。また、Win32のGetLastErrorやOpenGLのglGetErrorのように統一されたcallを持っている場合も同様と言える。一般に、この手のGetLastErrorのような操作はsyscallを伴わないことが期待される。常識的なOS/ライブラリでは、エラーコードはTLSに格納し、エラーコード取得APITLSからエラー番号を読み取るだけの操作になっていることが殆どといえる。
この原則に従わないライブラリの代表例としてはdlerrorが有る

The messages returned by dlerror() may reside in a static buffer that is overwritten on each call to dlerror(). Application code should not write to this buffer. Programs wishing to preserve an error message should make their own copies of that message.

dlerrorの場合、エラーを表示するためにはメッセージをコピーする必要があるが、毎回エラーメッセージが必要なわけでもないため悩ましいポイントとなる。dlerrorのようなAPIは、例えば解決できなかったシンボルやエラーのあったライブラリ名のような任意の文字列をエラーメッセージ中に含めるためにこのようなAPIとなっている。