5.5 です。

IO#flock について,どうも腑に落ちない点があります。

複数のプロセスから一つのファイルにデータを追記する必要が生じた
ので,そのやり方を調べていたところ,処理系によって IO#flock の
動作が違いました。

以下の三つで実験しました。

(A) ruby 1.8.7 (2009-06-12 patchlevel 174) [i386-mswin32]
(B) ruby 1.9.1p243 (2009-07-16 revision 24175) [i386-mswin32]
(C) ruby 1.8.7 (2009-06-12 patchlevel 174) [i686-darwin10]

それぞれ mswin187,mswin191,mac187 と書くことにします。
(A), (B) は Windows XP,(C) は Snow Leopard です。

以下のスクリプトを用意しました。

-- lock1.rb --
open("log.txt", "a+") do |f|
  f.flock File::LOCK_EX
  f.puts 1
  sleep 5
  f.puts 2
end
------

-- lock2.rb --
open("log.txt", "a+") do |f|
  f.puts 3
end
------


lock1.rb を走らせた直後に lock2.rb を走らせれば,以下のように
動作する,と期待して実験しました。

[1] まず lock1.rb が 1 を書こうとする。flush はしてないので,
 バッファーに書かれるのみ。
[2] lock1.rb は 5 秒間のスリープに入る。
[3] lock2.rb が 3 を書こうとするが,ロックされているので,書
 けない。待機する。
[4] lock1.rb が 2 を書こうとする。ファイルを閉じる際に実際の
 書き込みが行われる。
[5] lock1.rb によるロックが解除される。
[6] ロックが解除されたため,lock2.rb が 3 を書いてファイルを閉
 じる。

その結果,log.txt には

------
1
2
3
------

と書かれるはず,と思いました。

ところが,mswin187 および mswin191 では 3 が書かれず,1, 2 だ
けが書かれました。
mac187 では,3, 1, 2 の順に書かれました。


次に,lock2.rb に 1 行追加して以下のようにし,

-- lock3.rb --
open("log.txt", "a+") do |f|
  f.flock File::LOCK_EX
  f.puts 3
end
------

lock1.rb → lock3.rb の順に走らせました。

すると,mswin187 では期待どおり 1, 2, 3 の順に書かれましたが,
mac187 ではいつまでたっても両方のスクリプトが終了せず,
mswin191 では 1, 2 だけが書かれて以下の例外が発生しました。

lock3.rb:2:in `flock': Invalid argument - log.txt (Errno::EINVAL)
        from lock3.rb:2:in `block in <main>'
        from lock3.rb:1:in `open'
        from lock3.rb:1:in `<main>'

この EINVAL の意味を
http://msdn.microsoft.com/ja-jp/library/5814770t.aspx
で調べたところ,

> 不正な引数。関数の引数のいずれかに無効な値が指定されています。
> たとえば、fseek 呼び出しでファイル ポインタを移動するとき、
> 指定した元の位置がファイルの先頭より前にある場合です。

と書かれていましたが,なんだかよく分かりません。


次に,PStore ではどうなのだろうと,二つのプロセスから一つの
ファイルに PStore でデータを書こうとしたところ,mswin191 では
やはり同じ例外が発生しました。(再現コードは略します)

-- 
5.5 / moji.gr.jp