Tags: patch Followup-For: Bug #494515 I found two revision in the upstream SVN that deal with garbage collection (revisions 3255 and 3263). When I applied these changes to the Debian package, the script runs without crashing.
(The script doesn't animate properly though, it should use a GtkDrawingArea. Fixed version attached :)
--- a/glib/ChangeLog +++ b/glib/ChangeLog @@ -1,3 +1,17 @@ +2008-06-19 Kouhei Sutou <[EMAIL PROTECTED]> + + * src/rbgobj_type.c (rbgobj_lookup_class_by_gtype_body): add + create_class option. + + * src/rbgobject.h (GTYPE2CINFO_NO_CREATE): add. + + * src/rbgobject.h, src/rbgobj_type.c + (rbgobj_lookup_class_by_gtype_full): add. + + * src/rbgobj_typeinstance.c (each_cinfo): don't create object + during GC to work with ruby 1.8.7. + Reported by Kazuhiro NISHIYAMA. Thanks!!! + 2008-05-23 Kouhei Sutou <[EMAIL PROTECTED]> * test/run-test.rb: use which not --version to detect make. --- a/glib/src/rbgobj_type.c +++ b/glib/src/rbgobj_type.c @@ -39,8 +39,9 @@ } RGObjClassInfoDynamic; typedef struct { - VALUE parent; - GType gtype; + VALUE parent; + GType gtype; + gboolean create_class; } RGObjClassByGtypeData; static void @@ -125,10 +126,14 @@ return (VALUE)NULL; c = rb_hash_aref(gtype_to_cinfo, INT2NUM(gtype)); - if (!NIL_P(c)){ + if (!NIL_P(c)) { Data_Get_Struct(c, RGObjClassInfo, cinfo); return (VALUE)cinfo; } + + if (!cdata->create_class) + return (VALUE)NULL; + c = Data_Make_Struct(rb_cData, RGObjClassInfo, cinfo_mark, NULL, cinfo); cinfo->gtype = gtype; cinfo->mark = NULL; @@ -230,8 +235,19 @@ const RGObjClassInfo * rbgobj_lookup_class_by_gtype(GType gtype, VALUE parent) { + return rbgobj_lookup_class_by_gtype_full(gtype, parent, TRUE); +} + +const RGObjClassInfo * +rbgobj_lookup_class_by_gtype_full(GType gtype, VALUE parent, + gboolean create_class) +{ VALUE critical = rb_thread_critical; - RGObjClassByGtypeData data = { .parent = parent, .gtype = gtype }; + RGObjClassByGtypeData data; + + data.gtype = gtype; + data.parent = parent; + data.create_class = create_class; rb_thread_critical = 1; --- a/glib/src/rbgobject.h +++ b/glib/src/rbgobject.h @@ -76,6 +76,7 @@ #define CLASS2CINFO(klass) (rbgobj_lookup_class(klass)) #define GTYPE2CINFO(gtype) (rbgobj_lookup_class_by_gtype(gtype, Qnil)) +#define GTYPE2CINFO_NO_CREATE(gtype) (rbgobj_lookup_class_by_gtype_full(gtype, Qnil, FALSE)) #define RVAL2CINFO(obj) (rbgobj_lookup_class(CLASS_OF(obj))) #define GTYPE2CLASS(gtype) (rbgobj_gtype_to_ruby_class(gtype)) #define CLASS2GTYPE(klass) (rbgobj_lookup_class(klass)->gtype) @@ -150,8 +151,11 @@ /* rbgobj_type.c */ -extern const RGObjClassInfo* rbgobj_lookup_class(VALUE klass); -extern const RGObjClassInfo* rbgobj_lookup_class_by_gtype(GType gtype, VALUE parent); +extern const RGObjClassInfo *rbgobj_lookup_class(VALUE klass); +extern const RGObjClassInfo *rbgobj_lookup_class_by_gtype(GType gtype, VALUE parent); +extern const RGObjClassInfo *rbgobj_lookup_class_by_gtype_full(GType gtype, + VALUE parent, + gboolean create_object); extern VALUE rbgobj_gtype_to_ruby_class(GType gtype); extern VALUE rbgobj_define_class(GType gtype, const gchar* name, VALUE module, void* mark, void* free, VALUE parent); --- a/glib/src/rbgobj_typeinstance.c +++ b/glib/src/rbgobj_typeinstance.c @@ -16,6 +16,10 @@ VALUE cInstantiatable; +typedef void (*ClassInfoCallbackFunc) (gpointer instance, + const RGObjClassInfo *class_info, + gpointer user_data); + static VALUE instantiatable_s_allocate(klass) VALUE klass; @@ -40,9 +44,7 @@ /**********************************************************************/ static void -each_cinfo(gpointer instance, - void (*func)(gpointer instance, const RGObjClassInfo* cinfo, gpointer user_data), - gpointer user_data) +each_cinfo(gpointer instance, ClassInfoCallbackFunc func, gpointer user_data) { const GType gtype = G_TYPE_FROM_INSTANCE(instance); GType* interfaces; @@ -51,25 +53,35 @@ interfaces = g_type_interfaces(gtype, &n_interfaces); { guint i; - for (i = 0; i < n_interfaces; i++) - func(instance, GTYPE2CINFO(interfaces[i]), user_data); + for (i = 0; i < n_interfaces; i++) { + const RGObjClassInfo *info; + + info = GTYPE2CINFO_NO_CREATE(interfaces[i]); + if (info) + func(instance, info, user_data); + } } { - GType i; - for (i = gtype; i != G_TYPE_INVALID; i = g_type_parent(i)) - func(instance, GTYPE2CINFO(i), user_data); + GType type; + for (type = gtype; type != G_TYPE_INVALID; type = g_type_parent(type)) { + const RGObjClassInfo *info; + + info = GTYPE2CINFO_NO_CREATE(type); + if (info) + func(instance, info, user_data); + } } } static void -call_cinfo_free(gpointer instance, const RGObjClassInfo* cinfo, gpointer user_data) +call_cinfo_free(gpointer instance, const RGObjClassInfo *cinfo, gpointer user_data) { if (cinfo->free) cinfo->free(instance); } static void -call_cinfo_mark(gpointer instance, const RGObjClassInfo* cinfo, gpointer user_data) +call_cinfo_mark(gpointer instance, const RGObjClassInfo *cinfo, gpointer user_data) { if (cinfo->mark) cinfo->mark(instance); }
--- a/glib/ChangeLog +++ b/glib/ChangeLog @@ -1,3 +1,9 @@ +2008-07-27 Sjoerd Simons <[EMAIL PROTECTED]> + + * src/rbgobj_closure.c: Only use G_REMOVE_RELATIVE when a closure is + directly invalidated. This prevents object creation when freeing the + closure from the garbage collector. + 2008-06-19 Kouhei Sutou <[EMAIL PROTECTED]> * src/rbgobj_type.c (rbgobj_lookup_class_by_gtype_body): add --- a/glib/src/rbgobj_closure.c +++ b/glib/src/rbgobj_closure.c @@ -142,9 +142,6 @@ GList *next; for (next = rclosure->objects; next; next = next->next) { GObject *object = G_OBJECT(next->data); - VALUE obj = rbgobj_ruby_object_from_instance2(object, FALSE); - if (!NIL_P(rclosure->rb_holder) && !NIL_P(obj)) - G_REMOVE_RELATIVE(obj, id_closures, rclosure->rb_holder); g_object_weak_unref(object, rclosure_weak_notify, rclosure); } g_list_free(rclosure->objects); @@ -163,7 +160,16 @@ GRClosure *rclosure = (GRClosure*)closure; if (rclosure->count > 0) { + GList *next; + rclosure->count = 1; + for (next = rclosure->objects; next; next = next->next) { + GObject *object = G_OBJECT(next->data); + VALUE obj = rbgobj_ruby_object_from_instance2(object, FALSE); + if (!NIL_P(rclosure->rb_holder) && !NIL_P(obj)) + G_REMOVE_RELATIVE(obj, id_closures, rclosure->rb_holder); + } + rclosure_unref(rclosure); } } @@ -178,7 +184,13 @@ static void gr_closure_holder_free(GRClosure *rclosure) { - rclosure_invalidate(NULL, (GClosure*)rclosure); + if (rclosure->count > 0) { + rclosure->count = 1; + + /* No need to remove us from the relatives hash of our objects, as + * those aren't alive anymore anyway */ + rclosure_unref(rclosure); + } } GClosure*
#!/usr/bin/ruby # This program displays a pixel-based animation in a window # FIXME: Update the picture 20 times per second require 'gtk2' class BasicWindow < Gtk::Window def initialize(title = nil) super(Gtk::Window::TOPLEVEL) if title set_title("#{title} in Ruby/GTK") end signal_connect("key_press_event") do |widget, event| if event.state.control_mask? and event.keyval == Gdk::Keyval::GDK_q destroy true else false end end signal_connect("delete_event") do |widget, event| quit end end def quit destroy true end end class Image < BasicWindow # Create a string of 100x100x4 characters def pixels result = "" @offset = 0 unless defined? @offset 100.times do |y| 100.times do |x| # Red component result << (((x + @offset) * 255) / (100 + @offset)) # Green component result << (((y + @offset) * 255) / (100 + @offset)) # Blue component result << 128 # Padding result << 0 end end @offset += 1 return result end def initialize super('Image') signal_connect('destroy') do Gtk.main_quit end self.border_width = 8 vbox = Gtk::VBox.new(false, 8) vbox.border_width = 8 add(vbox) label = Gtk::Label.new label.set_markup('<u>Rendered image</u>') vbox.pack_start(label, false, false, 0) frame = Gtk::Frame.new frame.shadow_type = Gtk::SHADOW_IN # The alignment keeps the frame from growing when users resize # the window align = Gtk::Alignment.new(0.5, 0.5, 0, 0) align.add(frame) vbox.pack_start(align, false, false, 0) # Realize the window to be able to properly do graphics operations realize image = Gtk::DrawingArea.new image.set_size_request(100, 100) frame.add(image) image.realize Gtk.timeout_add(50) do gc = image.style.fg_gc(image.state) Gdk::RGB.draw_rgb_32_image(image.window, gc, 0, 0, 100, 100, Gdk::RGB::DITHER_NONE, pixels, 100*4) true end end end # Do the Disco Duck image = Image.new image.show_all Gtk.main