monthly gimite

試験運用中。

「ファイルをcloseすればflockも解除される」は嘘

たいていの場合はこのとおりの挙動になるので、これを信じてしまってて、おかげでハマりました。

$ cat a.rb
open("test.txt", "r+") do |f| #ファイルをopen
  f.flock(File::LOCK_EX)      #ロックする
  system("ruby b.rb &")       #バックグラウンドでb.rbを実行
end                           #ファイルを閉じる

$ cat b.rb
open("test.txt", "r+") do |f|
  f.flock(File::LOCK_EX)
end

$ ruby a.rb

これをやると、b.rbは固まります。

open("test.txt", "r+") do |f|
  f.flock(File::LOCK_EX)
  system("ruby b.rb &")
  f.flock(File::LOCK_UN)      #アンロック
end

としたら、固まらなくなりました。どうも「closeしても、子プロセスが同じファイルを参照しているので、ファイルへの参照が開放されない→ロックが自動解除されない」ということっぽいです。

あ、実際にはファイルになんか書き込んだりするでしょうから、アンロックする前にflushが必要*1ですし、例外が起きた場合にも対応、とか考えると「正しいロック」は

open("test.txt", "r+") do |f|
  f.flock(File::LOCK_EX)
  begin
    …
  ensure
    f.flush()                 #バッファをフラッシュ
    f.flock(File::LOCK_UN)
  end
end

でしょうか。めんどいな。

ついでですけど、UNIXだと「他のプロセスがロックしてるファイルでも普通に読み書きできちゃう」ってところがWindowsCygwin含む)と違うんですよね*2。最初びっくりしました。

*1:flushしないでアンロックすると、バッファになんか残ってた場合に、アンロックしたあとで書き込むことになっちゃいますから。

*2:RubyだとどっちもFile#flockなので挙動の違いに注意。