[Ruby 1.9 - Bug #5386][Open] FiberオブジェクトのGC時にSEGV

Issue #5386 has been reported by Kazuki Tsujimoto.


Bug #5386: FiberオブジェクトのGC時にSEGV

Author: Kazuki Tsujimoto
Status: Open
Priority: Normal
Assignee: Kazuki Tsujimoto
Category: core
Target version:
ruby -v: ruby 1.9.4dev (2011-10-01 trunk 33368) [x86_64-linux]

=begin
辻本です。

ポータブルな再現コードが作れていないのですが、
Ubuntu 10.04 x86_64にて以下のコードを実行すると
FiberオブジェクトをGCする処理の中でSEGVします。

require ‘fiber’

1.times {
Fiber.new{}
}

2.times.map {|i|
Thread.new {
Fiber.new{}.resume
}.join
}.each {|t|
t.join
}

GC.start
GC.stress = true

1.times {
Fiber.new{}
}

バックトレースを添付します。

調べてみたところ、GC.stress設定後の1回目のマークフェーズで

Breakpoint 2, fiber_mark (ptr=0xad7130) at cont.c:265
(gdb) rp ((rb_fiber_t*)ptr)->cont->saved_thread.self
T_DATA(VM/thread): $1 = (struct RTypedData *) 0xaaf528

となっているFiberオブジェクトが、2回目のマークフェーズでは

Breakpoint 2, fiber_mark (ptr=0xad7130) at cont.c:265
(gdb) rp ((rb_fiber_t*)ptr)->cont->saved_thread.self
T_NONE: $2 = (struct RBasic *) 0xaaf528

となっており、saved_thread.selfのマーク漏れのようです。

以下の修正で直りました。

diff --git a/vm.c b/vm.c
index 665351b…2ab2b92 100644
— a/vm.c
+++ b/vm.c
@@ -1735,6 +1735,7 @@ rb_thread_mark(void *ptr)
RUBY_MARK_UNLESS_NULL(th->first_proc);
if (th->first_proc) RUBY_MARK_UNLESS_NULL(th->first_args);

  • RUBY_MARK_UNLESS_NULL(th->self);
    RUBY_MARK_UNLESS_NULL(th->thgroup);
    RUBY_MARK_UNLESS_NULL(th->value);
    RUBY_MARK_UNLESS_NULL(th->errinfo);
    =end

$BDTK$G$9!#(B

Subject: [ruby-dev:44567] Re: [ruby-dev:44566] [Ruby 1.9 -
Bug#5386][Open] Fiber$B%*%V%8%'%/%H$N(BGC$B;~$K(BSEGV
From: SASADA Koichi [email protected]

$B%$%^%$%A<+?.$,$J$$$N$G$9$,!$<+J,<+?H$r(B mark $B$9$k$C$F$*$+$7$$5$$,$7$^$9!%(B
$B$H$$$&$o$1$G!$<!$N$h$&$JJQ99$O$I$&$G$7$g$&$+!%(B

$B$"$j$,$H$&$4$6$$$^$9!#<h$j9~$s$G$*$-$^$7$?!#(B

イマイチ自信がないのですが,自分自身を mark するっておかしい気がします.
というわけで,次のような変更はどうでしょうか.

Index: cont.c

— cont.c (revision 33272)
+++ cont.c (working copy)
@@ -138,7 +138,7 @@
if (ptr) {
rb_context_t *cont = ptr;
rb_gc_mark(cont->value);

  • rb_thread_mark(&cont->saved_thread);
  • rb_gc_mark(cont->saved_thread->self);

    if (cont->vm_stack) {
    #ifdef CAPTURE_JUST_VALID_VM_STACK

Issue #5386 has been updated by Kazuki Tsujimoto.

Status changed from Closed to Assigned
ruby -v changed from - to ruby 1.9.4dev (2011-10-01 trunk 33368)
[x86_64-linux]

辻本です。

これらのコミットによってtest-allがSEGVするようになってしまった環境があるので
r33378で一旦revertしました。

http://u64.rubyci.org/~chkbuild/ruby-trunk/recent.html
http://c5664.rubyci.org/~chkbuild/ruby-trunk/recent.html
http://fbsd.rubyci.org/~chkbuild/ruby-trunk/recent.html

Bug #5386: FiberオブジェクトのGC時にSEGV

Author: Kazuki Tsujimoto
Status: Assigned
Priority: Normal
Assignee: Kazuki Tsujimoto
Category: core
Target version:
ruby -v: ruby 1.9.4dev (2011-10-01 trunk 33368) [x86_64-linux]

=begin
辻本です。

ポータブルな再現コードが作れていないのですが、
Ubuntu 10.04 x86_64にて以下のコードを実行すると
FiberオブジェクトをGCする処理の中でSEGVします。

require ‘fiber’

1.times {
Fiber.new{}
}

2.times.map {|i|
Thread.new {
Fiber.new{}.resume
}.join
}.each {|t|
t.join
}

GC.start
GC.stress = true

1.times {
Fiber.new{}
}

バックトレースを添付します。

調べてみたところ、GC.stress設定後の1回目のマークフェーズで

Breakpoint 2, fiber_mark (ptr=0xad7130) at cont.c:265
(gdb) rp ((rb_fiber_t*)ptr)->cont->saved_thread.self
T_DATA(VM/thread): $1 = (struct RTypedData *) 0xaaf528

となっているFiberオブジェクトが、2回目のマークフェーズでは

Breakpoint 2, fiber_mark (ptr=0xad7130) at cont.c:265
(gdb) rp ((rb_fiber_t*)ptr)->cont->saved_thread.self
T_NONE: $2 = (struct RBasic *) 0xaaf528

となっており、saved_thread.selfのマーク漏れのようです。

以下の修正で直りました。

diff --git a/vm.c b/vm.c
index 665351b…2ab2b92 100644
— a/vm.c
+++ b/vm.c
@@ -1735,6 +1735,7 @@ rb_thread_mark(void *ptr)
RUBY_MARK_UNLESS_NULL(th->first_proc);
if (th->first_proc) RUBY_MARK_UNLESS_NULL(th->first_args);

  • RUBY_MARK_UNLESS_NULL(th->self);
    RUBY_MARK_UNLESS_NULL(th->thgroup);
    RUBY_MARK_UNLESS_NULL(th->value);
    RUBY_MARK_UNLESS_NULL(th->errinfo);
    =end

Issue #5386 has been updated by Kazuki Tsujimoto.

辻本です。

このパッチで再現コードでのSEGVが起きなくなることを確認しました。
よければコミットしていただけないでしょうか。

Bug #5386: FiberオブジェクトのGC時にSEGV

Author: Kazuki Tsujimoto
Status: Assigned
Priority: Normal
Assignee: Kazuki Tsujimoto
Category: core
Target version:
ruby -v: ruby 1.9.4dev (2011-10-01 trunk 33368) [x86_64-linux]

=begin
辻本です。

ポータブルな再現コードが作れていないのですが、
Ubuntu 10.04 x86_64にて以下のコードを実行すると
FiberオブジェクトをGCする処理の中でSEGVします。

require ‘fiber’

1.times {
Fiber.new{}
}

2.times.map {|i|
Thread.new {
Fiber.new{}.resume
}.join
}.each {|t|
t.join
}

GC.start
GC.stress = true

1.times {
Fiber.new{}
}

バックトレースを添付します。

調べてみたところ、GC.stress設定後の1回目のマークフェーズで

Breakpoint 2, fiber_mark (ptr=0xad7130) at cont.c:265
(gdb) rp ((rb_fiber_t*)ptr)->cont->saved_thread.self
T_DATA(VM/thread): $1 = (struct RTypedData *) 0xaaf528

となっているFiberオブジェクトが、2回目のマークフェーズでは

Breakpoint 2, fiber_mark (ptr=0xad7130) at cont.c:265
(gdb) rp ((rb_fiber_t*)ptr)->cont->saved_thread.self
T_NONE: $2 = (struct RBasic *) 0xaaf528

となっており、saved_thread.selfのマーク漏れのようです。

以下の修正で直りました。

diff --git a/vm.c b/vm.c
index 665351b…2ab2b92 100644
— a/vm.c
+++ b/vm.c
@@ -1735,6 +1735,7 @@ rb_thread_mark(void *ptr)
RUBY_MARK_UNLESS_NULL(th->first_proc);
if (th->first_proc) RUBY_MARK_UNLESS_NULL(th->first_args);

  • RUBY_MARK_UNLESS_NULL(th->self);
    RUBY_MARK_UNLESS_NULL(th->thgroup);
    RUBY_MARK_UNLESS_NULL(th->value);
    RUBY_MARK_UNLESS_NULL(th->errinfo);
    =end

Issue #5386 has been updated by Tomoyuki C…

確認いただきありがとうございます。コミットしておきます。
ただ作業できるのが夜になってからなので、先にどなたかコミットして頂いてもわたしはかまいません。

Bug #5386: FiberオブジェクトのGC時にSEGV

Author: Kazuki Tsujimoto
Status: Assigned
Priority: Normal
Assignee: Kazuki Tsujimoto
Category: core
Target version:
ruby -v: ruby 1.9.4dev (2011-10-01 trunk 33368) [x86_64-linux]

=begin
辻本です。

ポータブルな再現コードが作れていないのですが、
Ubuntu 10.04 x86_64にて以下のコードを実行すると
FiberオブジェクトをGCする処理の中でSEGVします。

require ‘fiber’

1.times {
Fiber.new{}
}

2.times.map {|i|
Thread.new {
Fiber.new{}.resume
}.join
}.each {|t|
t.join
}

GC.start
GC.stress = true

1.times {
Fiber.new{}
}

バックトレースを添付します。

調べてみたところ、GC.stress設定後の1回目のマークフェーズで

Breakpoint 2, fiber_mark (ptr=0xad7130) at cont.c:265
(gdb) rp ((rb_fiber_t*)ptr)->cont->saved_thread.self
T_DATA(VM/thread): $1 = (struct RTypedData *) 0xaaf528

となっているFiberオブジェクトが、2回目のマークフェーズでは

Breakpoint 2, fiber_mark (ptr=0xad7130) at cont.c:265
(gdb) rp ((rb_fiber_t*)ptr)->cont->saved_thread.self
T_NONE: $2 = (struct RBasic *) 0xaaf528

となっており、saved_thread.selfのマーク漏れのようです。

以下の修正で直りました。

diff --git a/vm.c b/vm.c
index 665351b…2ab2b92 100644
— a/vm.c
+++ b/vm.c
@@ -1735,6 +1735,7 @@ rb_thread_mark(void *ptr)
RUBY_MARK_UNLESS_NULL(th->first_proc);
if (th->first_proc) RUBY_MARK_UNLESS_NULL(th->first_args);

  • RUBY_MARK_UNLESS_NULL(th->self);
    RUBY_MARK_UNLESS_NULL(th->thgroup);
    RUBY_MARK_UNLESS_NULL(th->value);
    RUBY_MARK_UNLESS_NULL(th->errinfo);
    =end

Issue #5386 has been updated by Tomoyuki C…

以下でどうでしょうか?
test-all の test_many_fibers_with_threads
がSEGVするというのは起きなくなるのは確認しましたが、元々の再現スクリプトでは手元で SEGV を発生させられていないのでそちらは未確認です。

なお cont->saved_thread の構造体は cont->saved_thread.self が指しているオブジェクトが wrap
している構造体のコピーで、アドレスは別物なのでそれぞれ別にマークしないといけない、ということなのかなぁと思いましたが、具体的に何がマーク漏れで困ってるのかはちゃんと確認していません。

diff --git a/cont.c b/cont.c
index 3e68d89…22944d6 100644
— a/cont.c
+++ b/cont.c
@@ -139,6 +139,7 @@ cont_mark(void *ptr)
rb_context_t *cont = ptr;
rb_gc_mark(cont->value);
rb_thread_mark(&cont->saved_thread);

  • rb_gc_mark(cont->saved_thread.self);

    if (cont->vm_stack) {
    #ifdef CAPTURE_JUST_VALID_VM_STACK


Bug #5386: FiberオブジェクトのGC時にSEGV

Author: Kazuki Tsujimoto
Status: Assigned
Priority: Normal
Assignee: Kazuki Tsujimoto
Category: core
Target version:
ruby -v: ruby 1.9.4dev (2011-10-01 trunk 33368) [x86_64-linux]

=begin
辻本です。

ポータブルな再現コードが作れていないのですが、
Ubuntu 10.04 x86_64にて以下のコードを実行すると
FiberオブジェクトをGCする処理の中でSEGVします。

require ‘fiber’

1.times {
Fiber.new{}
}

2.times.map {|i|
Thread.new {
Fiber.new{}.resume
}.join
}.each {|t|
t.join
}

GC.start
GC.stress = true

1.times {
Fiber.new{}
}

バックトレースを添付します。

調べてみたところ、GC.stress設定後の1回目のマークフェーズで

Breakpoint 2, fiber_mark (ptr=0xad7130) at cont.c:265
(gdb) rp ((rb_fiber_t*)ptr)->cont->saved_thread.self
T_DATA(VM/thread): $1 = (struct RTypedData *) 0xaaf528

となっているFiberオブジェクトが、2回目のマークフェーズでは

Breakpoint 2, fiber_mark (ptr=0xad7130) at cont.c:265
(gdb) rp ((rb_fiber_t*)ptr)->cont->saved_thread.self
T_NONE: $2 = (struct RBasic *) 0xaaf528

となっており、saved_thread.selfのマーク漏れのようです。

以下の修正で直りました。

diff --git a/vm.c b/vm.c
index 665351b…2ab2b92 100644
— a/vm.c
+++ b/vm.c
@@ -1735,6 +1735,7 @@ rb_thread_mark(void *ptr)
RUBY_MARK_UNLESS_NULL(th->first_proc);
if (th->first_proc) RUBY_MARK_UNLESS_NULL(th->first_args);

  • RUBY_MARK_UNLESS_NULL(th->self);
    RUBY_MARK_UNLESS_NULL(th->thgroup);
    RUBY_MARK_UNLESS_NULL(th->value);
    RUBY_MARK_UNLESS_NULL(th->errinfo);
    =end