Let's start Scheme

2015-07-16

あとらんだむ

時間があるときにまとめてとも言う。

MSVCとスタック

最近AppveyorというWindows用CIサービスでもCIをし始めたのだが(なぜかx64版が0xc0000005で落ちるのだがなぜだろう、起動すらしてない感じに見えるのだが?)、試用期間が終わって無償版に自動移行した際に起きたスタックオーバーフロー例外が起きるようになった。起きている場所は毎回同じ。移行される前は動いていたテストなので不思議な感じがしていたのだが、原因がCPUが1個にされたためにテストケースがシングルスレッドで走ったため発覚したバグだった。

ちょっと前にCPUが1個のときはテストケースをシングルスレッドで走らせた方が速いということを確認したので、ランタイムでCPUの数を確認して適当にディスパッチさせるようにしたのだが、これが原因(もしくは功を奏したの)だった。問題になったのは10万くらいネストしたリストの書き出し。これくらいネストするとC側のスタックが尽きるのでスタックの深さが既定値(メインスレッドで1MB、子スレッドで64KB)を超えたら&i/o-errorを投げるようにしていたのだが、これが上手いこといっていなかった。MSVCは既定では1MBしかスタックを割り当ててくれないので、リンカオプション(具体的には/STACK)で8MBを指定していたのだが、どうもこのオプションでは上手く行かなかったらしい。とりあえずどれが上手く動くのか分からないのでC_FLAGSとCXX_FLAGSに/Fオプションを渡し、さらにCMakeが持っている2つの内部リンカフラグ両方に/STACKオプションを追加した。これで試すと手元の環境ではスタックオーベーフロー例外は起きなくなったのだが、Appveyor上ではまだ起きる。起動時にスタック領域を制限されるのか、Windowsサーバーの仕様なのかは不明だが起きるものは起きる。

どうしたものかと適当にGoogle先生にお伺いを立てたところ以下の記事を発見。

How to trap stack overflow in a Visual C++ application

_try_exceptを使って例外を捕捉したらVirtualFreeとVirtualProtectでスタック領域を元に戻してやるというもの。これいけるんじゃね?と思い早速追加。

https://bitbucket.org/ktakashi/sagittarius-scheme/src/031c6cbaf9c75df5815b7885a2c9ba5c34bb119f/src/writer.c?at=default#cl-1065

インラインアセンブラの部分は適当に変更(そうしないとx64ではコンパイルできない)。なんとなく動いているようである。

これとStackWalk64を使うとスタックトレースが取れそうな気がしないでもないので、組み込んだら原因不明の0xc0000005の発生場所が特定できるかもしれないなぁ。

タイムゾーン

どうもPOSIXやMSVC付属のCRTにあるタイムゾーン関係のAPIは使いにくい。というか、基本的にはタイムゾーンオブジェクトみたいな風にできないようである。別にシステムの時間とかを変更したいわけではなく、単にある地域のGMTオフセット等が知りたいだけなのだが、そういう使い方がやりづらい。Javaだとjava.util.Timezone(Java8からは非推奨になったけど)等があるのでなんとかできるのだろうとちょっと調べてみた。

結果、やれるけど割りと茨の道。Javaは自前のタイムゾーンDBを持っててそこから情報を取得している。Java8からはtzdb.datというファイルに変更されて、更新があってもとユーザがJREの更新を待つ必要がなくなったというのはあるが、OSから取得していないというところは一緒である。

タイムゾーンの情報は現在はIANAが管理していて、データ及びPOSIXの時間関係のソースはパブリックドメインで公開されている。そこに山があると登りたくなるのが登山家であれば、そこに問題があると何とかしたくなるのがプログラマである。別に面白みがあるものではないが、OSが上手いことAPIを提供してくれないのであれば、自前でなんとかせねばあるまいということである。

とりあえずFTPサーバーからデータファイルを落として、そいつらをS式に落とし込む部分までは書いたので後は落とし込んだ情報を元に時間をなんとかするだけなのだが、そこが面倒ともいう。とりあえずは、GMTオフセットと夏時間が取得できればいいのだが、今週末(というか明日)予定 している0.6.6のリリースには当然間に合わない。ただ、tzdb.dat相当のファイルをリポジトリに入れたくない(自動生成されるファイルを入れたくない)ということから、ここまでは今のうちにやっておかないと(0.6.6が提供するAPIのみで書けるようにしておくという意味)間延びするか、ポリシーに反してリポジトリを汚すかの2択になってしまうので。

IANAのデータ、閏秒のデータも入っているから何とかすれば外だしにできそうなのだが、何を血迷ったのかC側に入れてあるのでちょっと辛い気がしないでもない。まぁ、もう少し機能をScheme側に押し出してやればやれなくもないのだが、どうしようかね。


なんかもう一つ書くことがあった気がしたのだが、完全にわすれたようなのでオチもなくお終い。

No comments:

Post a Comment