Project

General

Profile

Bug #595

Fiber ignores ensure clause

Added by Koichi Sasada about 8 years ago. Updated 12 months ago.

Status:
Assigned
Priority:
Normal
Assignee:
ruby -v:
-
Backport:
[ruby-dev:36511]

Description

=begin
Ruby プロセス終了時,Fiber が ensure を無視します.
これは,前から直そうと思って手がついていなかった問題です.
10月末までには直そうと思います.結構複雑なので,後回しにしていましました.

fib = Fiber.new{
begin
Fiber.yield :ok
ensure
puts "should be print out"
end
}
p fib.resume
=end

ensure_fiber.patch View (2.12 KB) _ wanabe, 01/13/2010 06:45 PM

ensure_fiber2.patch View (7.57 KB) _ wanabe, 08/05/2012 01:05 PM


Related issues

Duplicates Ruby trunk - Bug #2460: RubySpecでFiberのSpecがおちる Closed 12/08/2009

History

#1 Updated by Yuki Sonoda almost 8 years ago

  • Target version set to 1.9.1 Release Candidate

=begin

=end

#2 Updated by Roger Pack almost 8 years ago

=begin
Help me out--shouldn't this print out only when you call fib.resume twice?

fib = Fiber.new{
begin
Fiber.yield :ok
ensure
puts "should be print out"
end
}
p fib.resume
p fib.resume

prints out all right.
=end

#3 Updated by Yuki Sonoda almost 8 years ago

  • Target version changed from 1.9.1 Release Candidate to 2.0.0

=begin

=end

#4 Updated by Yuki Sonoda almost 8 years ago

=begin
パッチを書くか、もしくはドキュメントにKNOWN BUGとして書く、ということで。
=end

#5 Updated by _ wanabe over 7 years ago

=begin
ワナベと申します。

かなり前のチケットですが、題名の件についてパッチを書きました。
もしまだどなたもパッチを書かれていないようならご検討ください。

Index: thread.c
===================================================================
--- thread.c (リビジョン 23617)
+++ thread.c (作業コピー)
@@ -293,6 +293,8 @@

static void rb_mutex_unlock_all(mutex_t *mutex, rb_thread_t *th);

+void rb_fiber_terminate_all(rb_thread_t *th);
+
void
rb_thread_terminate_all(void)
{
@@ -310,6 +312,7 @@

  thread_debug("rb_thread_terminate_all (main thread: %p)\n", (void *)th);
  st_foreach(vm->living_threads, terminate_i, (st_data_t)th);
  • rb_fiber_terminate_all(th);

    while (!rb_thread_alone()) {
    PUSH_TAG();
    @@ -1210,6 +1213,7 @@
    thread_debug("rb_thread_execute_interrupts: %ld\n", err);

    if (err == eKillSignal || err == eTerminateSignal) {
    
  •  rb_fiber_terminate_all(th);
    th->errinfo = INT2FIX(TAG_FATAL);
    TH_JUMP_TAG(th, TAG_FATAL);
    }
    

    Index: cont.c

    --- cont.c (リビジョン 23617)
    +++ cont.c (作業コピー)
    @@ -534,6 +534,7 @@
    case 0:
    return Qnil;
    case 1:

  •  case -1:
    

    return argv[0];
    default:
    return rb_ary_new4(argc, argv);
    @@ -946,6 +947,36 @@
    return fib->status != TERMINATED ? Qtrue : Qfalse;
    }

+static VALUE
+terminate_all_i(VALUE fibval)
+{
+ if (rb_fiber_alive_p(fibval)) {
+ VALUE value = rb_exc_new2(rb_eSystemExit, "terminate");
+ return fiber_switch(fibval, -1, &value, 0);
+ }
+}
+
+void
+rb_fiber_terminate_all(rb_thread_t th)
+{
+ VALUE fibval;
+ rb_fiber_t *fib, *root_fib;
+ rb_thread_t *th = GET_THREAD();
+
+ rb_thread_set_current(th);
+ fibval = th->root_fiber;
+ if (!RTEST(fibval)) return;
+ GetFiberPtr(fibval, root_fib);
+
+ fib = root_fib->prev_fiber;
+ while (fib != root
fib) {
+ rb_rescue2(terminate_all_i, fib->cont.self,
+ 0, 0, rb_eSystemExit);
+ fib = fib->prev_fiber;
+ }
+ rb_thread_set_current(_th);
+}
+
/

* call-seq:
* fiber.resume(args, ...) -> obj

--
ワナベ

=end

#6 Updated by Koichi Sasada over 7 years ago

=begin
 ささだです.

 返事が随分遅くなってしまってすみません.

wanabe wrote::

かなり前のチケットですが、題名の件についてパッチを書きました。
もしまだどなたもパッチを書かれていないようならご検討ください。

 実は,以前似たようなものを作ったのですが,さくっと SEGV の嵐で,面倒く
さいなぁ,と思って放置していたのでした.このパッチですと,たとえば
test-all とかはどうでした?

--
// SASADA Koichi at atdot dot net

=end

#7 Updated by _ wanabe over 7 years ago

=begin
ワナベです。

2009/06/15 6:33 に SASADA Koichiko1@atdot.net さんは書きました:

wanabe wrote::

かなり前のチケットですが、題名の件についてパッチを書きました。
もしまだどなたもパッチを書かれていないようならご検討ください。

実は,以前似たようなものを作ったのですが,さくっと SEGV の嵐で,面倒く
さいなぁ,と思って放置していたのでした.このパッチですと,たとえば
test-all とかはどうでした?

ruby 1.9.2dev (2009-06-14 trunk 23691) [i386-mingw32] では
make test-all で SEGV が出てしまいました。
TestFiber#test_many_fibers_with_threads が E で終わった直後の
TestFiber#test_normal で落ちているようです。
ですがなぜか test_fiber.rb を直接起動するとエラーなしで完走します。

またパッチはLinux環境で書いたのですが、その時のmake test-allでは問題はなく、
今改めて ruby 1.9.2dev (2009-06-15 trunk 23692) [i686-linux] で試してみても
SEGV は発生しませんでした。

どういう事かよく分かりませんが、不安定であることは間違いないので
このパッチは役に立たなさそうです。申し訳ありません。

--
ワナベ

=end

#8 Updated by Koichi Sasada over 7 years ago

=begin
 ささだです.

wanabe wrote::

ruby 1.9.2dev (2009-06-14 trunk 23691) [i386-mingw32] では
make test-all で SEGV が出てしまいました。
TestFiber#test_many_fibers_with_threads が E で終わった直後の
TestFiber#test_normal で落ちているようです。
ですがなぜか test_fiber.rb を直接起動するとエラーなしで完走します。

またパッチはLinux環境で書いたのですが、その時のmake test-allでは問題はなく、
今改めて ruby 1.9.2dev (2009-06-15 trunk 23692) [i686-linux] で試してみても
SEGV は発生しませんでした。

どういう事かよく分かりませんが、不安定であることは間違いないので
このパッチは役に立たなさそうです。申し訳ありません。

 いえいえ.もうちょっと追跡すればなんとかなると思うのですが,ここはどう
にも難しいですよね.Fiber の集合を保存する,私が最後にあわてて突っ込んだ
部分にバグがあるような気がしています.

 1.9.2 には間に合わせたいところ.

--
// SASADA Koichi at atdot dot net

=end

#9 Updated by _ wanabe over 6 years ago

=begin
以前このチケットについて書いたパッチが SEGV すると書きましたが、
パッチの問題ではなく Bug #1325 の影響だったようです。
改めて確認したところ make test-all では SEGV しませんでした。

それとは別の問題として、例外でジャンプさせると rescue される恐れがあったので
throw/catch を使うように変更したパッチを添付します。
ご検討くだされば幸いです。
=end

#10 Updated by _ wanabe over 6 years ago

  • File deleted (ensure_fiber.patch)

=begin

=end

#11 Updated by _ wanabe over 6 years ago

=begin
ささださん

このチケットおよびパッチについてコメントいただければ幸いです。
この対処で問題ないかどうか、もし問題ないとしたら
1.9.2 リリース前に入れるべきかどうかが気になっています。
そもそもアプローチがまずいようでしたら別の方法を考えます。
=end

#12 Updated by Yusuke Endoh over 6 years ago

=begin
遠藤です。

2010年4月10日23:20 _ wanabe redmine@ruby-lang.org:

ささださん

このチケットおよびパッチについてコメントいただければ幸いです。
この対処で問題ないかどうか、もし問題ないとしたら
1.9.2 リリース前に入れるべきかどうかが気になっています。
そもそもアプローチがまずいようでしたら別の方法を考えます。

ささださんではないですが勝手にコメントします。

1) プロセス終了時でなく、Fiber が GC で回収される時に ensure 節が
実行されません。

1000.times do
Fiber.new do
begin
Fiber.yield
ensure
puts "foo!"
end
end.resume
GC.start
end

# foo! が 1 個しか出ない (期待は 1000 個)
$ ./ruby t.rb
foo!

実行中の Thread と同様に、yield 中の Fiber は GC しないようにする
くらいしか思いつきません。

2) Fiber を作ったスレッドが終了した場合、Fiber の ensure 節が実行
されません。

Thread.new do
Fiber.new do
begin
Fiber.yield
ensure
puts "foo!"
end
end.resume
end
sleep 1
puts "end"

# foo! が出ない
$ ./ruby t.rb
end

プロセスの終了時でなく、スレッドの終了時に Fiber を起こさないと
行けない?

どちらも仕様レベルで検討しないといけないことのような気がします。
まだ結構大変そうなので、1.9.2 は見送った方がいいんじゃないかなと
思います。早くコメントしていれば、wanabe さんなら余裕で直せたと
思うので残念ですが。

--
Yusuke Endoh mame@tsg.ne.jp

=end

#13 Updated by Koichi Sasada over 6 years ago

=begin
(2010/04/21 23:55), Yusuke ENDOH wrote::

どちらも仕様レベルで検討しないといけないことのような気がします。
まだ結構大変そうなので、1.9.2 は見送った方がいいんじゃないかなと
思います。早くコメントしていれば、wanabe さんなら余裕で直せたと
思うので残念ですが。

 返事が出来ていなくてすみません.最近,色々と余裕がなくて,じっくり考え
て返事が出来ず.

--
// SASADA Koichi at atdot dot net

=end

#14 Updated by Shyouhei Urabe about 6 years ago

  • Status changed from Open to Assigned

=begin

=end

#15 [ruby-dev:43715] Updated by Koichi Sasada over 5 years ago

未だに考え中ですが,これは 1.9.3 には仕様変更になるので入りませんよね?

#16 [ruby-dev:43744] Updated by Yukihiro Matsumoto over 5 years ago

  • ruby -v changed from ruby 1.9.2dev (2010-01-13) [i386-mingw32] to -

#17 [ruby-dev:43744] Updated by Yukihiro Matsumoto over 5 years ago

まつもと ゆきひろです

In message "Re: [Ruby 1.9 - Bug #595] Fiber ignores ensure clause"
on Sat, 11 Jun 2011 14:52:03 +0900, Koichi Sasada redmine@ruby-lang.org writes:

|未だに考え中ですが,これは 1.9.3 には仕様変更になるので入りませんよね?

この動作はバグだと考えているので、Yuguiさんが拒絶しない限り、
直せるのであれば1.9.3で直せばよいと思います。間に合わないな
らしょうがない。

#18 [ruby-dev:43745] Updated by Yui NARUSE over 5 years ago

RubySpec 的にバグ扱いになってますね。
core/fiber/resume_spec.rb

#19 [ruby-dev:43951] Updated by Hiroshi Nakamura over 5 years ago

  • Target version changed from 2.0.0 to 1.9.3

#20 Updated by Motohiro KOSAKI about 5 years ago

  • Status changed from Assigned to Closed

#21 [ruby-dev:45354] Updated by Yui NARUSE over 4 years ago

  • Status changed from Closed to Assigned
  • Target version changed from 1.9.3 to Next Major

#22 [ruby-dev:46005] Updated by Koichi Sasada about 4 years ago

あれ,これ 3.0 でいいんだっけ.

#23 [ruby-dev:46006] Updated by Yui NARUSE about 4 years ago

ko1 (Koichi Sasada) wrote:

あれ,これ 3.0 でいいんだっけ.

3月に話したときに大変そうだから先かなー的な雰囲気のことを仰っていたので、3.0かなぁ、と。
早く直るに越した事はありません。

#24 [ruby-dev:46030] Updated by _ wanabe about 4 years ago

ワナベと申します。

(1) GC で mark と sweep の間に、mark されていない Fiber を対象に
(2) ruby_cleanup 中に、メインスレッドに所属するすべての Fiber を対象に
(3) 子スレッド終了時(vm->living_threads から外されるとき)、所属するすべての Fiber を対象に
の 3 つのタイミングで、throw/catch により ensure 節を実行するパッチを書きました。

で遠藤さんがおっしゃっているような「yield 中の Fiber は GC しない」
という手法もやってみたのですが、test/ruby/test_fiber.rb がとても終わりそうにないことや
Fiber のマーク処理の重さや Fiber 自体のメモリ消費量などにより、断念しました。
そのため上記(1)のように、rb_gc_marked_p() という関数が必要になるなど強引な手段を使っています。

また、以下のようにして速度低下を計ってみました。

require "benchmark"
GC.start
Benchmark.bm(4) do |x|
tms = Benchmark::Tms.new
10.times do |i|
tms += x.report(" #{i}:") do
30000.times do
Fiber.new{Fiber.yield}.resume
end
end
end
puts " sum:#{tms}"
end

素の r36623 :2.980000 3.120000 6.100000 ( 6.107164)
パッチ適用後:3.580000 3.480000 7.060000 ( 7.061093)

と、無視できない程度に(約 14%)速度低下してしまいました。
とはいえこれ以上の方法は思いつかないのですが、いかがでしょうか。

#25 [ruby-dev:46060] Updated by Koichi Sasada about 4 years ago

こちら,返事が大変遅くなって済みません.

(2012/08/05 13:05), wanabe (_ wanabe) wrote:

(1) GC で mark と sweep の間に、mark されていない Fiber を対象に
(2) ruby_cleanup 中に、メインスレッドに所属するすべての Fiber を対象に
(3) 子スレッド終了時(vm->living_threads から外されるとき)、所属するすべての Fiber を対象に
の 3 つのタイミングで、throw/catch により ensure 節を実行するパッチを書きました。

で遠藤さんがおっしゃっているような「yield 中の Fiber は GC しない」
という手法もやってみたのですが、test/ruby/test_fiber.rb がとても終わりそうにないことや
Fiber のマーク処理の重さや Fiber 自体のメモリ消費量などにより、断念しました。
そのため上記(1)のように、rb_gc_marked_p() という関数が必要になるなど強引な手段を使っています。

 とりとめもなく3つほど.

 (1) ですが,ファイナライザのように実行するのはどうかと思っておりまし
た.ただ,その場合,ちょっと管理が大変なんですよね.

 あと,throw よりは,

#define eKillSignal INT2FIX(0)
#define eTerminateSignal INT2FIX(1)

この辺がいいのかなぁ,と思っていました.いや,むしろここを thorw にした
ほうが設計は綺麗かもしれませんね....現在のの実装は,絶対 catch 出来な
い,ってのを目指している感じです.

 速度ですが,小崎さんが仰っているように,あまり気にしてもしょうがない,
という気もするのですが,例えば ensure 節でひっかける可能性があるか,とい
うチェックをするのは結構効くのではないかと思っています.

 というのも,殆どの用途はガチで使っているわけじゃないと思うので,ensure
な処理を必要とする Fiber は,実は少ないのではないかと,

 別の案としては,Fiber 生成時に「ensure ちゃんと処理して」フラグを新設
し,ガチで使う人(ensure をきっちり動かして欲しい人)はこれでやってね
(参考: http://bugs.ruby-lang.org/issues/6694),というのはどうだろう,
とか思うんですが,ちょっと手抜きしすぎでしょうか.

--
// SASADA Koichi at atdot dot net

#26 Updated by Yura Sokolov over 2 years ago

What about this ticket?
Guaranteed ensure inside of Fiber and Fiber.raise (as complement for Thread.raise) will be usefull for full coroutine based environment ala python's gevent.

#27 [ruby-dev:49299] Updated by Benoit Daloze 12 months ago

Could we clarify what is the desired behavior and what prevents it to be implemented?

There is a very old RubySpec about this and I would like to know whether this might be guaranteed in a future release or not.

Also available in: Atom PDF