週刊nmosh - 非同期FFI関数呼び出し / makeIntegerとC++オーバーロード

Win64対応で逆に32bitビルドが壊れたので修正に追われるでござるの巻。

async FFI call

pthreadでは非同期FFI関数呼び出しをサポートしている...といってもこれは単純に通常のFFI呼び出しをメッセージパッシングインターフェースにしただけ。つまり、通常のFFI呼び出しが:

  • call(関数ポインタ, パラメタ, 戻り値バッファ)

の3点セットで行われるので、これをキューに詰めているだけとなる。
非同期FFI callのメリットは通常の同期FFI呼び出しと完全に等価であることで、プログラム自体の修正を伴わずに一部のFFI関数だけ非同期呼び出しに変えるといったことが出来る。
もちろん、単純に非同期呼び出しに変えるだけではあまり意味が無いが、chime( http://d.hatena.ne.jp/mjt/20120916/p1 )と組み合わせることで単一のbindingを同期/非同期の両呼び出しに対応させることができる。
...これはVMがマルチスレッド化されていれば、VM自体を複数のスレッドで走らせることで実現できることでもある。ただ、近代的なスレッディング様式ではVMの多重度とスレッドの多重度にギャップを生じるケースがある。典型的な例はスレッドプールAPIと言える:

pthread_workqueueはDarwinで採用されているpthread向けのスレッドプールAPIでlibpthread_workqueueはWin32や他のpthreadで動作するポータブルなライブラリになっている。pthread workqueueインターフェースは、単純に関数ポインタをenqueueできるだけのインターフェースとなっており、キュー毎にVMコンテキストを作る隙が無い
Vista以降のWin32にはもっと複雑なThread pool APIが有る。

Thread pool APIはIOCPや他の同期オブジェクトと統合されているのでかなり強力だが、APIの移植性を保ったままマッピングするのが地味に難しい。

C++オーバーロードの難しさ

コードではmakeInteger関数がC++コード中でscheme整数オブジェクトを作るのに使われている。
問題は、makeIntegerが様々なオブジェクトに対して使われているので、移植性を持つためには全部のパターンが必要なこと。
これだけのバリエーションが有る:

    static Object makeInteger(int n)
    {
        if (Fixnum::canFit(n)) {
            return Object::makeFixnum(n);
        } else {
            return Object::makeBignum(n);
        }
    }
    static Object makeInteger(unsigned int n)
    {
...
    }
// FIXME: We don't need (u)intptr_t makeInteger for
// sizeof(int) == sizeof(uintptr_t) archs
#if MOSH_BIGNUM_SIZEOF_INTPTR_T != 4
    static Object makeInteger(intptr_t n)
    {
...
    }
    static Object makeInteger(uintptr_t n)
    {
...
    }
#else // ... But we need long int variants
    static Object makeInteger(long int n)
    {
...
    }
    static Object makeInteger(unsigned long int n)
    {
...
    }
#endif //  MOSH_BIGNUM_SIZEOF_INTPTR_T != 4

なんてこった。(...の部分は中身が同じなので省略)
if - endifで切っているのは、uintptr_tとunsigned longのオーバーロードが共存できるかどうかが環境によって異なるため(!)。uintptr_tとunsigned longって違う型なんじゃないかと思うけど、環境によってはuintptr_tはunsigned longのtypedefなので。。
原理的にはsize_tなどまだまだ多くのバリアントで必要になってしまう。簡単には、(u)intptr_tとfixedintだけ持ち、他の型ではCスタイルまたはstatic_castなキャストを使うことで改善できるが、如何せん使用箇所が多いので。。