This is the mail archive of the
insight@sources.redhat.com
mailing list for the Insight project.
Patch to fix edge case in itk destructor code
- From: Mo DeJong <supermo at bayarea dot net>
- To: insight at sources dot redhat dot com
- Date: Fri, 31 May 2002 15:35:06 -0700
- Subject: Patch to fix edge case in itk destructor code
- Organization: House of Mirth
Here is a patch that fixes an edge case involving an external itk widget that has
components that contain each other. The test cases for itk::Widget and itk::Toplevel
go into the gory details if you are interested.
cheers
Mo
2002-05-31 Mo DeJong <supermo@bayarea.net>
* itk/library/Toplevel.itk (destructor):
* itk/library/Widget.itk (destructor): Protect
against case where one external component destroy
destroys another componet widget.
* itk/tests/toplevel.test:
* itk/tests/widget.test: Test for bug where one
external component deletes another during the
itk destructor.
Index: itk/library/Toplevel.itk
===================================================================
RCS file: /cvs/src/src/itcl/itk/library/Toplevel.itk,v
retrieving revision 1.2
diff -u -r1.2 Toplevel.itk
--- itk/library/Toplevel.itk 22 Feb 2002 02:15:47 -0000 1.2
+++ itk/library/Toplevel.itk 31 May 2002 22:22:39 -0000
@@ -65,9 +65,19 @@
}
itk_component delete hull
- # Any remaining components must be outside the hull
- foreach component [component] {
- destroy [component $component]
+ # Any remaining components must be outside the hull.
+ # Loop twice to avoid an error that can happen if
+ # a component got destroyed as a result of another
+ # component getting destroyed.
+
+ set components [component]
+ foreach component $components {
+ set path($component) [component $component]
+ }
+ foreach component $components {
+ if {[winfo exists $path($component)]} {
+ destroy $path($component)
+ }
}
}
Index: itk/library/Widget.itk
===================================================================
RCS file: /cvs/src/src/itcl/itk/library/Widget.itk,v
retrieving revision 1.2
diff -u -r1.2 Widget.itk
--- itk/library/Widget.itk 22 Feb 2002 02:15:47 -0000 1.2
+++ itk/library/Widget.itk 31 May 2002 22:22:39 -0000
@@ -66,9 +66,19 @@
}
itk_component delete hull
- # Any remaining components must be outside the hull
- foreach component [component] {
- destroy [component $component]
+ # Any remaining components must be outside the hull.
+ # Loop twice to avoid an error that can happen if
+ # a component got destroyed as a result of another
+ # component getting destroyed.
+
+ set components [component]
+ foreach component $components {
+ set path($component) [component $component]
+ }
+ foreach component $components {
+ if {[winfo exists $path($component)]} {
+ destroy $path($component)
+ }
}
}
Index: itk/tests/toplevel.test
===================================================================
RCS file: /cvs/src/src/itcl/itk/tests/toplevel.test,v
retrieving revision 1.2
diff -u -r1.2 toplevel.test
--- itk/tests/toplevel.test 22 Feb 2002 02:15:48 -0000 1.2
+++ itk/tests/toplevel.test 31 May 2002 22:22:39 -0000
@@ -89,15 +89,20 @@
test toplevel-1.8 {when a mega-widget object is deleted, its window and any
components are destroyed (even if in another window) } {
+ catch {destroy .t1}
+ catch {destroy .t2}
+ catch {rename .t2 {}}
+ catch {itcl::delete class ButtonTop}
+
itcl::class ButtonTop {
inherit itk::Toplevel
constructor {args} {
eval itk_initialize $args
- itk_component add button {
+ itk_component add button {
button $itk_option(-container).b -text Button
- } {}
+ } {}
pack $itk_component(button)
}
@@ -106,17 +111,90 @@
toplevel .t1
ButtonTop .t2 -container .t1
-
set button [.t2 component button]
-
itcl::delete object .t2
-
set result [list $button [winfo exists $button]]
+ itcl::delete class ButtonTop
+ destroy .t1
+ set result
+} {.t1.b 0}
+
+test toplevel-1.9 {when a window that contains a megawidget component
+ is destroyed, the component is removed from the megawidget} {
+ catch {destroy .t1}
+ catch {destroy .t2}
+ catch {rename .t2 {}}
+ catch {itcl::delete class ButtonTop}
+
+ itcl::class ButtonTop {
+ inherit itk::Toplevel
+ constructor {args} {
+ eval itk_initialize $args
+
+ itk_component add button {
+ button $itk_option(-container).b -text Button
+ } {}
+ pack $itk_component(button)
+ }
+
+ itk_option define -container container Container {}
+ }
+
+ toplevel .t1
+ ButtonTop .t2 -container .t1
+ set result [list [.t2 component]]
+ destroy .t1
+ lappend result [list [.t2 component]]
+ itcl::delete object .t2
itcl::delete class ButtonTop
+ set result
+} {{button hull} hull}
+test toplevel-1.10 {when destroying a component that is inside another
+ window protect against that case where one component destroy
+ actually destroys other contained components} {
+ catch {destroy .t1}
+ catch {destroy .t2}
+ catch {rename .t2 {}}
+ catch {itcl::delete class ButtonTop}
+
+ itcl::class ButtonTop {
+ inherit itk::Toplevel
+
+ constructor {args} {
+ eval itk_initialize $args
+
+ # Note, the component names matter here since
+ # [.t2 component] returns names in hash order.
+ # We need to delete cframe first since it
+ # is the parent of cbutton.
+
+ itk_component add cframe {
+ button $itk_option(-container).cframe
+ } {}
+ pack $itk_component(cframe)
+
+ itk_component add cbutton {
+ button $itk_component(cframe).b -text Button
+ } {}
+ pack $itk_component(cbutton)
+ }
+
+ itk_option define -container container Container {}
+ }
+
+ toplevel .t1
+ ButtonTop .t2 -container .t1
+ set result [list [.t2 component]]
+ # destructor should destroy cframe but not cbutton
+ itcl::delete object .t2
+ lappend result [winfo exists .t1.cframe]
+ destroy .t1
+ itcl::delete class ButtonTop
set result
-} {.t1.b 0}
+} {{hull cframe cbutton} 0}
+
# ----------------------------------------------------------------------
# Clean up
Index: itk/tests/widget.test
===================================================================
RCS file: /cvs/src/src/itcl/itk/tests/widget.test,v
retrieving revision 1.2
diff -u -r1.2 widget.test
--- itk/tests/widget.test 22 Feb 2002 02:15:49 -0000 1.2
+++ itk/tests/widget.test 31 May 2002 22:22:39 -0000
@@ -267,15 +267,19 @@
test widget-1.27 {when a mega-widget object is deleted, its window and any
components are destroyed (even if in another window) } {
+ catch {destroy .t1}
+ catch {rename .t1.bw {}}
+ catch {itcl::delete class ButtonWidget}
+
itcl::class ButtonWidget {
inherit itk::Widget
constructor {args} {
eval itk_initialize $args
- itk_component add button {
+ itk_component add button {
button $itk_option(-container).b -text Button
- } {}
+ } {}
pack $itk_component(button)
}
@@ -290,15 +294,97 @@
pack .t1.bw
set button [.t1.bw component button]
-
itcl::delete object .t1.bw
-
set result [list $button [winfo exists $button]]
+ destroy .t1
+ itcl::delete class ButtonWidget
+ set result
+} {.t1.f.b 0}
+
+test widget-1.28 {when a window that contains a megawidget component
+ is destroyed, the component is removed from the megawidget} {
+ catch {destroy .t1}
+ catch {rename .t1.bw {}}
+ catch {itcl::delete class ButtonWidget}
+
+ itcl::class ButtonWidget {
+ inherit itk::Widget
+
+ constructor {args} {
+ eval itk_initialize $args
+ itk_component add button {
+ button $itk_option(-container).b -text Button
+ } {}
+ pack $itk_component(button)
+ }
+
+ itk_option define -container container Container {}
+ }
+
+ toplevel .t1
+ frame .t1.f
+ ButtonWidget .t1.bw -container .t1.f
+
+ pack .t1.f
+ pack .t1.bw
+ set result [list [.t1.bw component]]
+ destroy .t1.f
+ lappend result [list [.t1.bw component]]
+
+ itcl::delete object .t1.bw
+ destroy .t1
itcl::delete class ButtonWidget
+ set result
+} {{button hull} hull}
+
+test widget-1.29 {when destroying a component that is inside another
+ window protect against that case where one component destroy
+ actually destroys other contained components} {
+ catch {destroy .t1}
+ catch {rename .t1.bw {}}
+ catch {itcl::delete class ButtonWidget}
+
+ itcl::class ButtonWidget {
+ inherit itk::Widget
+ constructor {args} {
+ eval itk_initialize $args
+
+ # Note, the component names matter here since
+ # [.t2 component] returns names in hash order.
+ # We need to delete cframe first since it
+ # is the parent of cbutton.
+
+ itk_component add cframe {
+ button $itk_option(-container).cframe
+ } {}
+ pack $itk_component(cframe)
+
+ itk_component add cbutton {
+ button $itk_component(cframe).b -text Button
+ } {}
+ pack $itk_component(cbutton)
+ }
+
+ itk_option define -container container Container {}
+ }
+
+ toplevel .t1
+ frame .t1.f
+ ButtonWidget .t1.bw -container .t1.f
+
+ pack .t1.f
+ pack .t1.bw
+ set result [list [.t1.bw component]]
+ # destructor should destroy cframe but not cbutton
+ itcl::delete object .t1.bw
+ lappend result [winfo exists .t1.f.cframe]
+
+ destroy .t1
+ itcl::delete class ButtonWidget
set result
-} {.t1.f.b 0}
+} {{hull cframe cbutton} 0}
# ----------------------------------------------------------------------