[ruby-dev:46142] [ruby-trunk - Bug #6441] IO.pipe on ENFILE

From: "shyouhei (Shyouhei Urabe)" <shyouhei@...>
Date: 2012-09-18 01:18:32 UTC
List: ruby-dev #46142
Issue #6441 has been updated by shyouhei (Shyouhei Urabe).


authorNari (Narihiro Nakamura) wrote:
> naruse (Yui NARUSE) wrote:
> > 今の Ruby は open(2) などで、errno=ENFILE が発生した場合、
> > すなわち fd を使いきっている場合には、rb_gc() を呼び、IO オブジェクトを GC して、
> > fd が解放されないか試み、それでもダメだったら諦めるとしています。
> > 
> > しかし、IO.pipe の場合はこれに失敗することがあります。
> 
> 失敗する、というのは「fd の解放を試み、ダメなら諦める」ということが
> IO.pipeでできないことがある、ということでしょうか?

パイプがたくさんゴミになっている状況を考えてください。
本来であれば、GCされれば、それらのゴミパイプ(=fd)は回収されるはずです。

さて、そのような状況下であらたにIO.pipeを呼んだところ、ENFILEになったとしましょう。
この場合、fdが開放されることを期待してGCが走ります。
しかしながら現在のrubyではlazy sweepがあるため、GCしたからといってすぐにはsweepされません。
したがって、本来であればGCすることでゴミパイプが回収されてfdに余裕が出ることが期待されるところ、
引き続きfdが枯渇した状況が続いてしまいます(=GCしても意味なかった)。

> > これは、lazy sweep が上記の目的のため T_FILE の場合は直ちに sweep することにしている所、
> > pipe の場合はその例外にあたらないからです。
>
> その例外というのはpipeで作られるfdがT_FILEのオブジェクトにわりあてられ
> てないとかそういう話なのでしょうか。

上記のような問題がなぜパイプ以外で発生しないかというと、普通のファイルの場合だけlazy sweepにハックが入っていて、
「普通のファイルなら即座にsweep」ということになっているからです。という意味だと思われます。

> また再現コードなどがあればいただけると嬉しいです。

  zsh % ulimit -n 7 # 適当に小さい数字
  zsh % ruby -ve 'loop { IO.pipe }'
  ruby 2.0.0dev (2012-09-16 trunk 36984) [x86_64-linux]
  -e:1:in `pipe': Too many open files (Errno::EMFILE)
          from -e:1:in `block in <main>'
          from -e:1:in `loop'
          from -e:1:in `<main>'
  zsh: exit 1     ~/target/trunk/bin/ruby -ve 'loop { IO.pipe }'

ゴミを生じているだけですので本来であれば無限に実行が続くべきです。
----------------------------------------
Bug #6441: IO.pipe on ENFILE
https://bugs.ruby-lang.org/issues/6441#change-29478

Author: naruse (Yui NARUSE)
Status: Assigned
Priority: Normal
Assignee: authorNari (Narihiro Nakamura)
Category: 
Target version: 
ruby -v: ruby 2.0.0dev (2012-05-16 trunk 35659) [x86_64-netbsd6.99.5]


今の Ruby は open(2) などで、errno=ENFILE が発生した場合、
すなわち fd を使いきっている場合には、rb_gc() を呼び、IO オブジェクトを GC して、
fd が解放されないか試み、それでもダメだったら諦めるとしています。

しかし、IO.pipe の場合はこれに失敗することがあります。
これは、lazy sweep が上記の目的のため T_FILE の場合は直ちに sweep することにしている所、
pipe の場合はその例外にあたらないからです。


-- 
http://bugs.ruby-lang.org/

In This Thread