gourley@CAD.MCC.COM (Kevin Gourley) (09/14/90)
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 = XtScreen(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)); > ----------------------------------