In article <87ad3l3kho.fsf / serein.a02.aist.go.jp>,
  Tanaka Akira <akr / m17n.org> writes:

>>> 個人的には、欲しいのは stdio に協調して動く sysread です。stdio のバッ
>>> ファにデータが溜ってる時には、sysread がそっちから読んでくれると嬉しい
>>> ですね。sysread は即座に得られるデータがない時以外はブロックしないので、
>>> データがどうしても必要になってから sysread を呼べば、nonblock はだいた
>>> い必要ないと思います。

この考えについてもう少し書きます。

> 即座に得られるデータが 1byte でもあれば partial read になるのでブロッ
> クしないと理解しています。
>
> 即座に得られるデータがない時以外にブロックすることってあるんですか?

SUSv3 の read() の項を読む限り、ブロックするのは即座に得られるデータが
無い時だけです。

さて、現在の IO の読み込みメソッドは次のように nonblock に影響されます。
(他にあるでしょうか?)

(1) 即座に得られるデータがある場合、read が即座に得られるデータだけを返す

% (echo -n a; sleep 2; echo b)|ruby -rio/nonblock -e 'p STDIN.nonblock { STDIN.read(nil) }'
"a"
% (echo -n a; sleep 2; echo b)|ruby -rio/nonblock -e 'p STDIN.nonblock { STDIN.read(8) }'
"a"

(2) 即座に得られるデータがない場合、read(nil) が "" を返し、read(len)
    が EAGAIN を発生する

% sleep 2|ruby -rio/nonblock -e 'p STDIN.nonblock { STDIN.read(8) }'
-e:1:in `read': Resource temporarily unavailable (Errno::EAGAIN)
        from -e:1
        from -e:1:in `nonblock'
        from -e:1
% sleep 2|ruby -rio/nonblock -e 'p STDIN.nonblock { STDIN.read(nil) }'
""

(3) 即座に得られるデータが無い場合、eof? が即座に true を返す

% sleep 2|ruby -rio/nonblock -e 't1 = Time.now; p STDIN.nonblock { STDIN.eof? }; t2 = Time.now; p t2-t1'
true
0.000563

ここで、(1) は nonblock を使う目的です。少なくとも [ruby-talk:71562]
[ruby-talk:87946] [ruby-list:24272] はそのような要求に思えます。

しかし、(2) は必ずしも適切な挙動ではありません。データが必要になった時
に read を呼ぶとすれば、データが 1byte 以上到着するまでブロックするの
が適切でしょう。そうでなければ polling しなければいけないので面倒です
し、下手に busy wait されたら迷惑です。また、理由はともかくどうしても
ブロックしたくないのだとすれば、IO.select によってデータが到着している
かどうか判定できますから、read を呼ぶ前に IO.select でデータの存在を確
認すれば、決してブロックしないという挙動は実現可能です。(race
condition を気にしないことにすれば) 従って、この挙動は必須ではありませ
ん。

また、(3) も IO.select を使えば、データがない時にブロックする eof? か
らブロックしない eof? を作ることが可能です。従って、この挙動も必須では
ありません。

というわけで、欲しいのは
* 即座に得られるデータが存在する場合、ブロックせずに読み込む
* 即座に得られるデータが存在しない場合、データが到着するまでブロックする
というメソッドです。nonblock.rb のようなメソッドを組み込むよりは、こう
いう用途に即したメソッドを付けるほうがいいと思います。

さて、そういうメソッドは、仮に readpartial という名前とすると、現在の
Ruby では次のように実装できます。

class IO
  def readpartial(n)
    IO.select([self])
    nonblock { STDIN.read(n) } 
  end
end

しかし、実の所、この readpartial は stdio のバッファまわりの挙動を除け
ば blocking-mode における sysread の挙動そのもので、実際、C レベルでは
READ_DATA_PENDING とかを使えば nonblock を使わなくても実装可能なはずで
す。

というわけで、readpartial を C レベルで追加すれば、nonblock の最大の問
題である他のプロセスへの影響も無くせて、その点でも嬉しいわけです。

ちなみに、readpartial という名前は partial read から思いついたわけです
が、Nachos という教育用の OS に ReadPartial という関数があるようです。

他の名前の案としては、挙動は sysread そのものなので、sysread をそうし
てしまうという方法が考えられます。readpartial と sysread の違いは
stdio のバッファまわりで、sysread と他のメソッドを混用していないプログ
ラムでは stdio のバッファは空なはずですから、その場合は readpartial と
sysread の挙動は一致し互換性の問題はない、と思います。(その場合は本物
の read(2) を sysread! などとして用意すべきだとは思いますが)
-- 
[田中 哲][たなか あきら][Tanaka Akira]