« [英語] マシュマロ盗難の謎は解決されるもののの、Alfred Hitchcock というのは、あのHitchcockなのかどうなのか最後までよくわからず、結局、湖の中にいたSomethingも何なのかはっきり読み取れなかった分、どこか消化不良のまま終わってしまったJigsaw Jones シリーズ | トップページ | [英語] いかにも動機が子供らしいと思ってしまったJigsaw Jonesシリーズ »

2007年9月 4日 (火)

[Ruby] WIN32OLE_EVENT#unadvise


WIN32OLE_EVENTオブジェクトを途中でGCできないのは、
次のようなスクリプトを考慮しているから



ie = WIN32OLE.new('InternetExplorer.Application')
ev = WIN32OLE_EVENT.new(ie, 'DWebBrowserEvents')
ev.on_event{|*args| handler(*args)}
# ここでGC発生
ie.visible = true
ie.navigate(url) # handlerに通知したい
# ここで二度目のGC発生
while true
WIN32OLE_EVENT.message_loop
sleep 0.1
end


ev.on_event でイベントハンドラを定義しても、
その直後にGCが発生してevがGCされてしまうと
イベントを拾うことができない。



そこで、WIN32OLEオブジェクトがGCされない間、
つまり ieがGCされない間、evがGCされなければいいじゃん
という意見を頂戴した。
一瞬、いい案に思えたんだけど、上の例の二度目の
GCの後でも、ieはGCされても、InternetExplorerのプロセスは残るし
マウスで、Internet Explorerを操作できる。
その間にevがGCされると、イベントを拾うことができなくなる。
(現状では、evはGCされないので、イベントを拾い続ける。)



結局のところ、単純に ev がGCされるてもいいときは、
通知を受ける必要がなくなったときで、
それは、スクリプトを書いている人が判断すりゃいいじゃん
という投げやりな結論になってしまった。



ということで、もう通知は必要ないよというためのメソッド
WIN32OLE_EVENT#unadviseを trunkに追加してみた。



本当にGCされるかどうかは、まだ検証していないのだけど、
unadviseするとそれ以降は、通知されなくなるのは確認済み。



こういうのを用意すると、逆のWIN32OLE_EVENT#adviseも
欲しくなるんじゃないかと思うのだけど、
そっちは手元でテスト中なのでコミットはまだ。



名前は、IConnectionPoint の Unadviseから取ったんだけど、
ピンとこない人の方が多いかな。


|

« [英語] マシュマロ盗難の謎は解決されるもののの、Alfred Hitchcock というのは、あのHitchcockなのかどうなのか最後までよくわからず、結局、湖の中にいたSomethingも何なのかはっきり読み取れなかった分、どこか消化不良のまま終わってしまったJigsaw Jones シリーズ | トップページ | [英語] いかにも動機が子供らしいと思ってしまったJigsaw Jonesシリーズ »

コメント

まるでユーザではないのですが。。。

ie.instance_variable_set("@event", ev)
な状態(「ieがGCされない間、evがGCされなければいいじゃん」)のあとに
ie = nil
GC.start
というような状態になったら「通知を受ける必要がなくなったと」「スクリプトを書いている人が判断」したと動くのがRubyっぽいかなぁと思いました。
明示的に切り離すメソッドも必要だとは思いますが、それはデフォルトじゃない方がいいかなぁと思いました。


Internet Explorerが死ぬまでイベントを拾い続けるようにするのなら、Internet Explorerが死ぬまでevをGCしないようにするというのはどうでしょうか。
できるのかどうかわかりませんが、内部にevをGCから保護するようなテーブルみたいなのを持って、Internet Explorerが死んだイベントがきたらそのテーブルからevを削除する、みたいなことはできないのかなぁと思いました。
(Ruby/GTK2はこんな風にしたはず。)

こうすれば、スクリプトが終了しなくてもevが必要なくなったらGCされる気がします。

投稿: kou | 2007年9月 5日 (水) 13時02分

1つ目の案は現行のバージョンと動作が異なるので、躊躇してます。逆に意識しなくても、ieがGCされるときにはGCされちゃうので、その瞬間からイベントが飛んでこなくなると、あれ?なんで?と戸惑う人が出てくるんじゃないかと。

2つ目の案は、実は実際にやってみました。
1つ問題があって、IEの終了のタイミングとRubyの終了のタイミングがほぼ同時の場合、IEからの通知を受けてテーブルからevを削除しようとするのと、Rubyスクリプト終了時のテーブルまるごとGCがぶつかることがあるみたいでコアダンプすることがあり、実現できていないんです。
それさえうまくクリアできれば、2つ目の案を採用したいところです。

投稿: suke | 2007年9月 5日 (水) 20時18分

なるほど。
やはり、もうすでに試していたのですね。。。

で、懲りずにアイディアだけですが、
テーブルにオブジェクトをぶちこむんじゃなくて、markとIEが死んだかを表すflagを使うのはどうでしょうか?

RubyのGC中にIEが死んで通知がきても、(VALUEじゃない)flagをいじるだけになるのでRubyは死なない気がします。
(GCが走ってmarkが呼ばれるときはflagを見てマークするかどうかを変えてやる)

flagの置き場所ですがoleeventdataにひとつフィールドを追加しちゃうだけでよさそうな気はします。

投稿: kou | 2007年9月 6日 (木) 13時48分

すいません。ちょっとよくわからなかったのですが、

Data_Make_Struct(k, oleeventdata, ev_mark, pev);

みたいにして

ev_mark(oleeventdata *pev) {
  if (pev->flag) {
    rb_gc_mark する。
  }
}

みたいなことでしょうか?
これって、oleeventdata自身に自分のVALUEを持つということになるんでしょうか?それともなんか勘違いしてますか?

投稿: suke | 2007年9月 6日 (木) 20時42分

はい、そういうことを言いたかったです。


oleeventdataは自分のVALUEを持たなくてもよいと思っていました。

IEからの、俺死んだよ、通知がどうやってくるのかがわからないのですが、oleeventdataだけの情報で通知を受けることができると思っていました。
で、それができるのなら、その通知を受けたときにflagを1にしてやればいいかなぁと思っていました。


# よくわからないくせにごちゃごちゃ混乱させてすみません。

投稿: kou | 2007年9月 8日 (土) 09時47分

rb_gc_markとかの活用は考えたことが無かったので参考になりました。ありがとうございます。
自分でも、まだ問題が整理ができていないように感じているので、もう少し考えてみます。

投稿: suke | 2007年9月 8日 (土) 16時44分

あぁ。。。
ごめんなさい、ごめんなさい、ごめんなさい。。。

私、本当にアホなことを書いていました。
その通りです。VALUEがないとマークできないです。
# 自分自身もマークできると勘違いしていた。。。


言いたかったのはIEが死んだときの通知にfreeしないで、freeするのはGCのときだけにすればどうかしら、ということだったんです。。。
本当にすみませんでした。。。


マークで頑張るならWIN32OLE_EVENTのインスタンスを管理するグローバルなオブジェクトを一つ用意しなきゃいけないと思います。
って、これもまた余計なことになるかも。。。

投稿: kou | 2007年9月 8日 (土) 19時31分

そうですよねえ。やっぱりVALUEを持つのを用意しないと駄目ですよね。すっきりしました。よかったです。

いろいろ考えたんですが、根本的に、IEからの通知とGCのタイミングは同期していないように思えるので、いずれにせよ途中でevをGCするのはまずい場合がありそうな気がしてきてます。

投稿: suke | 2007年9月 8日 (土) 23時39分

この記事へのコメントは終了しました。

トラックバック


この記事へのトラックバック一覧です: [Ruby] WIN32OLE_EVENT#unadvise:

« [英語] マシュマロ盗難の謎は解決されるもののの、Alfred Hitchcock というのは、あのHitchcockなのかどうなのか最後までよくわからず、結局、湖の中にいたSomethingも何なのかはっきり読み取れなかった分、どこか消化不良のまま終わってしまったJigsaw Jones シリーズ | トップページ | [英語] いかにも動機が子供らしいと思ってしまったJigsaw Jonesシリーズ »