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