Bug #2575

a test fail for IO#readpartial is broken on *BSD

Added by taca (Takahiro Kambe) over 2 years ago. Updated about 1 year ago.

[ruby-dev:39987]
Status:Open Start date:01/08/2010
Priority:Normal Due date:
Assignee:- % Done:

0%

Category:core
Target version:-
ruby -v:ruby 1.8.7 (2009-06-12 patchlevel 174) [i486-netbsdelf]

Description

IO#readpartialのテストに失敗します。確認はNetBSD 5.0_STABLE, current (5.99.22)とFreeBSD 7.2-STABLE、いずれもi386環境で行いました。

test/ruby/test_io.rb の test_readpartial_pos() に、

      open("foo", "w") {|f| f << "abc" }
      open("foo") {|f|
        f.seek(0)
        assert_equal("ab", f.readpartial(2))
        assert_equal(2, f.pos)
      }

といったテストがあります。ここで、f.posの結果がゼロとなってテストに失敗します。直前のf.seek(0)を削除するとテストに成功するようになりますが、この有無で結果が変わってはなりません。

io.cのio_getpartial()では、

1. read_buffered_data()でFILE構造体に読み込み済みのデータを読んでみる。
2. 読み込み済みのデータがなければ、
   2.1 read(2)でデータをFILE構造体に読み込む。
   2.2 後でfflush(3)を呼び出してFILE構造体の内容の整合性が取れ(ることを
       期待してい)る。

といったことをしています。2.1の処理は、

merge revision(s) 21913:
        * io.c (io_getpartial): fflush after read for updating pos in FILE.
          not portable, I guess.  [ruby-core:21561]

とコミットされています。ここで推測されているように実際に移植性はありません。そもそも、fflush(3)で構造体の内容が適切になされると期待することに無理がある気もします。

*BSDのFILE構造体(DragonFlyは不明)には、

o ファイル中のオフセットを示す構造体メンバ _offset があります。
o 構造体メンバ_flagsのフラッグ値には、_offsetの値が実際に有効かどうかを
  示す __SOFF という値があります。

そして、この問題は以下のように説明できます。

(1) f.seek(0)を呼び出すとfseeko(3)が呼び出されて、_offsetに値と__SOFFが
    フラッグが設定されます。このときの _offset が後のf.tellの戻り値となっ
    ています。

(2) f.seek(0)を呼び出さないと、f.tellの呼び出された時点で_offsetに値と
    __SOFFが設定されるため、期待した値が返ることになります。

解決策としては、fflush(3)の呼び出しに代えて__SOFFをアンセットしてからftello(3)を呼び出すといったことが考えられます。取り敢えずのパッチで、問題が解決することを確認しました。

io.c.diff - 取り敢えずのパッチ (516 Bytes) taca (Takahiro Kambe), 01/08/2010 12:40 pm

io.c.diff - trial fix for NetBSD and FreeBSD (996 Bytes) taca (Takahiro Kambe), 02/05/2010 05:21 pm

History

Updated by taca (Takahiro Kambe) over 2 years ago

In message <4b46a911c23d1_8c3bbe17ec398dd@redmine.ruby-lang.org>
	on Fri, 8 Jan 2010 12:40:04 +0900,
	Takahiro Kambe <redmine@ruby-lang.org> wrote:
> 解決策としては、fflush(3)の呼び出しに代えて__SOFFをアンセットしてから
> ftello(3)を呼び出すといったことが考えられます。取り敢えずのパッチで、
> 問題が解決することを確認しました。
この問題は解決しますが、解決しているのはオフセットだけで、
read_buffered_data()辺りにはfread(3)呼び出してるコードも
存在します。

副作用もあるでしょうし、最終的にはRuby 1.9でstdio捨てると
いう話に結び付くのでしょう。

-- 
神戸 隆博(かんべ たかひろ)		at 仕事場 

Updated by taca (Takahiro Kambe) over 2 years ago

[ruby-dev:40051] に添付のパッチで現象が解決することを確認しました。
根本的な原因は、Bug #1872で、timerスレッド関係か何かの関係で顕在化したと考えられます。

Updated by taca (Takahiro Kambe) over 2 years ago

In message <4b4c141dbb220_8bcf020a0844891@redmine.ruby-lang.org>
	on Tue, 12 Jan 2010 15:18:06 +0900,
	Takahiro Kambe <redmine@ruby-lang.org> wrote:
> [ruby-dev:40051] に添付のパッチで現象が解決することを確認しました。
と、書きましたが、

> 根本的な原因は、Bug #1872で、timerスレッド関係か何かの関係で顕在化し
> たと考えられます。
単純なバッククォートやsystem()の実行での問題は解消しましたが、やはり、

	test_local_barrier(TC_Thread):

で刺さるときがあるようです。

-- 
神戸 隆博(かんべ たかひろ)		at 仕事場 

P.S.
刺さったプロセスを kill -KILL で終了させて、make test-all を終わらせた
あとに、親なしとなったrubyのプロセスが10個くらい残り、parkedな状態
(pthread_joinか何か)となってます。NetBSD current (5.99.22)自体も怪しい
ところはあるので、取り敢えず 5.99.23 にしてから再確認する予定です。

Updated by taca (Takahiro Kambe) over 2 years ago

申し訳ありませんが、redmineに登録された、

  [ruby-dev:40055]によるコメント#2
  [ruby-dev:40056]によるコメント#3)

については、IO#readpartialの問題とは無関係でした。

Updated by hukl (John Bader) over 2 years ago

I have issues with that on FreeBSD. I ran unicorn and it delivered only partial responses. The maintainer fixed this with:

diff --git a/io.c b/io.c
index 375cbc8..d4d28e5 100644
--- a/io.c
+++ b/io.c
@@ -122,6 +122,9 @@ extern void Init_File _((void));
# endif
#endif

+#define preserving_errno(stmts) \
+	do {int saved_errno = errno; stmts; errno = saved_errno;} while (0)
+
VALUE rb_cIO;
VALUE rb_eEOFError;
VALUE rb_eIOError;
@@ -490,7 +493,7 @@ io_fwrite(str, fptr)
	r = write(fileno(f), RSTRING(str)->ptr+offset, l);
        TRAP_END;
#if BSD_STDIO
-	fseeko(f, lseek(fileno(f), (off_t)0, SEEK_CUR), SEEK_SET);
+	preserving_errno(fseeko(f, lseek(fileno(f), (off_t)0, SEEK_CUR), SEEK_SET));
#endif
        if (r == n) return len;
        if (0 <= r) {
---

This resolved the issue for me. I applied the patch to the FreeBSD ruby18 port and reinstalled.

Updated by taca (Takahiro Kambe) over 2 years ago

Hi John.

Dose your patch solve the partial read problem on FreeBSD without io.c.diff,
or your patch provide additional fix?

Anyway, I rewrite slightly your patch and merged my updated fix to attached file.

Also available in: Atom PDF