Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Is it legal to hold a ptr to a Tk_Window after the window has been destroyed?

0 views
Skip to first unread message

Mo

unread,
Feb 22, 2002, 12:31:23 AM2/22/02
to
Hi all.

I have run into a rather tricky problem that has to do with
itk 3.2 and Tcl/Tk 8.3, and I could use a little help.
The code I am looking at holds a pointer to a Tk_Window
structure and then uses this structure to clean up after
itself.


Tk_Window tkwin;

...

tkwin = Tk_NameToWindow(...);

Later on, when a mega widget is destroyed or deleted, the
following could happen.

Tcl_DStringInit(&buffer);
Tcl_DStringAppend(&buffer, "itk::remove_destroy_hook ", -1);
Tcl_DStringAppend(&buffer, Tk_PathName(tkwin), -1);
Tcl_Eval(interp, Tcl_DStringValue(&buffer));


The problem is that the window that was attached to this
Tk_Window structure could have been destroyed just before
this code is run. That gives us some strange results because
the Tk_PathName(tkwin) command returns a TkWindow->pathName
char* pointer which is no longer valid.


A quick peek into the ChangeLog shows that the esteemed Mr. Hobbs
seemed to be working on fixing this sort of problem with the
following patch:

2001-07-02 Jeff Hobbs <je...@ActiveState.com>

* generic/tkWindow.c (Tk_DestroyWindow): changed to use
Tcl_EventuallyFree instead of ckfree so that widgets that have
references to a tkwin can use them.

Index: tkWindow.c
===================================================================
RCS file: /cvsroot/tktoolkit/tk/generic/tkWindow.c,v
retrieving revision 1.30
retrieving revision 1.31
diff -u -r1.30 -r1.31
--- tkWindow.c 28 May 2001 16:56:02 -0000 1.30
+++ tkWindow.c 3 Jul 2001 05:59:50 -0000 1.31
@@ -1496,3 +1496,3 @@
}
- ckfree((char *) winPtr);
+ Tcl_EventuallyFree((ClientData) winPtr, TCL_DYNAMIC);

This call to Tcl_EventuallyFree() would seem to indicate that Tk
was meant to support the use of a Tk_Window structure even
after the widget itself was destroyed. Problem is, this does not
seem to work in practice.


One might assume you could do something like so:


tkwin = Tk_NameToWindow(...);
Tcl_Preserve((ClientData) tkwin);

And when you were done with the Tk_Window:

Tcl_DStringInit(&buffer);
Tcl_DStringAppend(&buffer, "itk::remove_destroy_hook ", -1);
Tcl_DStringAppend(&buffer, Tk_PathName(tkwin), -1);
Tcl_Eval(interp, Tcl_DStringValue(&buffer));

Tcl_Release((ClientData) tkwin);

Sadly, this does not work either. The problem here is that the
char* data pointed to by the TkWindow->pathName member is stored
inside of the parentPtr->mainPtr->nameTable hash record (as you
can see by looking at the NameWindow() method in tkWindow.c).

When the window is destroyed by a call to Tk_DestroyWindow the
following gets run:

if (winPtr->pathName != NULL) {
...
Tcl_DeleteHashEntry(Tcl_FindHashEntry(&winPtr->mainPtr->nameTable,
winPtr->pathName));
}

That call to Tcl_DeleteHashEntry() will delete the Tcl_HashEntry associated
with the window name in the parent's hash table. It will also deallocate
the memory pointed to by the TkWindow->pathName member because they are
allocated together as seen in the StringCreate() method in tclHash.c.
This is why the call to Tk_PathName(tkwin) was returning garbage.

Is use of a Tk_Window structure after the window has been destroyed
something that should be supported in Tk? If so, should the structure
be deallocated in two phases? I could imagine an initial phase that
would free up internal memory but keep just enough around so that
the macros used to access a Tk_Window would still work. Then a
second function could be called after all the ref counts help
by Tcl_Preserve calls were released.

If one should simply avoid accessing any of the members of a Tk_FakeWin
after the window has been destroyed, then perhaps we should add a macro
to like so to tk.h:

#define Tk_IsAlreadyDead(tkwin) \
(((Tk_FakeWin *) (tkwin))->flags & TK_ALREADY_DEAD)


Any advice you could provide would help.

thanks
Mo

George A. Howlett

unread,
Feb 22, 2002, 7:38:40 PM2/22/02
to
Mo <m...@nospam.com> wrote:
> Is use of a Tk_Window structure after the window has been destroyed
> something that should be supported in Tk? If so, should the structure
> be deallocated in two phases? I could imagine an initial phase that
> would free up internal memory but keep just enough around so that
> the macros used to access a Tk_Window would still work. Then a
> second function could be called after all the ref counts help
> by Tcl_Preserve calls were released.

> If one should simply avoid accessing any of the members of a Tk_FakeWin
> after the window has been destroyed, then perhaps we should add a macro
> to like so to tk.h:

> #define Tk_IsAlreadyDead(tkwin) \
> (((Tk_FakeWin *) (tkwin))->flags & TK_ALREADY_DEAD)

In Tk's original design, you don't use the Tk_Window token after the
window is destroyed. If fact, accepted Tk programming style was to
mark it NULL to indicate that it had been destroyed.

You'll note that none of the Tk resource *Free* routines

Tk_FreeOptions(specs, widgRec, display, flags);
Tk_FreeColor(colorPtr);
Tk_FreeBitmap(display, bitmap);
Tk_FreeFont(tkfont);

take a Tk_Window as an argument. Instead you save the display pointer
in your widget structure just for this purpose.

This irrevocably changed with the unfortunate Tk_OptionTable API. The
Tcl_Obj versions all need a Tk_Window argument.

Tk_FreeConfigOptions(recordPtr, optionTable, tkwin);
Tk_FreeColorFromObj(tkwin, objPtr);
Tk_FreeBitmapFromObj(tkwin, objPtr);
Tk_FreeFontFromObj(tkwin, objPtr);

This forces a Tk_Window has to be somewhat useable, even after the
window has been destroyed. To what extent? That's not documented.
Obviously, Tk_WindowId isn't going to return anything useful. Jeff
patched tkWindow.c to cover this hole in the API. Unfortunately for
you, you're finding out what the bandaid covers and doesn't.

I won't even question the wisdom of colors and fonts as Tcl_Objs.

The best advice is avoid using Tk_Window if you can, and save the path
name of the window in the [incr Tk]'s ArchComponent structure so that
you don't need to rely on the Tk holding the pathname in memory
somewhere.

--gah (who never uses Tk_CreateOptionTable)

Mo

unread,
Feb 23, 2002, 7:49:33 PM2/23/02
to
"George A. Howlett" wrote:

> The best advice is avoid using Tk_Window if you can, and save the path
> name of the window in the [incr Tk]'s ArchComponent structure so that
> you don't need to rely on the Tk holding the pathname in memory
> somewhere.
>
> --gah (who never uses Tk_CreateOptionTable)

Thanks. I took your advice and created patch 521922 for itk.

http://sourceforge.net/tracker/index.php?func=detail&aid=521922&group_id=13244&atid=313244

cheers
Mo

0 new messages