[comp.windows.x] question about ancestor list in _XtFindRemapWidget

gourley@CAD.MCC.COM (Kevin Gourley) (09/25/90)

Jeff, I think you ran into the same bug that I was experiencing.  I
sent out a message to xpert a few weeks ago about this bug.  I later
sent it in as a bug report with suggested patch to xbug.  I have
included my original message below.  The reason I am cc'ing it along
to xpert again is because I DID MAKE ONE CHANGE TO MY PATCH.  [I
changed my call of XtScreen to XtScreenOfObject.]

Kevin Gourley (gourley@mcc.com) 512-338-3589
Microelectronics and Computer Technology Corp., Austin, TX



I have been experiencing a problem that has been causing 'bus error's
frequently since we started using GNU malloc.  I finally tracked it down
to a bug in Xt.  The use of GNU malloc only makes the bug more apparent.

The problem is in EventUtil.c in _XtFindRemapWidget:

Widget _XtFindRemapWidget(event, widget, mask, pdi)
    XEvent	*event;
    Widget	widget;
    EventMask	mask;
    XtPerDisplayInput pdi;
{
    Widget		dspWidget = widget;
    
+->  if (!pdi->traceDepth || !(widget == pdi->trace[0]))
|     {
|	  _XtFillAncestorList(&pdi->trace, &pdi->traceMax, 
|			      &pdi->traceDepth, widget, NULL);
|	  pdi->focusWidget = NULL; /* invalidate the focus
|				      cache */
|      }
|    ...
|
|
This 'if' condition leads to erroneous results.  pdi retains
a cached snapshot of the path up the widget hierarchy in
pdi->trace (which is an array of widget pointers).  These values are
recomputed by calling _XtFillAncestorList whenever it is
determined that the cache is no longer valid.  The condition in
the 'if' statement is NOT SUFFICIENT to adequately check to see
if the pdi->trace array is still valid.

I had some code in a rather complex application that created a dialog
box composed of a hierarchy of widgets.  Upon interacting with that
dialog, values would get cached in the pdi->trace array.  Then at some
later point, I would end up destroying the widgets and their memory
would get 'free'd.  Later, when I created a new dialog composed of
freshly allocated widgets, I would occasionally encounter cases where
some of the new widgets coincided in memory location with the
addresses of widgets in the previous dialog.  (This was a result of a
smarter memory allocator).  The problem is that even though I have a
new hierarchy of widgets, the '(widget == pdi->trace[0])' test was
erroneous because sometimes the widget pointer WAS indeed the same as
the cached widget pointer value in pdi->trace[0] BUT it was not the
SAME widget.  Since the time that the widget pointer value was cached,
that widget was freed and a new widget was allocated in the same
space.  That was the only thing that was the same though!  The rest of
the pdi->trace array did not reflect the current path up the widget
hierarchy and in fact contained stale pointers to widgets that had
been freed.  Since the 'if' clause did not detect the erroneous
pdi->trace array, it did not recompute the values by calling
_XtFillAncestorList and it resulted in 'bus errors' further down in
the code.

This doesn't appear to be as much of a problem in other forms of
malloc because new widgets don't tend to get allocated in the exact
same location as older freed widgets.

My solution is the following:

1) I created a function in EventUtil.c that resets the pdi->traceDepth
   to 0, thus causing the pdi->trace array to be recomputed when necessary.

     void _XtClearPerDisplayInputCache(dpy)
     Display       *dpy;
     {
         XtPerDisplayInput pdi;
    
         pdi = _XtGetPerDisplayInput(dpy);
	 if (pdi) pdi->traceDepth = 0;
     }

2) I changed a function in Destroy.c that now calls
   _XtClearPerDisplayInputCache whenever a widget is desroyed to mark
   the cache as "dirty".

      static void Phase1Destroy (widget)
      Widget    widget;
      {
>         extern void _XtClearPerDisplayInputCache();
>         Screen  *screen;
      
          widget->core.being_destroyed = TRUE;
      
>          if (screen = XtScreenOfObject(widget))
>              _XtClearPerDisplayInputCache(DisplayOfScreen(screen));

      } /* Phase1Destroy */


Please let me know if this has been fixed by a patch or if my solution is
not correct.  Thank you.
     /                      ____              
    /__/ ___      o ___    /     ___           / ___ 
   /\   /__/ | / / /  /   / --- /  / /  / /-- / /__/ | /
  /  | /__   |/ / /  /   /___/ /__/ /__/ /   / /__   |/
  ___________________________________________________/
  Microelectronics & Computer Tech.Corp.(MCC), CAD Dept.
  3500 W.Balcones Center Dr., Austin,TX   78759                        
  gourley@mcc.com  (512)338-3589      FAX:(512)338-3600

----------------------------------
In 'diff' form, the patches are:

Xt/EventUtil.c:
74a75,84
> void _XtClearPerDisplayInputCache(dpy)
> Display       *dpy;
> {
>     XtPerDisplayInput pdi;
>
>     pdi = _XtGetPerDisplayInput(dpy);
>     if (pdi) pdi->traceDepth = 0;
> }
>
>

Xt/Destroy.c:
59a60,62
>     extern void _XtClearPerDisplayInputCache();
>     Screen  *screen;
>
60a64,67
>
>     if (screen = XtScreen(widget))
>       _XtClearPerDisplayInputCache(DisplayOfScreen(screen));
>
----------------------------------