テストケースをファイルをロードしてテストするという風に変更したのだが、スレッド作って可能な限り同時に流した方が効率がいいよなぁと思い簡単なテストをしたらこけた。
っで調査。
問題になるのは、共有オブジェクト(DLL、so)で作られたライブラリを読み込むテスト。(意外な依存関係があったりするので、ピュアスキームだけマルチでって分けにはいかないのが辛い)。
なぜかその類のライブラリがあるとインポートエラーになる。ちょっと実装を覗いた。共有オブジェクトは一度呼ばれるとその情報が保存されて、同じオブジェクトを2度読み込まないようになっている。まぁこれはいい。っで、その中に初期化関数があるのだが、読み込みの際にそいつも一緒に呼ばれる。そうするとオブジェクトは初期化されたというフラグが立つ。ここに問題があった。
親スレッドが呼ぶ分にはまったく問題ないんだけど、複数の子スレッドで同一のDLLが呼ばれると問題になる。
この辺は多少以上に設計方針に関わっていて、子スレッドでインポートされたライブラリは親スレッドに影響を与えないという作りにしてあるのだが、これが拙い。兄弟間ではライブラリの共有が出来ないからだ。そうすると一度呼ばれたライブラリは2度と初期かされないので、兄が呼ぶと、弟が呼んでも答えてくれない。
解決策はまぁ、2つだろう。子供は親に迷惑をかけるものだと割り切るか、ターゲットを八方美人にするか。設計方針的には後者になるか。ただ、実際2度初期化をするとおかしくなるものもあるので、そこは気をつけないとまずいか。
大分影響範囲が広いのと、今後のことを考えるとやらない方がいい気がしてきた。初期化関数が煩雑になると多分後でバグを入れやすくなる。なのでとりあえずパスすることにしよう。
ちなみに、Gauche(0.9.2)で同様のことをしたらセグった。 想定外なんだろうなぁ、やっぱり。
さらに追記:
上記の問題はあらかじめ親スレッド側で子スレッドからインポートされるライブラリを呼び出しておけば回避できる。(Gaucheもそれで動いた)。
テストをマルチで走らせたければ、DLLはあらかじめ呼び出す必要がある。個別テストを考えなければ走らせる順番を換えればいけるということか。どうしよう。
追記その3:
プロセス全体でロードしたライブラリはルートのVMが持つハッシュテーブルを共有するようにした。とりあえず個別では動く。全部を一度に流すとまだマルチスレッドでは動かない。マルチスレッド周りは相変わらず動作が予測し難い。どこが問題だろう?
No comments:
Post a Comment