Good evening !

On Mon, Oct 04, 2010 at 09:53:56AM +0200, Axel Simon wrote:
> 
> On Oct 3, 2010, at 22:42, John Obbele wrote:
> >  Optimally, I would wish to provide a dummy memory finalizer to
> >  the gtk2hs GObject implementation but it seems this can't be
> >  easily done (for good reasons I assume).
> >
> 
> The good reason is that XfconfChannel may cease to exist before the
> Haskell object goes out of scope (well, at least in principle this
> is a possibility). If you don't use constructNewGObject, then
> Haskell land may refer to a destroyed C structure.

That's sound right to me.

Note also that in the previous case, construct/makeNewGObject
works equally well, my bad for not understanding this before but
XfconfChannels are NOT floating objects.

> >2) xfconf_channel_new returns a newly created clone. You need to
> >  call object_unref to free it but neither object_ref nor
> >  object_ref_sink. Problem: makeNewGObject calls the former and
> >  constructNewGObject the latter.
> >
> >  Simple solution : call directly the C functions like this:
> >
> >       channelNew :: String -> IO XfconfChannel
> >       channelNew name =
> >           withUTFString name $ \cname -> do
> >               objPtr <- {#call xfconf_channel_new #} cname
> >               obj <- newForeignPtr objectUnref objPtr
> >               return $! XfconfChannel obj
> >
> >
> I consider this behaviour as broken. I think object_ref_sink should
> do the right job for exactly this problem. If it doesn't then our
> other code might be broken (or XFconf is simply not adapted to the
> new sink/ref mechanism, although I thought it should be backwards
> compatible).

I don't think this is broken. I'm not a glib expert/developer so I
might be wrong but after re-reading the brief GObject stuff about
floating references [0], I understand the situation this way:

[0]: 
http://library.gnome.org/devel/gobject/stable/gobject-The-Base-Object-Type.html#gobject-The-Base-Object-Type.description

/me puts its "cap'tain obvious" hat, sorry if I repeat things everybody
    already knows

0) Floating references are just syntactic sugar for lazy C programmers.
   The idea is simple: when dealing with widgets, 99% of the time, you
   add them to containers and they belong to the *container*, not your
   `create_ui` function. So in order to avoid thousands of
   `gtk_container_add (box,widget) && g_object_unref (widget)`, glib
   developers have created floating references:

    GtkWidget* create_ui(void) {
        GtkWidget *box = gtk_hbox_new( ...);      // box->ref_count = 1
        GtkWidget *button = gtk_button_new( ...); // button->ref_count = 1

        /* At this point, both box and button are "owned" by createUI */

        gtk_container_add (box, button);          // button->ref_count *STILL* 
= 1
        /* g_object_unref (button); */            /* unneeded */

        /* Now, (*button).ref_count, originally owned by "createUI",
         * was overwritten (sunk ?) by `gtk_container_add`.
         * 
         * If you delete (*box), it will break its reference to (*button)
         * and thus destroy it. Hence (*button) effectively "belongs to"
         * (*box).
         */

        return GTK_WIDGET (box);
    }

1) XfconfChannel are not widgets. You do not store them in boxes, You do
   not "transfer their ownership to the first container passing by".
   Consequently they do not need floating references and the ref_sink
   mechanism.

   A simple Haskell function should looks like this:

    readMyConfiguration :: IO Configuration
    readMyConfiguration = do
        chan <- channelNew "Foobar"

        option1 <- channelGetProperty chan "foo1"
        option2 <- channelGetProperty chan "foo2"

        return (Configuration option1 option2)
        
        -- Note that the last `g_object_unref` is omitted in Haskell
        -- because this is why the GC is here for. "chan" is
        -- automagically clean thanks to its finalizer.

   If I had defined "channelNew = makeNewGObject (...) {#call channel_new#}",
   `{#channel_new#}` would return a non-floating object with ref_count = 1,
   makeNewGObject would increase the reference to 2 and finally the
   Haskell GC would finalize my channel and decrease its ref_count back
   to 1.

   If I had defined "channelNew = constructNewGObject (...) {#call 
channel_new#}",
   this would be exactly the same problem, since `constructNewGObject`
   would call `g_object_ref_sink` which equals `g_object_ref` for non-floating
   objects.

   In both case, at the end of "readMyConfiguration", chan->ref_count == 1.
   The object is not destroyed by Glib. I have a memory leak.

   /me puts its "engineer" hat

   And I am fairly confident about this fact. I've used

    testLoop :: IO ()
    testLoop = replicateM_ 100000 (channelNew "foo" >>= channelOperation >>= 
print)

   … and experimental results show that the only way not to get a memory
   leak, is to by-pass both constructNewGObject and makeNewGObject.

/me puts its "naïve computer scientist" hat.

2) Note that all this discussion is only relevant to non-floating
   objects.

   I've rewrite the first C example and loop over creating widgets /
   unreferencing them, just to be sure, and the gtk2hs program shows no
   memory leaks.


Conclusion:

1) constructNewGObject/makeNewGObject cannot be used in gtk2hs for new
   *non-floating* GObjects, that is for every object not inheriting
   from "GInitiallyUnowned".

2) I will hang myself if I have to test anymore ref_counts in gtk2hs.

please beat me ,)
/John

------------------------------------------------------------------------------
Beautiful is writing same markup. Internet Explorer 9 supports
standards for HTML5, CSS3, SVG 1.1,  ECMAScript5, and DOM L2 & L3.
Spend less time writing and  rewriting code and more time creating great
experiences on the web. Be a part of the beta today.
http://p.sf.net/sfu/beautyoftheweb
_______________________________________________
Gtk2hs-devel mailing list
Gtk2hs-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/gtk2hs-devel

Reply via email to