Gtk+ signal bug




I have finally tracked down the long standing bug in gtk--.  
It wasn't in gtk-- nor did it have anything to do with the virtual 
functions not getting called back in the right order or getting missed.
(It wasn't until I managed to replicate it with pure gtk+ that
I could be sure.)

The underlyning problem is memory corruption in gtk+.  A
simple series of actions allow a signal to be disconnected twice
resulting in gtk+ falling back and using a piece of deleted memory
which corrupt the stack.

------------------------------------------------------------------
#include <gtk/gtk.h>

GtkWidget *win;
GtkWidget *button;
int cid;

void myquit(void*,void*)
 {
   gtk_object_destroy(GTK_OBJECT(button));
   gtk_object_destroy(GTK_OBJECT(win));
   gtk_signal_disconnect(GTK_OBJECT(button),cid);
 }
main(int argc, char ** argv)
 {
   gtk_init(&argc,&argv);
   win=gtk_window_new(GTK_WINDOW_TOPLEVEL);
   button=gtk_button_new_with_label("close");
   gtk_container_add(GTK_CONTAINER(win),button);
   cid=gtk_signal_connect(GTK_OBJECT(button),"clicked",GTK_SIGNAL_FUNC(&myquit),0);
   gtk_widget_show_all(win);
   gtk_main();
 };
------------------------------------------------------------------

This code is roughly equivelent to gtk-- deletion sequence.  
The user asked for the wrapper to be deleted in myquit.  This
triggers destroy() on all objects controlled by that item.  
And later it causes all of the signal connections which reference
the C++ layer to be disconnected.

Therein lays the problem.  Gtk+ will unreference the signal handler
for cid 3 times, even through it has a count of 2.   

The sequence of events looks like...

   gtk_signal_connect()        : ref=1;
     gtk_handlers_run()        : ref=2;
       gtk_object_destroy()    : ref=1;
       gtk_signal_disconnect() : ref=0;
     gtk_handlers_run()        : ref=-1; (warns)

Thus the handler remaining on the list and being disconnected after
the destroy is triggering errors.   It is impossible for us to surpress
the disconnect, because it is a seperate entire and it is a invarient
that either gtk+ disconnects the signal before the C++ object dies,
or that we disconnect it ourselves.

Possible solutions.  
  1) The handler could have a flag to indicate that it is already orphaned 
     by the object and thus ignores the disconnect.
  2) The object could remove referenced to the handlers it is abandoning.
  3) When the object destroys, but the ref counter is still one, we
     could call the destroy noticification which would disable the 
     disconnect.  (handler sends disconnect notify when its object is
     going away rather than when it is going away.)

Number 2 isn't good if we get a warnign that we are trying to
disconnect a connection id which is already there.  
Number 3 may be workable since in order for this to occure the 
handler must be emitting and thus used its argument list.  However,
there is no way to know is code depends on the handler sending
its destroy notification late.

I can provide additional information if needed or patches for the
various solutions once the best course of action is decided.

--Karl



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]