[ruby-trunk - Bug #7452][Assigned] Main thread is stopped after running finalizers if the main threa

Issue #7452 has been reported by mrkn (Kenta M.).


Bug #7452: Main thread is stopped after running finalizers if the main
thread has a finalizer

Author: mrkn (Kenta M.)
Status: Assigned
Priority: Normal
Assignee: authorNari (Narihiro N.)
Category: core
Target version: 2.0.0
ruby -v: ruby 2.0.0dev (2012-11-28 trunk 37923) [x86_64-darwin11.4.2]

以下のようにメインスレッドにファイナライザを登録すると、ファイナライザ実行後に止まってしまいます。

ObjectSpace.define_finalizer(Thread.main) {}

trunk と ruby_1_9_3 ブランチの先頭で発生することを確認しています。

trunk は以下の gist に貼った patch で修正できました。

1.9.3 は以下の gist に貼った patch で修正できました。

Issue #7452 has been updated by mrkn (Kenta M.).

このバグに対する再現テストの書き方が分からないので知ってる人がいたら教えて下さい!!

Bug #7452: Main thread is stopped after running finalizers if the main
thread has a finalizer

Author: mrkn (Kenta M.)
Status: Assigned
Priority: Normal
Assignee: authorNari (Narihiro N.)
Category: core
Target version: 2.0.0
ruby -v: ruby 2.0.0dev (2012-11-28 trunk 37923) [x86_64-darwin11.4.2]

以下のようにメインスレッドにファイナライザを登録すると、ファイナライザ実行後に止まってしまいます。

ObjectSpace.define_finalizer(Thread.main) {}

trunk と ruby_1_9_3 ブランチの先頭で発生することを確認しています。

trunk は以下の gist に貼った patch で修正できました。

1.9.3 は以下の gist に貼った patch で修正できました。

Issue #7452 has been updated by authorNari (Narihiro N.).

メインスレッドにファイナライザを登録できないようなパッチになっているようですが、ファイナライザ実行後に止まってしまうことが問題の本質のように思います。
なんで止まってしまうのでしょうね…。

Bug #7452: Main thread is stopped after running finalizers if the main
thread has a finalizer

Author: mrkn (Kenta M.)
Status: Assigned
Priority: Normal
Assignee: authorNari (Narihiro N.)
Category: core
Target version: 2.0.0
ruby -v: ruby 2.0.0dev (2012-11-28 trunk 37923) [x86_64-darwin11.4.2]

以下のようにメインスレッドにファイナライザを登録すると、ファイナライザ実行後に止まってしまいます。

ObjectSpace.define_finalizer(Thread.main) {}

trunk と ruby_1_9_3 ブランチの先頭で発生することを確認しています。

trunk は以下の gist に貼った patch で修正できました。

1.9.3 は以下の gist に貼った patch で修正できました。

Issue #7452 has been updated by mrkn (Kenta M.).

ファイナライザは実行後にメインスレッドだけfree-listには追加しないようにするパッチなんですね。

そのとおりです。

笹田さんに修正方法が場当たり的だと指摘されて、自分もそう思っているのですが、
残念ながら今のところエレガントな解決方法を考えられていません。

Bug #7452: Main thread is stopped after running finalizers if the main
thread has a finalizer

Author: mrkn (Kenta M.)
Status: Assigned
Priority: Normal
Assignee: authorNari (Narihiro N.)
Category: core
Target version: 2.0.0
ruby -v: ruby 2.0.0dev (2012-11-28 trunk 37923) [x86_64-darwin11.4.2]

以下のようにメインスレッドにファイナライザを登録すると、ファイナライザ実行後に止まってしまいます。

ObjectSpace.define_finalizer(Thread.main) {}

trunk と ruby_1_9_3 ブランチの先頭で発生することを確認しています。

trunk は以下の gist に貼った patch で修正できました。

1.9.3 は以下の gist に貼った patch で修正できました。

Issue #7452 has been updated by authorNari (Narihiro N.).

authorNari (Narihiro N.) wrote:

メインスレッドにファイナライザを登録できないようなパッチになっているようですが

ああっ、すいません、パッチを読み違えてました…。
ファイナライザは実行後にメインスレッドだけfree-listには追加しないようにするパッチなんですね。


Bug #7452: Main thread is stopped after running finalizers if the main
thread has a finalizer

Author: mrkn (Kenta M.)
Status: Assigned
Priority: Normal
Assignee: authorNari (Narihiro N.)
Category: core
Target version: 2.0.0
ruby -v: ruby 2.0.0dev (2012-11-28 trunk 37923) [x86_64-darwin11.4.2]

以下のようにメインスレッドにファイナライザを登録すると、ファイナライザ実行後に止まってしまいます。

ObjectSpace.define_finalizer(Thread.main) {}

trunk と ruby_1_9_3 ブランチの先頭で発生することを確認しています。

trunk は以下の gist に貼った patch で修正できました。

1.9.3 は以下の gist に貼った patch で修正できました。

Issue #7452 has been updated by authorNari (Narihiro N.).

バグの原因がわかりました。
ファイナライザに登録したメインスレッドはRubyプロセス終了時のファイナライザ実行時(rb_objspace_call_finalizer)でfreelistに追加されてしまっているようです。

(gdbのバックトレース)
#0 add_slot_local_freelist (objspace=0x5555559ed8f0, p=0x555555a5be48)
at gc.c:819
#1 0x00005555555c7f42 in finalize_list (objspace=0x5555559ed8f0,
p=0x555555a5be48) at gc.c:1423
#2 0x00005555555c7fd5 in finalize_deferred (objspace=0x5555559ed8f0) at
gc.c:1443
#3 0x00005555555c81ee in rb_objspace_call_finalizer
(objspace=0x5555559ed8f0) at gc.c:1509
#4 0x00005555555c81a6 in rb_gc_call_finalizer_at_exit () at gc.c:1493
#5 0x00005555555b1b5a in ruby_finalize_1 () at eval.c:127
#6 0x00005555555b1dbb in ruby_cleanup (ex=0) at eval.c:193
#7 0x00005555555b20a7 in ruby_run_node (n=0x555555a581e8) at eval.c:307
#8 0x00005555555746b9 in main (argc=3, argv=0x7fffffffd248) at
main.c:36

rb_objspace_call_finalizer()呼び出し後にruby_vm_destruct(VMのデストラクタ)が動き、そのデストラクタではメインスレッドを使うのですが、上記の通りfreeされてしまっているのでうまくいかないようですね。
これはメインスレッドに限った話ではなく、rb_objspace_call_finalizer()呼び出し後に触る可能性があるオブジェクトにファイナライザを登録するのがまずいみたいです。

ややad-hocですが、こんな感じでなおしてみました。

VMから見えているものはとりあえずマークしておいて、通常のファイナライズは回避し、強制的なファイナライズでそれらを実行させるようにしています。

もしかしたら以下のパッチのようにほとんど強制的なファイナライズにしてもいいかなあと思いますが、こっちはちょっと自信がないです。
Rubyのファイナライズ周りに詳しい方の意見を伺いたいところです。。。

diff --git a/gc.c b/gc.c
index 63869a0…8d68e38 100644
— a/gc.c
+++ b/gc.c
@@ -1505,15 +1505,9 @@ rb_objspace_call_finalizer(rb_objspace_t
*objspace)
if (ATOMIC_EXCHANGE(finalizing, 1)) return;

 /* run finalizers */
  • do {
  •   finalize_deferred(objspace);
    
  •   /* mark reachable objects from finalizers */
    
  •   /* They might be not referred from any place here */
    
  •   mark_tbl(objspace, finalizer_table);
    
  •   gc_mark_stacked_objects(objspace);
    
  •   st_foreach(finalizer_table, chain_finalized_object,
    
  •              (st_data_t)&deferred_final_list);
    
  • } while (deferred_final_list);
  • finalize_deferred(objspace);
  • assert(deferred_final_list == 0);
  • /* force to run finalizer */
    while (finalizer_table->num_entries) {
    struct force_finalize_list *list = 0;

Bug #7452: Main thread is stopped after running finalizers if the main
thread has a finalizer

Author: mrkn (Kenta M.)
Status: Assigned
Priority: Normal
Assignee: authorNari (Narihiro N.)
Category: core
Target version: 2.0.0
ruby -v: ruby 2.0.0dev (2012-11-28 trunk 37923) [x86_64-darwin11.4.2]

以下のようにメインスレッドにファイナライザを登録すると、ファイナライザ実行後に止まってしまいます。

ObjectSpace.define_finalizer(Thread.main) {}

trunk と ruby_1_9_3 ブランチの先頭で発生することを確認しています。

trunk は以下の gist に貼った patch で修正できました。

1.9.3 は以下の gist に貼った patch で修正できました。

2012$BG/(B11$B7n(B29$BF|(B 18:01 Nobuyoshi N. [email protected]:

(12/11/28 21:42), authorNari (Narihiro N.) wrote:

$B$d$d(Bad-hoc$B$G$9$,!"$3$s$J46$8$G$J$*$7$F$_$^$7$?!#(B
fix_7452_for_trunk.diff · GitHub

VM$B$+$i8+$($F$$$k$b$N$O$H$j$"$($:%^!<%/$7$F$$$$F!"DL>o$N%U%!%$%J%i%$%:$O2sHr$7!"6/@)E$J%U%!%$%J%i%$%:$G$=$l$i$r<B9T$5$;$k$h$&$K$7$F$$$^$9!#(B

VM$B$+$i8+$($F$$$k$b$N$,(Bfinalize$B$5$l$F$7$^$&$H$$$&$N$OC1=c$J%^!<%/O3$l$N%P%0$H9M$($F$$$$$s$8$c$J$$$G$9$+$M!#(B

rb_objspace_call_finalizer$B$G$O%^!<%/$,$9$Y$F$N%*%V%8%'%/%H$KIU$$$F$J$$>uBV$G<B9T$5$l$k$s$G$9$h$M!#(B
$B$J$N$G!“$b$7$+$9$k$H$3$3$G(BVM$B$+$i8+$($k$b$N$@$1%^!<%/$9$k$@$1$8$c$J$/!”(Bgc_marks$B$H$+FI$s$@$[$&$,$$$$$N$+$b$G$9$,!D!#(B

$B$H$j$“$($:$O!”!V$[$H$s$I6/@)E*$J%U%!%$%J%i%$%:!W$NJ}$G%3%%C%H$7$F$$^$7$?!#(B
test-all$B$J$I$ODL$C$?$N$G$H$j$"$($:$3$l$GMM;R$r8+$?$$$H$*$b$$$^$9!#(B

(12/11/28 21:42), authorNari (Narihiro N.) wrote:

ややad-hocですが、こんな感じでなおしてみました。
fix_7452_for_trunk.diff · GitHub
VMから見えているものはとりあえずマークしておいて、通常のファイナライズは回避し、強制的なファイナライズでそれらを実行させるようにしています。

VMから見えているものがfinalizeされてしまうというのは単純なマーク漏れのバグと考えていいんじゃないですかね。