著作一覧 |
Ruby拡張ライブラリと、Ruby、それからRuby拡張ライブラリが依拠するDLLの3つのプレイヤーがいる場合に、Rubyが落ちる場合と落ちない場合がある。
通常、まずいのは次の組み合わせだ。
RubyがリンクしたMSVCRTと拡張ライブラリがリンクしたMSVCRTが異なる。
これは、mallocとfreeのMSVCRTの実装による。
・もしかすると、MSVCRT70以降は共通化されているかも知れないが未確認。
・理屈上で確認しているのは、MSVCRT(6)とそれ以降の任意の1つとの組み合わせ
これは、次の状況で発生する(追記あり)。
・拡張ライブラリでマクロを利用してオブジェクトを生成する(具体的にはData_Make_Struct)。マクロなのでmallocの呼び出しは拡張ライブラリ側のMSVCRTで行う。
・GCで上記オブジェクトを回収。GCのfreeなのでRuby側のMSVCRT側で行う
それに対して、拡張ライブラリが利用するサードパーティDLLはRubyオブジェクトを利用することはないので、よほど生なAPI(mallocしたオブジェクトを返すのでfreeは呼び出し側でやってくれ、というようなもので、しかもWin32APIではなくMSVCRTのmallocを利用する)が無ければ問題ない。
のだが、readlineのMSVCRT(6)が、rubyとreadline.soをMSVCRT(100)で利用すると落ちるのだった。
で、なんでだか追っかけたら、fdの問題だった。うむ。そう来たか。
というか、メモリーアロケーションにしろ、ファイルハンドルにしろ、なぜ素のWin32のオブジェクトを使わないのかと(それは、POSIXそっくりさんのMSVCRTと、Win32の設計思想の相違をMSVCRTが吸収しているからだと言えばそれまでなのだが)
追記:
と思っていたのだが、今見ると、ALLOCはxmallocで、xmallocはgc.cのruby_xmallocだから、拡張ライブラリとRuby本体で異なるmallocを使うということは無いようだな。
ということで、上のは、ファイル関係以外は嘘っぱちということだ。
ジェズイットを見習え |
一番ありがちなのはerrnoとか
ああ、確かに。昔々、winsock.hで、#define errno get_errno() /*正確かどうかはわからない*/ というのを見たけど、変数の参照はその通りですね。
errnoについてはもう少々複雑というかもう一段階挟まってて、MSVCRT内で使うTLSに取られてるわけですが、そのTLSのためのインデックスがバージョンごとに別なのでerrnoも別になってるという状態です。<br>というか、VCはコンパイラ自身がTLSをサポートしてるはずなので、素直にそっちを使っててくれれば問題なかったんじゃないかと。
そんなことになっているとは。<br>(ちょっと見てみる)__flsindexと__tlsindexで別管理だからってことかな。これをすべてのDLLで共有するとしたらTlsAllocの戻り値をシェアできる領域にコピーしておく必要があるけど、MSVCRTの段階でtlsindexをシェアしないようにしているから既に遅かったということでは(VC10にはdefファイルが入っていないから現在はどうかわからないけど)。
>VCはコンパイラ自身がTLSをサポートしてるはず<br>これって、__declspec(thread) int errno; という意味?
そういう意味。どのバージョンからサポートしたのか分からないけど。
VC6ではサポート済みでしたね(仕事で使ったので覚えてる)。でも、これもDLL単位になりそうな。
あぁなるほど、dllexportにはできないというわけか。<br>確かに、特定のsegmentに対するoffsetだと、DLLが複数になったら解決できないな。
少なくともVC6にはありましたねえ。<br>とりあえずこのURLは見ておくといいかな。特に一番下の項とか必見。<br>http://msdn.microsoft.com/en-us/library/2s9wt68x.aspx
これって、Vista以前は、DLLでは事実上declspecディレクティブは使えないって意味なのかな? と思ったが、暗黙のうちにリンカに解決(アプリケーションロード時に動的にロード)されればOKってことか。つまりRubyの拡張ライブラリではTlsAllocしなければだめということか(tlsを使うのがあるかどうかは微妙な気がするけど)