From: Kouhei S. <ko...@co...> - 2006-03-07 12:10:15
|
須藤です. 現在,GLib::Object#signal_connectで登録したハンドラは自分で 削除しないと削除されないようです. このため, object.signal_connect("XXX") do ... end というようにハンドラを登録すると,ハンドラがobjectの参照を持っ たまま生き残ってしまい,objectがGCされません. これは以下のスクリプトを実行するとわかります. require 'glib2' def x(disconnect) o = GLib::Object.new id = o.signal_connect("notify") {} o.signal_handler_disconnect(id) if disconnect nil end [true, false].each do |disconnect| GC.disable 100.times {x(disconnect)} p [disconnect, :before, ObjectSpace.each_object(GLib::Object){}] GC.enable GC.start p [disconnect, :after, ObjectSpace.each_object(GLib::Object){}] end このスクリプトを実行するとこうなるはずです. [true, :before, 100] [true, :after, 0] [false, :before, 100] [false, :after, 100] -- 現状: 登録されたハンドラはCレベルのグローバルなハッシュテーブルに 保存され,rubyのGCからも保護されています.このハッシュテーブ ルにはハンドラとsignal_connectの第二引数以降の値を保存してい ます. ハッシュテーブルの情報はグローバルなハッシュテーブルで管理さ れているため,ハンドラが登録されているオブジェクトが死んでも 生き残ります.このため,signal_handler_disconnectで明示的に ハンドラを削除しないと,ハンドラが登録対象のオブジェクトを参 照しつづけるため,登録対象のオブジェクトがGCされません. 解決策案: ハッシュテーブルにハンドラだけではなく,登録対象のオブジェク トを見付けることができる情報(idとかGObjectのポインタとか) も含めておく.g_object_weak_refで登録対象のオブジェクトが死 んだのを判断できるようにして,登録対象のオブジェクトが死んだ らハッシュテーブルからハンドラを削除する. ただし,同じハンドラが複数のオブジェクトに登録されることもあ るため,登録された回数とオブジェクトが死んだときにハンドラを 削除する回数のバランスを保たないと,SEGVする気がする. こんな感じでよさそうな気がするけど,どうかなぁ. table = { handler1 => [target_obj1, target_obj2], handler2 => [target_obj1, target_obj3], ..., } ハンドラ登録: table[handler] ||= [] table[handler] << target_obj ハンドラ削除: table[table.index(handler), 1] = nil table.delete(handler) if table[handler].empty? |