From: Kouhei S. <ko...@co...> - 2006-06-14 04:52:52
|
須藤です. Ruby側で登録したコールバック(例えばsignal_connectとか)がCから呼び出される とします.例えば,以下のようなプログラムで,以下のコマンドでコンパイルでき ます. % cc `pkg-config gtk+-2.0 cairo --cflags --libs` \ -I`ruby -rrbconfig -e 'print Config::CONFIG["archdir"]'` \ -l`ruby -rrbconfig -e 'print Config::CONFIG["RUBY_SO_NAME"]'` \ -o test test.c #include <gtk/gtk.h> #include <ruby.h> int main(int argc, char **argv) { gtk_init(&argc, &argv); ruby_init(); ruby_init_loadpath(); ruby_script(argv[0]); ruby_set_argv(argc, argv); rb_eval_string("require 'gtk2'\n" "Gtk.init\n" "Gtk.timeout_add(0) do\n" " raise 'timeout'\n" "end\n"); gtk_main(); return 0; } これを実行するとSegmentation faultで落ちます. (eval):3: [BUG] Segmentation fault ruby 1.8.4 (2005-12-24) [i486-linux] Rubyで書くとこんな感じですが,こちらは落ちません. require "gtk2" Gtk.init Gtk.timeout_add(0) do raise "timeout" end Gtk.main なかださんにいろいろ教えてもらっていて,詳しいことはそちらを みてもらうとよいと思いますが,私にはよくわからないところがあ ります. http://www.rubyist.net/~nobu/t/20060611.html#c01 http://www.rubyist.net/~nobu/t/20060612.html#p01 http://www.rubyist.net/~nobu/t/20060612.html#c02 http://www.rubyist.net/~nobu/t/20060614.html#p01 それは,一番最後の http://www.rubyist.net/~nobu/t/20060614.html#p01 で言われている「境界面でラップする」という解決法を具体的にど のように実現すればよいかがわかりません.だれか教えてもらえま せんか? |
From: Masahiro S. ()
<sa...@to...> - 2006-06-14 06:56:11
|
酒井です。 From: "Kouhei Sutou" <ko...@co...> Date: Wed, 14 Jun 2006 11:11:48 +0900 > 須藤です. > > Ruby側で登録したコールバック(例えばsignal_connectとか)がCから呼び出される > とします.例えば,以下のようなプログラムで,以下のコマンドでコンパイルでき > ます. > > % cc `pkg-config gtk+-2.0 cairo --cflags --libs` \ > -I`ruby -rrbconfig -e 'print Config::CONFIG["archdir"]'` \ > -l`ruby -rrbconfig -e 'print Config::CONFIG["RUBY_SO_NAME"]'` \ > -o test test.c > > #include <gtk/gtk.h> > #include <ruby.h> > > int > main(int argc, char **argv) > { > gtk_init(&argc, &argv); > > ruby_init(); > ruby_init_loadpath(); > ruby_script(argv[0]); > ruby_set_argv(argc, argv); > > rb_eval_string("require 'gtk2'\n" > "Gtk.init\n" > "Gtk.timeout_add(0) do\n" > " raise 'timeout'\n" > "end\n"); > > gtk_main(); > > return 0; > } > > これを実行するとSegmentation faultで落ちます. > > (eval):3: [BUG] Segmentation fault > ruby 1.8.4 (2005-12-24) [i486-linux] このコードについては、Gtk.timeout_add で登録したブロックを呼び出す gtk_m_functionがrb_protectを使っていないのがまずいので、 それを修正すれば良いと思います。 > Rubyで書くとこんな感じですが,こちらは落ちません. > > require "gtk2" > > Gtk.init > Gtk.timeout_add(0) do > raise "timeout" > end > > Gtk.main こっちで落ちないのは、ruby_run()から実行されているため、 上のコードと異なり「例外でのジャンプ先」がちゃんとあるからです。 -- 酒井 政裕 / Masahiro Sakai |
From: Masahiro S. ()
<sa...@to...> - 2006-06-14 08:00:12
|
From: "Kouhei Sutou" <ko...@co...> Date: Wed, 14 Jun 2006 11:11:48 +0900 > 須藤です. > それは,一番最後の > > http://www.rubyist.net/~nobu/t/20060614.html#p01 > > で言われている「境界面でラップする」という解決法を具体的にど > のように実現すればよいかがわかりません.だれか教えてもらえま > せんか? コールバックで発生した例外等を、glib側にリターンする前にどこかに保存しておいて、 glib側からruby側にリターンしたときに、それを再び投げるということでしょう。 以前にも似たような話が出たことはあります。 http://ruby-gnome2.sourceforge.jp/ja/sfmltoj.cgi?key=/mailarchive/forum.php%3Fthread_id%3D1905665%26forum_id%3D9432 -- 酒井 政裕 / Masahiro Sakai |
From: Kouhei S. <ko...@co...> - 2006-06-14 08:36:45
|
須藤です. 06/06/14 に 酒井政裕 Masahiro Sakai<sa...@to...> さんは書きました: > > それは,一番最後の > > > > http://www.rubyist.net/~nobu/t/20060614.html#p01 > > > > で言われている「境界面でラップする」という解決法を具体的にど > > のように実現すればよいかがわかりません.だれか教えてもらえま > > せんか? > > コールバックで発生した例外等を、glib側にリターンする前にどこかに保存しておいて、 > glib側からruby側にリターンしたときに、それを再び投げるということでしょう。 やっぱりそういうことですか.でも,その「再び投げる」がいつに なるのかということがわからないです.って,↓の中にも書いてあ りますね. > 以前にも似たような話が出たことはあります。 > http://ruby-gnome2.sourceforge.jp/ja/sfmltoj.cgi?key=/mailarchive/forum.php%3Fthread_id%3D1905665%26forum_id%3D9432 ただ,「再び投げる」というのが実現できたとしても,Ruby側に リターン出来る場合はすぐにリターンした方が自然な気がします (現在の動作と同じ).そうしたい場合はRuby側にリターンでき るかどうかがわからないといけないと思いますが,そういうこと ってできるのでしょうか. # もう,ほんと全然わかっていなくてごめんなさい. |
From: Kouhei S. <ko...@co...> - 2006-06-14 14:28:37
|
須藤です. In <a79...@ma...> "Re: [ruby-gnome2-devel-ja] コールバック中の例外" on Wed, 14 Jun 2006 17:12:08 +0900, "Kouhei Sutou" <ko...@co...> wrote: > > > > > > http://www.rubyist.net/~nobu/t/20060614.html#p01 > > > > > > で言われている「境界面でラップする」という解決法を具体的にど > > > のように実現すればよいかがわかりません.だれか教えてもらえま > > > せんか? > > > > コールバックで発生した例外等を、glib側にリターンする前にどこかに保存しておいて、 > > glib側からruby側にリターンしたときに、それを再び投げるということでしょう。 > > ただ,「再び投げる」というのが実現できたとしても,Ruby側に > リターン出来る場合はすぐにリターンした方が自然な気がします > (現在の動作と同じ).そうしたい場合はRuby側にリターンでき > るかどうかがわからないといけないと思いますが,そういうこと > ってできるのでしょうか. 少し考えたのですが,もし,リターンできないというのがわかるな ら,そのときはSTDERRとかg_logとかでメッセージを表示するだけ で例外をあげない,というのが落としどころのような気がします. |
From: Masahiro S. ()
<sa...@to...> - 2006-06-14 15:33:02
|
酒井です。 From: "Kouhei Sutou" <ko...@co...> Date: Wed, 14 Jun 2006 17:12:08 +0900 > 須藤です. > > 06/06/14 に 酒井政裕 Masahiro Sakai<sa...@to...> さんは書きました: > > > > それは,一番最後の > > > > > > http://www.rubyist.net/~nobu/t/20060614.html#p01 > > > > > > で言われている「境界面でラップする」という解決法を具体的にど > > > のように実現すればよいかがわかりません.だれか教えてもらえま > > > せんか? > > > > コールバックで発生した例外等を、glib側にリターンする前にどこかに保存しておいて、 > > glib側からruby側にリターンしたときに、それを再び投げるということでしょう。 > > やっぱりそういうことですか.でも,その「再び投げる」がいつに > なるのかということがわからないです.って,↓の中にも書いてあ > りますね. > > > 以前にも似たような話が出たことはあります。 > > http://ruby-gnome2.sourceforge.jp/ja/sfmltoj.cgi?key=/mailarchive/forum.php%3Fthread_id%3D1905665%26forum_id%3D9432 glib側からruby側に戻ってくる個所のうち、内部で例外が発生した可能性ある 個所の全てに、「例外が発生していないかチェックして例外を再発生させるコー ド」をいれることになるんじゃないでしょうか。 それから、glib側でさらに処理が続き再びRubyのコードが呼ばれるような場合、 例えばgtkのメインループ等では、処理の中断をする必要があるでしょう。 ですが、そのようなAPIが常に存在するとは限らないはずです。 結局、例外や大域ジャンプを再送出するのは現実的ではないと思いますよ。 > ただ,「再び投げる」というのが実現できたとしても,Ruby側に > リターン出来る場合はすぐにリターンした方が自然な気がします > (現在の動作と同じ).そうしたい場合はRuby側にリターンでき > るかどうかがわからないといけないと思いますが,そういうこと > ってできるのでしょうか. 大域脱出して安全かどうかはglib側がどんな処理を行っているかによりますが、 一般にglibのコードはlongjmpで脱出することなんか考えずに設計されているので、 ドキュメントに特別な記述のない限り、まず危険と考えるべきでしょう。 現在問題が起らないとしてもそれはたまたまだと思います。 -- 酒井 政裕 / Masahiro Sakai |
From: Kouhei S. <ko...@co...> - 2006-06-14 14:34:47
|
須藤です. 06/06/14 に 酒井政裕 Masahiro Sakai<sa...@to...> さんは書きました: > このコードについては、Gtk.timeout_add で登録したブロックを呼び出す > gtk_m_functionがrb_protectを使っていないのがまずいので、 > それを修正すれば良いと思います。 はい,それはわかります. ただ,どう修正すればよいのかがわかりません.単にrb_protect() して例外をあげないようにすると,↓の例でも例外があがらないよ うになり,今までの動作とことなることになります. ということで,なかださんのいうように"ラップして外に送り出す" ということをしないといけないと思うのですが,それをどうすれば いいのかがわからないということです. http://www.rubyist.net/~nobu/t/20060614.html#p01 > > require "gtk2" > > > > Gtk.init > > Gtk.timeout_add(0) do > > raise "timeout" > > end > > > > Gtk.main > > こっちで落ちないのは、ruby_run()から実行されているため、 > 上のコードと異なり「例外でのジャンプ先」がちゃんとあるからです。 はい,そう思っていました. 同じ内容のコードがRubyで書いた場合とCで書いた場合で同じ ように動かないということを示したかったんです. |
From: Masao M. <mu...@hi...> - 2006-06-14 16:26:38
Attachments:
test.c
|
むとうです。 On Wed, 14 Jun 2006 17:03:03 +0900 "Kouhei Sutou" <ko...@co...> wrote: > 須藤です. > > 06/06/14 に 酒井政裕 Masahiro Sakai<sa...@to...> さんは書きました: > > > このコードについては、Gtk.timeout_add で登録したブロックを呼び出す > > gtk_m_functionがrb_protectを使っていないのがまずいので、 > > それを修正すれば良いと思います。 > > はい,それはわかります. > ただ,どう修正すればよいのかがわかりません.単にrb_protect() > して例外をあげないようにすると,↓の例でも例外があがらないよ > うになり,今までの動作とことなることになります. これなのですが、まぁ、今までの動作は置いておいて、 さかいさんのおっしゃるとおり、rb_protect()していない gtk_m_functionのバグで、そちらを修正した方が良さそうです。 過去の議論でもありましたが、以下のような整理になると思います。 1. Gtkのイベントループに登録したRubyのブロックがどこかで例外を発生した場合、 それをブロック内で処理(捕捉)しないでコケルのはアプリの不具合 2. 実際に捕捉していない例外が発生した場合、与えられたRubyのブロックの外側で例外 を捕捉し、Rubyが出してるっぽいエラーメッセージをRuby/GLib2がかわりに出す #ここまですればエラーの原因を開発者が発見できる・・・はず。 例えば、signal_connectで発生された例外を捕捉するのはどこか?というと どこで捕捉して良いのか定かではない(Gtk.mainをbegin/rescueで囲むのもカッコ悪いし signal_connectメソッドの周囲を囲むのも変)のでやはりブロックの中で閉じる、 というのがベターかと思います。 で、実際に、signal周りはこのような実装になっています。 #もちろん、実装したのはさかいさんです、念のため(笑)。 rbgobj_closure.cのrclosure_marshal_do()を参考にしてみてください。 それから実際にsignal_connectを使ったサンプルを添付しますね。timeout_addのとき とは違う動作をすることが確認できると思います。 #gtk_m_functionの方を修正してからお見せしようと思ったのですが、 #ちょっと今日は時間切れです・・・。すみません。 もちろん、完全にsegfaulをなくす方法があると良いんですが・・・。 ではでは。 -- .:% Masao Mutoh<mu...@hi...> |
From: Kouhei S. <ko...@co...> - 2006-06-17 04:52:04
|
須藤です. 06/06/15 に Masao Mutoh<mu...@hi...> さんは書きました: > 過去の議論でもありましたが、以下のような整理になると思います。 > 1. Gtkのイベントループに登録したRubyのブロックがどこかで例外を発生した場合、 > それをブロック内で処理(捕捉)しないでコケルのはアプリの不具合 > 2. 実際に捕捉していない例外が発生した場合、与えられたRubyのブロックの外側で例外 > を捕捉し、Rubyが出してるっぽいエラーメッセージをRuby/GLib2がかわりに出す > #ここまですればエラーの原因を開発者が発見できる・・・はず。 わかりました. > で、実際に、signal周りはこのような実装になっています。 > #もちろん、実装したのはさかいさんです、念のため(笑)。 確認しました. 実はもうひとつ気になっていることがあります.添付してあったプロ グラムを実行し,C-cするとSegmentation faultになります.これも やはりアプリケーション側の問題ということになるのでしょうか? |
From: Masao M. <mu...@hi...> - 2006-06-17 07:05:16
|
むとうです。 On Thu, 15 Jun 2006 09:42:09 +0900 "Kouhei Sutou" <ko...@co...> wrote: > 須藤です. > > 06/06/15 に Masao Mutoh<mu...@hi...> さんは書きました: > > > 過去の議論でもありましたが、以下のような整理になると思います。 > > 1. Gtkのイベントループに登録したRubyのブロックがどこかで例外を発生した場合、 > > それをブロック内で処理(捕捉)しないでコケルのはアプリの不具合 > > 2. 実際に捕捉していない例外が発生した場合、与えられたRubyのブロックの外側で例外 > > を捕捉し、Rubyが出してるっぽいエラーメッセージをRuby/GLib2がかわりに出す > > #ここまですればエラーの原因を開発者が発見できる・・・はず。 > > わかりました. > > > で、実際に、signal周りはこのような実装になっています。 > > #もちろん、実装したのはさかいさんです、念のため(笑)。 > > 確認しました. > > > 実はもうひとつ気になっていることがあります.添付してあったプロ > グラムを実行し,C-cするとSegmentation faultになります.これも > やはりアプリケーション側の問題ということになるのでしょうか? いやぁ、実は気づいたのですが、追うのやめてしまいました(苦笑)。 Ruby/GTK側でのsignal(GLibのシグナルではない方です)のハンドリング 周りが原因ですかね・・・・。 -- .:% Masao Mutoh<mu...@hi...> |
From: Masao M. <mu...@hi...> - 2006-06-17 09:38:48
|
むとうです。 On Thu, 15 Jun 2006 09:42:09 +0900 "Kouhei Sutou" <ko...@co...> wrote: > 須藤です. > 実はもうひとつ気になっていることがあります.添付してあったプロ > グラムを実行し,C-cするとSegmentation faultになります.これも > やはりアプリケーション側の問題ということになるのでしょうか? 調べてみたんですが、私の環境では以下のようなコードでもSegfaultします。 あまり深く追ってはいませんが、Ruby-GNOME2の問題ってわけじゃないん じゃないかなぁ。 #include <ruby.h> int main(int argc, char **argv) { ruby_init(); ruby_init_loadpath(); ruby_script(argv[0]); ruby_set_argv(argc, argv); rb_eval_string("loop {p 1; sleep 1}\n"); return 0; } 実行結果: 1 1 (eval): (eval):0:in `sleep': (Interrupt) from (eval):0 from (eval):0:in `loop' from (eval):0 (eval): [BUG] Segmentation fault ruby 1.8.2 (2004-12-25) [i386-linux] zsh: abort ./test2 -- .:% Masao Mutoh<mu...@hi...> |
From: Masao M. <mu...@hi...> - 2006-06-17 09:29:35
|
むとうです。 On Thu, 15 Jun 2006 01:26:12 +0900 Masao Mutoh <mu...@hi...> wrote: > むとうです。 > > On Wed, 14 Jun 2006 17:03:03 +0900 > "Kouhei Sutou" <ko...@co...> wrote: > > > 須藤です. > > > > 06/06/14 に 酒井政裕 Masahiro Sakai<sa...@to...> さんは書きました: > > > > > このコードについては、Gtk.timeout_add で登録したブロックを呼び出す > > > gtk_m_functionがrb_protectを使っていないのがまずいので、 > > > それを修正すれば良いと思います。 > > > > はい,それはわかります. > > ただ,どう修正すればよいのかがわかりません.単にrb_protect() > > して例外をあげないようにすると,↓の例でも例外があがらないよ > > うになり,今までの動作とことなることになります. > > これなのですが、まぁ、今までの動作は置いておいて、 > さかいさんのおっしゃるとおり、rb_protect()していない > gtk_m_functionのバグで、そちらを修正した方が良さそうです。 なおしました。 まず、Ruby/GLib2にG_PROTECT_CALLBACKというマクロを追加しました。 これで囲んだ関数内で例外が発生するとエラーメッセージを出して 異常終了します。 ここで、異常終了(exit(EXIT_FAILURE);)をさせてしまうのが rclosure_marshal_do()の今までの実装と異なります。 rclosure_marshal_do()もこちらのマクロを使うように変更しました ので、今後は「メインループ内に登録したブロック内でエラーが発生した場合は エラーメッセージを表示した上で異常終了する」 ということになります。 もしかしたら、Gtk.timeout_add等で例外が上がっても続いて欲しい・・・ というニーズがあるかと思ったのですが、やっぱりそこは、アプリ作者の 人が自分で例外を捕捉すべきと考え、exitしちゃうことにしました。 問題がありましたら言ってください。 -- .:% Masao Mutoh<mu...@hi...> |
From: Masao M. <mu...@hi...> - 2006-06-17 11:09:30
|
むとうです。 On Sat, 17 Jun 2006 18:29:26 +0900 Masao Mutoh <mu...@hi...> wrote: > むとうです。 > > これなのですが、まぁ、今までの動作は置いておいて、 > > さかいさんのおっしゃるとおり、rb_protect()していない > > gtk_m_functionのバグで、そちらを修正した方が良さそうです。 > > なおしました。 > > まず、Ruby/GLib2にG_PROTECT_CALLBACKというマクロを追加しました。 > これで囲んだ関数内で例外が発生するとエラーメッセージを出して > 異常終了します。 > > ここで、異常終了(exit(EXIT_FAILURE);)をさせてしまうのが > rclosure_marshal_do()の今までの実装と異なります。 > rclosure_marshal_do()もこちらのマクロを使うように変更しました > ので、今後は「メインループ内に登録したブロック内でエラーが発生した場合は > エラーメッセージを表示した上で異常終了する」 > ということになります。 > > もしかしたら、Gtk.timeout_add等で例外が上がっても続いて欲しい・・・ > というニーズがあるかと思ったのですが、やっぱりそこは、アプリ作者の > 人が自分で例外を捕捉すべきと考え、exitしちゃうことにしました。 > > 問題がありましたら言ってください。 強制的に終了してしまうしか手が無いとすると例えばデバッガなどで困ることも あるかと思い直し、GLib.exit_applicationというモジュール関数を作り それを呼び出すことにしました。 どうしても終了したくない場合はそちらをオーバーライドするということになります。 -- .:% Masao Mutoh<mu...@hi...> |