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

Reply via email to