[Gnucash-changes] Eliminate a double free of memory.

Derek Atkins warlord at MIT.EDU
Thu Jun 2 22:11:11 EDT 2005


Quoting Chris Shoemaker <c.shoemaker at cox.net>:

> On Thu, Jun 02, 2005 at 05:29:06PM -0400, David Hampton wrote:
> > On Thu, 2005-06-02 at 16:59 -0400, Chris Shoemaker wrote:
> > > 1) The gtk_destroy signal handler (gnc_customer_window_destroy_cb) does
> > > NOT emit the CM event.  
> > > (It should be calling gnc_close_gui_component(cw->component_id);)
> > > 
> > > 2) The CM destroy handler (gnc_customer_window_close_handler) does NOT
> > > destroy the dialog context.  Instead, that's actually done inside the
> > > gtk_destroy signal handler.
> > > 
> > > 
> > > The biggest hint that this is an abuse of the CM api is the fact that
> > > the close handler is not explicitly unregistering the component with
> > > the CM, instead relying on it happening as a side-effect of the
> > > gtk_destroy signal.  If the close handler wanted to do anything with
> > > the component after it destroyed the dialog (like, say, read the
> > > non-gui state) it would be screwed because the whole component has
> > > been freed!
> > > 
> > > I think all the confusion goes away when you do something like this:
> > > [warning: not even compile tested]
> > > 
> > > // This is the gtk_destroy signal handler
> > > void
> > > gnc_customer_window_destroy_cb (GtkWidget *widget, gpointer data)
> > > {
> > >   CustomerWindow *cw = data;
> > > 
> > >   gnc_close_gui_component(cw->component_id);
> > > 
> > > }
> > > 
> > > // This is the CM close handler
> > > static void
> > > gnc_customer_window_close_handler (gpointer user_data)
> > > {
> > >   CustomerWindow *cw = user_data;
> > >   GncCustomer *customer = cw_get_customer (cw);
> > > 
> > >   gnc_suspend_gui_refresh ();
> > > 
> > >   if (cw->dialog_type == NEW_CUSTOMER && customer != NULL) {
> > >     gncCustomerBeginEdit (customer);
> > >     gncCustomerDestroy (customer);
> > >     cw->customer_guid = *xaccGUIDNULL ();
> > >   }
> > > 
> > >   gnc_unregister_gui_component (cw->component_id);
> > > 
> > >   gtk_widget_destroy (cw->dialog);
> > >   cw->dialog = NULL;
> > >   g_free (cw);
> > > 
> > >   gnc_resume_gui_refresh ();
> > > }
> > > 
> > > Makes a heck of a lot more sense to me.  David?
> > 
> > I think this will work because gtk_widget_destroy correctly handles
> > recursive invocation.  If the widget is already in the process of being
> > destroyed then the call to gtk_widget_destroy from
> > gnc_customer_window_close_handler is a noop, otherwise it will actually
> > destroy the dialog.
> 
> That makes sense since "gtk_widget_destroy" is supposed to mean "break
> all your references".  If they're already broken, re-calling does
> nothing.  
> 
> I'm not sure if the destroy-signal will get re-delivered in the
> recursive case, but I would hope that it wouldn't matter since I would
> expect the CM to ignore any events after the un-registration, whether
> the events are cached and then discarded because they arrived after
> the unregistration, or were discarded immediately because the
> component had already been unregistered.

It all depends which API gets called first.  If the user clicks the window
manager close button, it winds up calling gtk_widget_destroy() first, which
partially destroys the widget and calls the callback, which would call the CM,
which calls the CM callback, which would re-call the gtk_widget_destroy().

Basically, we need to handle re-entrancy in BOTH apis.  They are necessarily
tied together, which is why I don't consider it bad that we're assuming a
side-effect.

> -chris

-derek

-- 
       Derek Atkins, SB '93 MIT EE, SM '95 MIT Media Laboratory
       Member, MIT Student Information Processing Board  (SIPB)
       URL: http://web.mit.edu/warlord/    PP-ASEL-IA     N1NWH
       warlord at MIT.EDU                        PGP key available



More information about the gnucash-devel mailing list