HP-UX の recv(2) をみると、HP-UX には 3種類の non-blocking I/O があるそうな。
<URL:http://docs.hp.com/en/B2355-90693/recv.2.html>
即座に読めるデータがまったく存在しないときに、
という 3つのどれになるかという話だが、POSIX 的に O_NONBLOCK でやったときには EAGAIN なので、Errno::EAGAIN を rescue しないといけない (Errno::EWOULDBLOCK だけでは済まない) 環境は存在するということだな。
0 を返すのは情報が不足しているので使うべきでない。TCP なら EOF と区別できないし、UDP なら空のパケットと区別できない。
昨日ひろのぶさんに指摘されたのだが、GNU/Linux で /sys/devices/system/cpu/cpu0/cpufreq/scaling_available_frequencies がうまく読めないという。
% ruby -ve ' Thread.new { sleep } File.read("/sys/devices/system/cpu/cpu0/cpufreq/scaling_available_frequencies") ' ruby 1.9.2dev (2009-03-13 trunk 22936) [i686-linux] (ここでブロックして終わらない)
以前 /proc/loadavg とかで似たようなことがあって、それは Linux の問題として (2.6.13 で) 直ったんだけどなぁ
ということで strace して確認するとまぁ今回も似たような話である。
... open("/sys/devices/system/cpu/cpu0/cpufreq/scaling_available_frequencies", O_RDONLY|O_LARGEFILE) = 3 ioctl(3, SNDCTL_TMR_TIMEBASE or TCGETS, 0xbf9fa6b8) = -1 ENOTTY (Inappropriate ioctl for device) fstat64(3, {st_mode=S_IFREG|0444, st_size=4096, ...}) = 0 _llseek(3, 0, [0], SEEK_CUR) = 0 select(4, [3], NULL, NULL, NULL) = 1 (in [3]) read(3, "1400000 1300000 1200000 1100000 1"..., 4096) = 62 select(4, [3], NULL, NULL, NULL
read した後 select して、そこで読み込み可能にならなくてブロックしている。
まぁ、ruby 側で fstat して普通のファイルにみえるものは select しないという案もなくはないのだが。
以前とちょっと違うのは、read_nonblock があることである。
% ruby -ve ' Thread.new { sleep } open("/sys/devices/system/cpu/cpu0/cpufreq/scaling_available_frequencies") {|f| begin loop { p f.read_nonblock(4096) } rescue EOFError end }' ruby 1.9.2dev (2009-03-13 trunk 22936) [i686-linux] "1400000 1300000 1200000 1100000 1000000 900000 800000 600000 \n" %
read_nonblock は select しないので、問題なく EOFError まで読める.
Perl の 2038年対応を実装してみる。
といっても読んだのはこの記事だけで、実装は見ていないので、中身はいくらか違うかもしれない。
最初、28年周期とあるのに 2012〜2037 とあって 2037-2012+1=26 で合わないので間違いかな、と思ったりもしたが、最終的には手元で行った実装も 2012〜2037 になった。
しかし、試してみるといくつかの timezone でうまくいってもおかしくないのにうまくいかないところが見つかった。(しょせんかなり乱暴な話なので、うまくいかないところはやはりあるのだが、そうではないところで見つかったという話である。)
調べてみると、zoneinfo がなんか変である。2037年の情報が gmtoff=0 になっていることがあって、そこを使っちゃうと変になるようだ。
% zdump -v Egypt|tail Egypt Thu Apr 24 21:59:59 2036 UTC = Thu Apr 24 23:59:59 2036 EET isdst=0 gmtoff=7200 Egypt Thu Apr 24 22:00:00 2036 UTC = Fri Apr 25 01:00:00 2036 EEST isdst=1 gmtoff=10800 Egypt Thu Aug 28 20:59:59 2036 UTC = Thu Aug 28 23:59:59 2036 EEST isdst=1 gmtoff=10800 Egypt Thu Aug 28 21:00:00 2036 UTC = Thu Aug 28 23:00:00 2036 EET isdst=0 gmtoff=7200 Egypt Thu Apr 23 21:59:59 2037 UTC = Thu Apr 23 23:59:59 2037 EET isdst=0 gmtoff=7200 Egypt Thu Apr 23 22:00:00 2037 UTC = Fri Apr 24 01:00:00 2037 EEST isdst=1 gmtoff=10800 Egypt Thu Aug 27 20:59:59 2037 UTC = Thu Aug 27 23:59:59 2037 EEST isdst=1 gmtoff=10800 Egypt Thu Aug 27 21:00:00 2037 UTC = Thu Aug 27 21:00:00 2037 UTC isdst=0 gmtoff=0 Egypt Mon Jan 18 03:14:07 2038 UTC = Mon Jan 18 03:14:07 2038 UTC isdst=0 gmtoff=0 Egypt Tue Jan 19 03:14:07 2038 UTC = Tue Jan 19 03:14:07 2038 UTC isdst=0 gmtoff=0
とりあえず報告しておく。
Debian GNU/Linux BTS #522949: TZ=Egypt date -d 'Dec 1 00:00:00 2037' prints UTC date.
おそらく、time_t の終端の処理が変なんじゃないかという気がする。
実際に危険なのは 2037年なようだから、2037年の情報を使わないという回避はありうるかもしれない。
うまくいかないところは、たとえば America/Sao_Paulo である。
あー、結局ブラジルで問題が起きたか、という感想はあるが、それはおいておこう。
たとえば、失敗するのは 2040-02-19 02:00:00 UTC である。
64bit time_t な環境では、
% zdump -v America/Sao_Paulo|grep 2040 America/Sao_Paulo Sun Feb 19 01:59:59 2040 UTC = Sat Feb 18 23:59:59 2040 BRST isdst=1 gmtoff=-7200 America/Sao_Paulo Sun Feb 19 02:00:00 2040 UTC = Sat Feb 18 23:00:00 2040 BRT isdst=0 gmtoff=-10800 America/Sao_Paulo Sun Oct 21 02:59:59 2040 UTC = Sat Oct 20 23:59:59 2040 BRT isdst=0 gmtoff=-10800 America/Sao_Paulo Sun Oct 21 03:00:00 2040 UTC = Sun Oct 21 01:00:00 2040 BRST isdst=1 gmtoff=-7200
となるので、2040-02-19 02:00:00 UTC は 2040-02-18 23:00:00 BRT になってほしい。
が、32bit time_t な環境では、2040年の情報は提供されないので、2037年以前の情報で適当に代用する.
どこで代用するかというと、カレンダーが一致するところである。
% cal 2 2040 2月 2040 日 月 火 水 木 金 土 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
2040年は閏年で、2月は水曜日から始まる。これと同じ条件は 2012年に起こる。
% cal 2 2012 2月 2012 日 月 火 水 木 金 土 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
というわけで、2012年で代用する。
が、2012年と 2040年は切り替わりのタイミングが違うのだ。
% zdump -v America/Sao_Paulo|grep 2012 America/Sao_Paulo Sun Feb 26 01:59:59 2012 UTC = Sat Feb 25 23:59:59 2012 BRST isdst=1 gmtoff=-7200 America/Sao_Paulo Sun Feb 26 02:00:00 2012 UTC = Sat Feb 25 23:00:00 2012 BRT isdst=0 gmtoff=-10800 America/Sao_Paulo Sun Oct 21 02:59:59 2012 UTC = Sat Oct 20 23:59:59 2012 BRT isdst=0 gmtoff=-10800 America/Sao_Paulo Sun Oct 21 03:00:00 2012 UTC = Sun Oct 21 01:00:00 2012 BRST isdst=1 gmtoff=-7200
切り替わりは、2012年は 25日だが、2040年は 18日と異なる。(どちらも現地時間)
その結果として、期待とは異なるタイミングで切り替わってしまう。
まぁ、この失敗は仕掛けから導かれる必然であろう。
うぅむ。time_t 終端近辺は腐ってるな。
% TZ=right/EST5EDT date -d @2140668023 Sun Nov 1 01:59:59 EDT 2037 % TZ=right/EST5EDT date -d @2140668024 Sun Nov 1 01:00:24 EST 2037
Debian GNU/Linux BTS #523280 - localtime doesn't count leap seconds near 2038
spawn の話をした。
資料の中で、:nice をつけるとしたら、という話が書いてあるが、ちょっと実装を検討してみる。
すると、setpriority が async-signal-safe でないことに気がついた。
うぅむ。正しくやるには fork した後に親が setpriority するようにしないといかんのか?
でもそうすると setpriority が失敗したときの扱いに困るな。
型レベルプログラミングの会 の資料を眺める。
D言語の CTFE は良いと思った。(問題が起きたら自己責任ということで、制限を緩めてもいいとは思ったが)
コード生成は面倒くさいので、一般に、コンパイル時の計算はもっと支援されるべきだと思う。
でも、それを型でやるのがいいのかはよくわからない。計算は値でやった方が簡単じゃないかなぁ。整数とかはすでにあるから自然数から作り直す必要もないし既存のライブラリも使える。
まぁ、そうすると型システムとどうつなぐのかという問題はある。型を生成するくらいはやりたいだろうし、もっとなにかしたいとすればどういう形でやるのかはいろいろ考えなければならないだろう。
実装されていないメソッドを respond_to? で判定できるようになったので、chkbuild で出している組み込みメソッドのリストに、実装されているかどうかも出すようにした。
で、実装されていないメソッドとしてなにが出てきたかというと、いくつかの環境で以下のようになった。
GNU/Linux: File.lchmod Process::Sys.issetugid Process::Sys.setrgid Process::Sys.setruid FreeBSD: なし NetBSD: Kernel.fork Process.fork Process::Sys.setresgid Process::Sys.setresuid Process::Sys.setrgid Process::Sys.setruid OpenBSD: File.lchmod Process::Sys.setrgid Process::Sys.setruid Solaris: File.lchmod Process::Sys.setresgid Process::Sys.setresuid Process::Sys.setrgid Process::Sys.setruid
おぉ、FreeBSD はぜんぶ実装されてるのか。
ところで、インスタンスメソッドが実装されているかどうかを確認するのは厄介であった。たとえば、Dir#tell が実装されているかどうか respond_to? で判断するには Dir のインスタンスが必要である。しかし、今回は Dir のインスタンスがあるわけではない。それなら Dir.allocate すればいいというのはそうなのであるが、Bignum など allocate できないクラスもある。(あと、module は allocate できないので別扱いする必要がある)
なので、インスタンスメソッドに関しては inspect の結果で判断することにした。が、これはいやなので、やはり UnboundMethod#implemented? が欲しいところだ。
今日から、LP64 環境では syscall が not-implemented になったようだ。
chkbuild の (とある LP64 環境の) 結果で気がついた。
chkbuild で出しておくと、diff で変化に気がつけるので便利である。
発表されてすでに 1週間たっているが、Google SoC で、FSIJ は 3件採択した。
そのうち 1件は Rubots というプロジェクトである。
まず、Player/Stage というプロジェクトがあって、実機のロボットとシミュレータ内のロボットを抽象化するミドルウェアを提供している。これを利用して書いたロボットの制御プログラムはプログラムの変更なしに実機でもシミュレータ内のロボットでも制御できる。
この制御プログラムを Ruby で書けるようにしよう、というのが GSoC でのプロジェクトである。
で、(メンターとして) とりあえず Player/Stage を使ってみる。
まず、Debian GNU/Linux (lenny) には、Player/Stage のソフトウェアがいくつかパッケージで用意されてるのでそれを使ってみる。robot-player と stage というのがそれなのでインストールして、 マニュアルの Quick start を読みながら試してみる。
コマンド名がちょっと違う (player が robot-player になっているとか) が、動くようだ。地図が出て、その中でロボットを手動で動かせる。
自動で制御する例が Quick start の最後に出てくるのだが、その制御プログラムはインストールされないらしいのでソースから作らないと試せない。
というわけでソースから player と stage をビルドする。依存するライブラリをいくつか入れれば、./configure; make; make install でうまくいく。とくに問題はない。(実際には configure に --prefix をつけたが)
んー。Debian のコマンドとはうまく通信できない? バージョンが違うから? まぁ、あまり気にしないことにしよう。
自前でビルドしたものだけで動かせばとくに問題はない。
あと、gazebo という 3D のシミュレータがあるということなのでこれも試す。これは lenny にはパッケージになっていないのだ。
ビルドしようとすると... おや、scons ですか。まぁいいんだけど、prefix はどう指定するのだろうかと調べると、scons prefix=path install らしい。
あとは (prefix を指定しているので) PKG_CONFIG_PATH の設定が必要だったり、#include <string.h> を追加したりといった些細なところをクリアしてビルドできた。
gazebo のマニュアル を見て試すと、なんとか動く。とりあえず 3D で表示される中のロボットを手動で制御できるのはできた。
struct tm の tm_gmtoff はいつ導入されたか?
BSD では、4.3BSD Tahoe のようだ。1988年。
1986年の 4.3BSD にはない。
[latest]