From: Jakub Travnik Date: 2002-09-20T06:38:28+09:00 Subject: [BUG] ruby/tcltk reentrancy bug Hello, I have found a bug in ruby's tcltk interface. Following code demonstrates problem: --------------------------------- require 'tk' @proc3=proc{ puts 'proc3, the old one'} proc2a=proc{ puts 'proc2 start' # IF EXCEPTION IS UNHANDLED, THIS WILL CRASH something_not_defined puts 'proc2 end' } proc2b=proc{ puts 'proc2 start' @proc3=proc{ puts 'proc3 start' # IF EXCEPTION IS UNHANDLED, THIS WILL CRASH # WHEN CALLED FROM ANYWHERE something_not_defined puts 'proc3 end' } puts 'proc2 end' } proc1=proc{ puts 'proc1 start' @dialog1=TkToplevel.new(@root) TkButton.new(@dialog1, 'text'=>'proc2a', 'command'=>proc2a).pack TkButton.new(@dialog1, 'text'=>'proc2b', 'command'=>proc2b).pack @dialog1.title 'dialog 1' @dialog1.transient(@parent) @dialog1.withdraw @dialog1.update @dialog1.deiconify @dialog1.grab @dialog1.wait_destroy puts 'proc1 end' } def runproc3 @proc3.call end @root=TkRoot.new TkButton.new(@root, 'text'=>'proc1', 'command'=>proc1).pack TkButton.new(@root, 'text'=>'runproc3', 'command'=>proc{runproc3}).pack Tk.mainloop --------------------------------- To crash follow these steps: Variant A: press button 'proc1' then 'proc2a'. Variant B: press button 'proc1', 'proc2b', close dialog, then press 'runproc3'. Problem depends on wait_destroy call reentrancy. Bug happen when exception is raised, but not handled inside reentrant part. Problem appears also when Proc object is defined in lexical environment of part called in reentrant way. (Variant B) Affected ruby versions: ruby 1.6.7 (2002-03-19) [i386-linux] This Windows version seems to survive above example: ruby 1.6.7 (2002-03-01) [i586-mswin32] But following code with more deep reentrancy will kill both windows and linux versions of ruby: --------------------------------- require 'tk' class Dialog def initialize(parent, &block) @parent=parent @block=block end def run @dialog=TkToplevel.new(@parent) @block.call @dialog @dialog.title 'dialog' @dialog.transient(@parent) @dialog.withdraw @dialog.update @dialog.deiconify @dialog.grab @dialog.wait_destroy end def exit @dialog.destroy end end w1=TkRoot.new TkButton.new(w1, 'text'=>'button 1', 'command'=>proc{ puts 'button1 start' d1=Dialog.new(w1){|w2| TkButton.new(w2, 'text'=>'button 2', 'command'=>proc{ d2=Dialog.new(w2){|w3| puts 'button2 start' TkButton.new(w3, 'text'=>'button 3', 'command'=>proc{ d3=Dialog.new(w3){|w4| puts 'button3 start' TkLabel.new(w4, 'text'=>'label').pack something_not_defined # <-- unhandled exception puts 'button3 end' } d3.run }).pack puts 'button2 end' } d2.run }).pack } d1.run puts 'button1 end' }).pack Tk.mainloop --------------------------------- To crash: press buttons as they appear. I think that this is bug in ruby since, when no exception occurs or it is handled before return from reentrancy, no crash will happen. Best regards, Jakub Travnik, jabber://jtra@jabber.com