From: John O. <joh...@gm...> - 2010-09-07 14:40:15
|
Hi everyone ! Sorry for the double post between haskell-beginners and this mailing-list but I am having trouble binding some part of Glib and Xfconf to Haskell. Below is my original post: =================================================================== To exercise my Haskell skills, I'm currently trying to bind libxfconf-0 with Haskell. A small preview can be found on patch-tag[0]. [0]: https://patch-tag.com/r/obbele/xfconf/home Since XFCE projects rely heavily on glib, I am borrowing (lots of) code from the gtk2hs package(s). I am still having issues with gobject casts and foreign pointer finalizers, but lets focus on GHashTable for now. For those who are not familiar with glib (neither I am), GHashTable[1] are hashes with homogenous key types but values of arbitrary types. In xfconf, keys are all cstring but values can be any type of gpointers (gchar *, gint*, gdouble*, widget*, struct* ?). [1]: http://library.gnome.org/devel/glib/stable/glib-Hash-Tables.html I've tried to wrap the glib hash table in a haskell data type this way: > data GHashTable k v = GHashTable GHashTablePtr The GHashTablePtr is our Haskell foreign pointer, courtesy of C→HS. It is bound later with a g_hash_table_destroy finalizer in order to (automatically) manage memory . My real problems are: - I cannot simply map (GHashTable k v) to a (Data.Map.Map k v) - I don't know how to constraint k and v to be of type (Ptr a) (Ptr CChar is ok but not Char or CChar). I remebered from RWH that type constraints on data type declarations are evil, so should I just let things be this way ? Does anyone have a solution or some pointers on it ? =================================================================== Thanks for your efforst on glib/gtk2hs and thanks for your time, /John |
From: Axel S. <Axe...@in...> - 2010-09-08 20:10:43
|
Hi John, On Sep 7, 2010, at 16:40, John Obbele wrote: > Hi everyone ! > > Sorry for the double post between haskell-beginners and this > mailing-list but I am having trouble binding some part of Glib > and Xfconf to Haskell. > > Below is my original post: > =================================================================== > To exercise my Haskell skills, I'm currently trying to bind > libxfconf-0 with Haskell. A small preview can be found on > patch-tag[0]. > > [0]: https://patch-tag.com/r/obbele/xfconf/home > > Since XFCE projects rely heavily on glib, I am borrowing (lots > of) code from the gtk2hs package(s). I am still having issues > with gobject casts and foreign pointer finalizers, but lets focus > on GHashTable for now. > > For those who are not familiar with glib (neither I am), > GHashTable[1] are hashes with homogenous key types but values of > arbitrary types. In xfconf, keys are all cstring but values can > be any type of gpointers (gchar *, gint*, gdouble*, widget*, > struct* ?). > > [1]: http://library.gnome.org/devel/glib/stable/glib-Hash-Tables.html > > I've tried to wrap the glib hash table in a haskell data type > this way: > >> data GHashTable k v = GHashTable GHashTablePtr > > The GHashTablePtr is our Haskell foreign pointer, courtesy > of C→HS. It is bound later with a g_hash_table_destroy finalizer > in order to (automatically) manage memory . > > My real problems are: > - I cannot simply map (GHashTable k v) to a (Data.Map.Map k v) > I don't know what you mean with "map". If you want to bind GHashTable then you providing an interface to a set of C functions that you can use in the IO monad to use the GHashTable. Maybe that's not that useful since Haskell provides it's own hashtables. Usually I would only bind what you need from GLib and expose the really useful functions of the library that builds on glib. > - I don't know how to constraint k and v to be of type (Ptr a) (Ptr > CChar is ok but not Char or CChar). I remebered from RWH that > type constraints on data type declarations are evil, so > should I just let things be this way ? > Usually the phantom type variables are enough. If you have a lookup function that compares elements by a comparison function, you can provide an argument for an explicit comparison function, or add (Eq a => ..) to just the lookup function and then use (==) as the equality test. Cheers, Axel > Does anyone have a solution or some pointers on it ? > =================================================================== > > Thanks for your efforst on glib/gtk2hs and > thanks for your time, > /John > ------------------------------------------------------------------------------ > This SF.net Dev2Dev email is sponsored by: > > Show off your parallel programming skills. > Enter the Intel(R) Threading Challenge 2010. > http://p.sf.net/sfu/intel-thread-sfd_______________________________________________ > Gtk2hs-devel mailing list > Gtk...@li... > https://lists.sourceforge.net/lists/listinfo/gtk2hs-devel |
From: John O. <joh...@gm...> - 2010-09-13 00:41:06
|
Thanks for your answers Alex :) ... and sorry for the delay, I was caught in some confusions in my project about various C2HS stuff (such as how to handle newtype (foreign) variables across multiple modules). But please be patient with me since I still have one question ! First to resume briefly the previous thread: > >My real problems are: > >- I cannot simply map (GHashTable k v) to a (Data.Map.Map k v) > > > > I don't know what you mean with "map". If you want to bind > GHashTable then you providing an interface to a set of C functions > that you can use in the IO monad to use the GHashTable. Maybe that's > not that useful since Haskell provides it's own hashtables. Usually > I would only bind what you need from GLib and expose the really > useful functions of the library that builds on glib. By "map", I was hoping to get an easy solution as with GList where you can import to / export from haskell the entire set of values without bothering you constantly keeping a pointer to GList*. Anyway, forget about this, I have kept things simple and only coded a minimal subset of GHashTable, re-reading xfconf documentation, it appears we only get (gchar*) keys and (gvalue*) values. Then, concerning the question. I would like to cast XfconfChannel to a GObject, play with its attribute and connect to it some signals. If I've correctly understood the recent cabalization of gtk2hs and its addons, and based on gconf-0.11, I should hook Setup.hs to Gtk2HsSetup.hs, create two files, "marshal.list" and "hierarchy.list" and modify the .cabal configuration accordingly, am I right ? What if my reference to the XfconfChannel gobject is a "weak" reference ? Is there some special things to do, since I do not require a g_object_unref finalizer. regards, John |
From: Axel S. <Axe...@in...> - 2010-10-06 06:25:37
|
Hi John, On 05.10.2010, at 22:07, John Obbele wrote: > 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". > Thanks for your observations and musings. We did have a report on an apparent memory leak in Pixbuf, which is indeed not a Widget. I assumed that the floating concept is applied to any GObject. Since I seem to be wrong about this, we have to revise quite a few things in Gtk2Hs. So we need a new function that does not increase the refcount. wrapGObject perhaps? > 2) I will hang myself if I have to test anymore ref_counts in gtk2hs. > Too bad. I would have set you up scurrying the land of Pixbufs and their references. Alas, I have to do that myself. > please beat me ,) Unless you beat me to it. Axel > /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 > Gtk...@li... > https://lists.sourceforge.net/lists/listinfo/gtk2hs-devel |
From: John O. <joh...@gm...> - 2010-10-07 00:15:59
|
On Wed, Oct 06, 2010 at 08:25:23AM +0200, Axel Simon wrote: > On 05.10.2010, at 22:07, John Obbele wrote: > >1) constructNewGObject/makeNewGObject cannot be used in gtk2hs for new > > *non-floating* GObjects, that is for every object not inheriting > > from "GInitiallyUnowned". > > > > Thanks for your observations and musings. We did have a report on an > apparent memory leak in Pixbuf, which is indeed not a Widget. > > I assumed that the floating concept is applied to any GObject. Since > I seem to be wrong about this, we have to revise quite a few things > in Gtk2Hs. So we need a new function that does not increase the > refcount. wrapGObject perhaps? I will look at it (#1202 ?) in the next few days and see if there is a link between the xfconf and pixbuf issue. regards, John, who should recheck out the darcs repository |
From: Axel S. <Axe...@in...> - 2010-09-22 06:36:30
|
Hi John, same here: sorry for the delay. On Sep 13, 2010, at 2:40, John Obbele wrote: > Anyway, forget about this, I have kept things simple and only > coded a minimal subset of GHashTable, re-reading xfconf > documentation, it appears we only get (gchar*) keys and (gvalue*) > values. > > Yes, that sounds better. If at all possible, you should not expose the user to GValues since these are too easy to get wrong (memory management, etc.). Please check what Duncan has done in gconf. I think he came up with a simple type class construction. > Then, concerning the question. I would like to cast > XfconfChannel to a GObject, play with its attribute and connect > to it some signals. If I've correctly understood the recent > cabalization of gtk2hs and its addons, and based on gconf-0.11, I > should hook Setup.hs to Gtk2HsSetup.hs, create two files, > "marshal.list" and "hierarchy.list" and modify the .cabal > configuration accordingly, am I right ? > Yes. You need marshal.list if you have callbacks, otherwise you don't have to bother. You probably need hierarchy.list since that is required if you want the tools to create new class definitions. > What if my reference to the XfconfChannel gobject is a "weak" > reference ? Is there some special things to do, since I do not > require a g_object_unref finalizer. At the moment, a Haskell handle to an object is a strong reference. If it goes out of scope, then the reference counter of that object is decremented. This scheme is a bit problematic since you can create space leaks by creating closures that include these object references and then attach these closures to callbacks from the same object. We have not yet figured out a way to circumvent this problem. The only way to get rid of an object is to explicitly destroy it. Hope this helps, Axel |
From: John O. <joh...@gm...> - 2010-09-22 23:26:33
Attachments:
gvalue_unset.patch
|
On Wed, Sep 22, 2010 at 08:36:20AM +0200, Axel Simon wrote: > Hi John, > > same here: sorry for the delay. no worries. I am not in a hurry and I can understand my questions seem sometimes awkward. > On Sep 13, 2010, at 2:40, John Obbele wrote: > >Anyway, forget about this, I have kept things simple and only > >coded a minimal subset of GHashTable, re-reading xfconf > >documentation, it appears we only get (gchar*) keys and (gvalue*) > >values. > > > > > > Yes, that sounds better. If at all possible, you should not expose > the user to GValues since these are too easy to get wrong (memory > management, etc.). Please check what Duncan has done in gconf. I > think he came up with a simple type class construction. Oki, thanks for the advice. I saw Duncan typeclass in GConf. I have not re-used it but I've created a similar typeclass (in fact the idea comes also from what I remember from RWH chapters about JSON). On the subject about GValue, I'm getting a special behaviour, don't know if it's can be called a bug and I've not yet test it against recent 0.11 version: So there is a C function with this signature: """ gboolean xfconf_channel_get_property(XfconfChannel *channel, const gchar *property, GValue *value); """ Which translates easily in haskell as: """ channelGetProperty :: XfconfChannelClass self => self -> String -> IO (Maybe XfconfValue) channelGetProperty chan property = withXfconf chan $ \chanPtr -> withUTFString property $ \prop -> allocaGValue $ \gvalue@(GValue gPtr) -> do -- c_get_property == FFI call to xfconf_channel_get_property success <- c_get_property chanPtr prop gPtr case fromGBoolean success of False -> do -- FIXME: bug in glib GValue implementation ? -- gvalue was not initialize by c_get_property -- allocaGValue will try to unset it -- and throw an error if our gvalue stay as it is valueInit gvalue bool return Nothing True -> Just `fmap` toXfconfValue gvalue """ As said in the haskell comment, when xfconf_channel_get_property cannot find the property (the key in the xfconf "hash" table), it returns False and doesn't set anything inside our GValue, which trigger this glib error message: """ (process:20219): GLib-GObject-CRITICAL **: g_value_unset: assertion `G_IS_VALUE (value)' failed """ A simple work-around is to arbitrary valueInit'ed our gvalue to something (bool in the previous code example) before ending our haskell function (and letting allocaGValue clean the memory). Note that if I valueInit' the gvalue before the call to xfconf, I am getting an error saying Glib cannot initialized the gvalue since it already was. (grrr) ... consequently, I propose to patch glib/System/Glib/GValue.chs (or add a message in haddock warning against this edge case): [!pseudo-patch attached!] > >Then, concerning the question. I would like to cast > >XfconfChannel to a GObject, play with its attribute and connect > >to it some signals. If I've correctly understood the recent > >cabalization of gtk2hs and its addons, and based on gconf-0.11, I > >should hook Setup.hs to Gtk2HsSetup.hs, create two files, > >"marshal.list" and "hierarchy.list" and modify the .cabal > >configuration accordingly, am I right ? > > > > Yes. You need marshal.list if you have callbacks, otherwise you > don't have to bother. You probably need hierarchy.list since that is > required if you want the tools to create new class definitions. I have successfully cast my object to the GObjectClass by copy-pasting samples of code I have found. Same for signals, they are working fine. ... except that I'm using plain vanilla c2hs with pre-compiled gtk2hs-0.10 packages :( I don't know if it really worths it now to try hooking my lib with the Gtk2hsSetup.hs. Sorry but until I get a new fedora 14 system (with shiny new gtk2hs-0.11 packages courtesy of J.Petersen), I will stick with my simple hand-coded Signals.chs and Types.chs. > >What if my reference to the XfconfChannel gobject is a "weak" > >reference ? Is there some special things to do, since I do not > >require a g_object_unref finalizer. > > At the moment, a Haskell handle to an object is a strong reference. > If it goes out of scope, then the reference counter of that object > is decremented. This scheme is a bit problematic since you can > create space leaks by creating closures that include these object > references and then attach these closures to callbacks from the same > object. We have not yet figured out a way to circumvent this > problem. The only way to get rid of an object is to explicitly > destroy it. I have tried to solve my weak-reference problem by artificially increasing the object reference with objectRef. If I've correctly read the glib documentation, this should do the trick. So everything could be fine if my quickcheck test suite was not eating my RAM. `top` is saying that the quickcheck binary uses 14Mo for a 20*1000 samples testsuite against 22Mo for a 20*2000 samples testsuite. On the other hand, GHC +RTS -sstderr keeps telling me that garbage collection is ok and that everything is fine. But surely, GHC does not count memory allocated through mallocBytes or {#call g_malloc0 #}, does it ? Perhaps I am just getting parano. +RTS -sstderr actually says, for 20*1000 samples: """ 3,775,334,132 bytes allocated in the heap 40,723,580 bytes copied during GC 166,552 bytes maximum residency (30 sample(s)) 284,920 bytes maximum slop 2 MB total memory in use (0 MB lost due to fragmentation) """ Should I understand that almost 3.78Go of test data where mallocated and conclude that 14Mo of 'RES' RAM is small enough for such a binary? regards, /John |
From: Axel S. <Axe...@in...> - 2010-09-23 07:20:09
|
Hi John, On 23.09.2010, at 01:26, John Obbele wrote: > channelGetProperty :: XfconfChannelClass self > => self -> String -> IO (Maybe XfconfValue) > channelGetProperty chan property = > withXfconf chan $ \chanPtr -> > withUTFString property $ \prop -> > allocaGValue $ \gvalue@(GValue gPtr) -> do > -- c_get_property == FFI call to xfconf_channel_get_property > success <- c_get_property chanPtr prop gPtr > case fromGBoolean success of > False -> do -- FIXME: bug in glib GValue implementation ? > -- gvalue was not initialize by c_get_property > -- allocaGValue will try to unset it > -- and throw an error if our gvalue stay as it is > valueInit gvalue bool > return Nothing > True -> Just `fmap` toXfconfValue gvalue > """ > > As said in the haskell comment, when xfconf_channel_get_property > cannot find the property (the key in the xfconf "hash" table), it > returns False and doesn't set anything inside our GValue, which > trigger this glib error message: > > """ > (process:20219): GLib-GObject-CRITICAL **: g_value_unset: assertion > `G_IS_VALUE (value)' failed > """ > > A simple work-around is to arbitrary valueInit'ed our gvalue to > something (bool in the previous code example) before ending our > haskell function (and letting allocaGValue clean the memory). > Yes, the problem is that allocaGValue will expect the functions that is called to set the GValue because it will tear down the GValue afterwards. > Note that if I valueInit' the gvalue before the call to xfconf, I > am getting an error saying Glib cannot initialized the gvalue > since it already was. (grrr) > > ... consequently, I propose to patch glib/System/Glib/GValue.chs > (or add a message in haddock warning against this edge case): > Why can't you simply initialize the GValue *only* in the error case? We could patch GValue but that means that your library will be dependent on some future version of gtk. > [!pseudo-patch attached!] > >>> Then, concerning the question. I would like to cast >>> XfconfChannel to a GObject, play with its attribute and connect >>> to it some signals. If I've correctly understood the recent >>> cabalization of gtk2hs and its addons, and based on gconf-0.11, I >>> should hook Setup.hs to Gtk2HsSetup.hs, create two files, >>> "marshal.list" and "hierarchy.list" and modify the .cabal >>> configuration accordingly, am I right ? >>> >> >> Yes. You need marshal.list if you have callbacks, otherwise you >> don't have to bother. You probably need hierarchy.list since that is >> required if you want the tools to create new class definitions. > > I have successfully cast my object to the GObjectClass by copy-pasting > samples of code I have found. Same for signals, they are working > fine. ... except that I'm using plain vanilla c2hs with > pre-compiled gtk2hs-0.10 packages :( > This is bad. Signals.chs and Types.chs must be automatically generated. I'm not sure how well the standard c2hs could work, but at least dependencies are broken so that a project with several files will not work. You will run into problems that certain pointers to and from C functions are recognizes as the Haskell types they were declared as, I think. > I don't know if it really worths it now to try hooking my lib with > the Gtk2hsSetup.hs. Sorry but until I get a new fedora 14 system > (with shiny new gtk2hs-0.11 packages courtesy of J.Petersen), I > will stick with my simple hand-coded Signals.chs and Types.chs. > You certainly do not want to build a package on something pre 0.11(.1). >>> What if my reference to the XfconfChannel gobject is a "weak" >>> reference ? Is there some special things to do, since I do not >>> require a g_object_unref finalizer. >> >> At the moment, a Haskell handle to an object is a strong reference. >> If it goes out of scope, then the reference counter of that object >> is decremented. This scheme is a bit problematic since you can >> create space leaks by creating closures that include these object >> references and then attach these closures to callbacks from the same >> object. We have not yet figured out a way to circumvent this >> problem. The only way to get rid of an object is to explicitly >> destroy it. > > I have tried to solve my weak-reference problem by artificially > increasing the object reference with objectRef. If I've correctly > read the glib documentation, this should do the trick. > I don't follow. What problem do you want to solve? Do you have weak references anywhere? Cheers, Axel |
From: John O. <joh...@gm...> - 2010-09-23 16:11:33
|
Hi again ! On Thu, Sep 23, 2010 at 09:19:55AM +0200, Axel Simon wrote: > Hi John, > > > ... consequently, I propose to patch glib/System/Glib/GValue.chs > > (or add a message in haddock warning against this edge case): > > > > Why can't you simply initialize the GValue *only* in the error case? > We could patch GValue but that means that your library will be > dependent on some future version of gtk. This is what I was doing until now. It works well once you've understood the trick and, indeed, there is no real need to patch System.Glib.GValue.chs. Hum ... let's say that, at least, this bunch of mails will help gtk2hs newbies who might encounter this same behavior in the future :) > > I have successfully cast my object to the GObjectClass by copy-pasting > > samples of code I have found. Same for signals, they are working > > fine. ... except that I'm using plain vanilla c2hs with > > pre-compiled gtk2hs-0.10 packages :( > > > > This is bad. Signals.chs and Types.chs must be automatically > generated. I'm not sure how well the standard c2hs could work, but at > least dependencies are broken so that a project with several files > will not work. > > You will run into problems that certain pointers to and from C > functions are recognizes as the Haskell types they were declared as, I > think. I was afraid so :'( /me cabal install glib gtk gconf /me copy and past part of gconf configuration to xfconf /me pull his hair out seeing that gtk2hsC2hs is outdated and can neither handle inline "#include<foobar>" instructions, nor multiple headers files through the cabal 'x-c2hs-header' directive, nor some "do" construct syntaxes (explicit '{', ';', '}' are my friends !) [... 4 hours and one thunderstorm later ... ] /me launch his quickcheck tests with parallel threads and is happy to see that everything run smoothly ... except for System.Glib.UTFString.fromUTF which is not as good as the utf8-string package to process arbitrary strings generated by QuickCheck2. Aye ! Problem solved ! > >>> What if my reference to the XfconfChannel gobject is a "weak" > >>> reference ? Is there some special things to do, since I do not > >>> require a g_object_unref finalizer. > >> > >> At the moment, a Haskell handle to an object is a strong reference. > >> If it goes out of scope, then the reference counter of that object > >> is decremented. This scheme is a bit problematic since you can > >> create space leaks by creating closures that include these object > >> references and then attach these closures to callbacks from the same > >> object. We have not yet figured out a way to circumvent this > >> problem. The only way to get rid of an object is to explicitly > >> destroy it. > > > > I have tried to solve my weak-reference problem by artificially > > increasing the object reference with objectRef. If I've correctly > > read the glib documentation, this should do the trick. > > > > I don't follow. What problem do you want to solve? Do you have weak > references anywhere? > I may have confused "weak-reference" with what the xfconf documentation calls "singleton object". Here is the official C API: http://www.xfce.org/documentation/4.6/api/xfconf/xfconf-xfconf-channel.html#xfconf-channel-get One can read that xfconf_channel_get() returns a XfconfChannel* object which is owned by the daemon and should *NOT* be g_object_unref(), whereas xfconf_channel_new*() return normal GObjects. I have artificially objectRef my objects from channel_get() to use them with gtk2hs. So simple question : is this the "right" solution or should I simply dump channel_get() and only use channel_new() with gtk2hs ? (Yes I'm really a memory-leak maniac, that's why I insist on this pseudo-problem). All these things said, and apart from these last problems, my bindings begin to look really good, so many thanks for your patience and your help. regards, John |
From: Axel S. <Axe...@in...> - 2010-09-23 17:25:51
|
Hi, On Sep 23, 2010, at 18:11, John Obbele wrote: >> >> Why can't you simply initialize the GValue *only* in the error case? >> We could patch GValue but that means that your library will be >> dependent on some future version of gtk. > > This is what I was doing until now. It works well once you've > understood the trick and, indeed, there is no real need to patch > System.Glib.GValue.chs. > > Hum ... let's say that, at least, this bunch of mails will help > gtk2hs newbies who might encounter this same behavior in the > future :) > Well, yes, we could have more functions dealing with GValue, although I hope there are very few places that GValues are necessary (and they are only necessary in bindings). I had a lot of fun with crashes where GValues were not initialized or not cleaned up properly (space leaks). The withGValue function seemed to be the best thing to do. >>> I have successfully cast my object to the GObjectClass by copy- >>> pasting >>> samples of code I have found. Same for signals, they are working >>> fine. ... except that I'm using plain vanilla c2hs with >>> pre-compiled gtk2hs-0.10 packages :( >>> >> >> This is bad. Signals.chs and Types.chs must be automatically >> generated. I'm not sure how well the standard c2hs could work, but at >> least dependencies are broken so that a project with several files >> will not work. >> >> You will run into problems that certain pointers to and from C >> functions are recognizes as the Haskell types they were declared >> as, I >> think. > > I was afraid so :'( > > /me cabal install glib gtk gconf > /me copy and past part of gconf configuration to xfconf > /me pull his hair out seeing that gtk2hsC2hs is outdated and can > neither handle inline "#include<foobar>" instructions, nor > multiple headers files through the cabal 'x-c2hs-header' > directive, nor some "do" construct syntaxes (explicit '{', > ';', '}' are my friends !) > You can only pass one header file to gtk2hsC2hs since it will compile it into a serialized form. We use the trick of defining a local header file if we need more than one header file or if they don't always define the right macros. Note that including a header file into the .chs file has no effect on the compilation of the final modules. C header file directives are nowadays ignored by ghc. > [... 4 hours and one thunderstorm later ... ] > > /me launch his quickcheck tests with parallel threads and is > happy to see that everything run smoothly ... except for > System.Glib.UTFString.fromUTF which is not as good as the > utf8-string package to process arbitrary strings generated by > QuickCheck2. > True, fromUTF bails out badly. What kind of behavior does utf8-string have that we don't? I don't really want to add yet another package to the dependency list of gtk, but I would be happy to add some sort of better error handling or recovery. >>> I have tried to solve my weak-reference problem by artificially >>> increasing the object reference with objectRef. If I've correctly >>> read the glib documentation, this should do the trick. >>> >> >> I don't follow. What problem do you want to solve? Do you have weak >> references anywhere? >> > > I may have confused "weak-reference" with what the xfconf > documentation calls "singleton object". Here is the official C > API: > > http://www.xfce.org/documentation/4.6/api/xfconf/xfconf-xfconf-channel.html#xfconf-channel-get > > One can read that xfconf_channel_get() returns a XfconfChannel* > object which is owned by the daemon and should *NOT* be > g_object_unref(), whereas xfconf_channel_new*() return normal > GObjects. > > I have artificially objectRef my objects from channel_get() to > use them with gtk2hs. > > So simple question : is this the "right" solution or should I > simply dump channel_get() and only use channel_new() with gtk2hs > ? > > (Yes I'm really a memory-leak maniac, that's why I insist on this > pseudo-problem). > Avoiding leaks is important. They are harder to find than crashes :-) Have you checked the constructGObject function? It might be the correct way to handle channel_get if this function creates a floating reference only. The docs of channel_get are very terse, it might help to look at the source code. > All these things said, and apart from these last problems, my > bindings begin to look really good, so many thanks for your > patience and your help. Great! I'm glad that people can now produce packages on top of Gtk2Hs without us being the bottleneck. Cheers, Axel |
From: John O. <joh...@gm...> - 2010-09-24 15:06:22
|
Hi again ! On Thu, Sep 23, 2010 at 07:25:42PM +0200, Axel Simon wrote: > >/me launch his quickcheck tests with parallel threads and is > > happy to see that everything run smoothly ... except for > > System.Glib.UTFString.fromUTF which is not as good as the > > utf8-string package to process arbitrary strings generated by > > QuickCheck2. > > > > True, fromUTF bails out badly. What kind of behavior does > utf8-string have that we don't? I don't really want to add yet > another package to the dependency list of gtk, but I would be happy > to add some sort of better error handling or recovery. I've just looked on this problem and it is probably not due to esoteric unicode numbers. Glib readUTFStringArray0 failed on arrays of string containing empty strings, a type of data QuickCheck seems particularly fond of. So when using readUTFStringArry0 on, say, ["one", "", "three"], fromUTF8 throws an exception. When I copy-pasted the module UTFString to implement my own, I modified glib/System/Glib/UTFString.hs this way: """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" --- a/glib/System/Glib/UTFString.hs 2010-09-06 2010-09-06 11:24:42.980482243 +0200 +++ b/glib/System/Glib/UTFString.hs 2010-09-24 16:10:21.821860119 +0200 @@ -149,8 +149,9 @@ readUTFStringArray0 :: Ptr CString -> IO [String] readUTFStringArray0 cStrArr = do cStrs <- peekArray0 nullPtr cStrArr + strings <- mapM peekUTFString cStrs g_strfreev cStrArr - mapM peekUTFString cStrs + return strings foreign import ccall unsafe "g_strfreev" g_strfreev :: Ptr a -> IO () """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" It's seems wiser to read the array (mapM peekUTFString) *before* freeing it, laziness considerations apart. Rebuilding glib-0.11.2 with this modification, my quickcheck property on string_list run fine with 10,000 samples, which should be enough to exclude fromUTF8 unicode conversion ability from the list of suspects. I can unfortunately not find how to reproduce the original bad behaviour with raw gtk2hs libraries, since few use readUTFStringArray0 and even if it's the case, tweaking the source string list to artificially insert empty string and trigger the exception is beyond my abilities. (... but it happened in my project huhu :) As for gobject count references, construction and memory consumption, it's another story for another day. regards, /John |
From: Axel S. <Axe...@in...> - 2010-09-24 15:43:48
|
On 24.09.2010, at 17:05, John Obbele wrote: > """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" > --- a/glib/System/Glib/UTFString.hs 2010-09-06 2010-09-06 > 11:24:42.980482243 +0200 > +++ b/glib/System/Glib/UTFString.hs 2010-09-24 16:10:21.821860119 > +0200 > @@ -149,8 +149,9 @@ > readUTFStringArray0 :: Ptr CString -> IO [String] > readUTFStringArray0 cStrArr = do > cStrs <- peekArray0 nullPtr cStrArr > + strings <- mapM peekUTFString cStrs > g_strfreev cStrArr > - mapM peekUTFString cStrs > + return strings > > foreign import ccall unsafe "g_strfreev" > g_strfreev :: Ptr a -> IO () > """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" > > It's seems wiser to read the array (mapM peekUTFString) *before* > freeing it, laziness considerations apart. Ups. Fixed. Now in darcs. Axel |
From: John O. <joh...@gm...> - 2010-10-03 20:42:34
Attachments:
glib.gdb
|
Hi again ! To conclude my long list of posts, here comes what I've understood of GObjects construction and memory management: > > [...] from the official C API documentation: > >http://www.xfce.org/documentation/4.6/api/xfconf/xfconf-xfconf-channel.html#xfconf-channel-get > > > >One can read that xfconf_channel_get() returns a XfconfChannel* > >object which is owned by the daemon and should *NOT* be > >g_object_unref(), whereas xfconf_channel_new*() return normal > >GObjects. > > > [...] > > Have you checked the constructGObject function? It might be the > correct way to handle channel_get if this function creates a > floating reference only. The docs of channel_get are very terse, it > might help to look at the source code. First, I must say that I have successfully used GDB to monitor and debug my GOBjects. I am using conditional breakpoints and a file containing the GDB commands to automate the task. For curious people, the file is attached to this message. It's an old trick well known on the glib/gtk+2 development mailing lists and wikis and I'm very happy to have learned these gdb (basic?) features. Surprisingly, gdb somehow works on Haskell FFI binaries. Well, only partially on their pseudo C stack, but it was more than enough in my case :) So my solution for the XfconfChannel memory management is: 1) xfconf_channel_get returns a singleton object owned by the xfconf daemon. You do not have to object_ref or unref it. Yet gtk2hs forces you to bind a "unref" finalizer to it. One practical solution is to use constructNewGObject, which calls objectRefSink and artificially increments the ref_count. When Haskell clean its memory, it finalizes the object with g_object_unref. The ref_count decreases and goes back to its normal value. It just works fine™, although I still do not understand how g_object_object_ref_sink performs internally ^^ 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). 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've quickly tested the other parts of gtk2hs I am using (mainly GValues) and all seems fine memory wise, so once again, many thanks for your hard work and your help ! regards, /John, who have one more newbie haskell question for the cafe or or beginners MLs … |
From: Axel S. <Axe...@in...> - 2010-10-04 07:54:07
|
Hi John, On Oct 3, 2010, at 22:42, John Obbele wrote: > Hi again ! > > To conclude my long list of posts, here comes what I've > understood of GObjects construction and memory management: > >>> [...] from the official C API documentation: >>> http://www.xfce.org/documentation/4.6/api/xfconf/xfconf-xfconf-channel.html#xfconf-channel-get >>> >>> One can read that xfconf_channel_get() returns a XfconfChannel* >>> object which is owned by the daemon and should *NOT* be >>> g_object_unref(), whereas xfconf_channel_new*() return normal >>> GObjects. >>> >> [...] >> >> Have you checked the constructGObject function? It might be the >> correct way to handle channel_get if this function creates a >> floating reference only. The docs of channel_get are very terse, it >> might help to look at the source code. > > > First, I must say that I have successfully used GDB to monitor > and debug my GOBjects. I am using conditional breakpoints and a > file containing the GDB commands to automate the task. For > curious people, the file is attached to this message. It's an old > trick well known on the glib/gtk+2 development mailing lists and > wikis and I'm very happy to have learned these gdb (basic?) > features. > > > Surprisingly, gdb somehow works on Haskell FFI binaries. Well, > only partially on their pseudo C stack, but it was more than > enough in my case :) > > > So my solution for the XfconfChannel memory management is: > > 1) xfconf_channel_get returns a singleton object owned by the xfconf > daemon. You do not have to object_ref or unref it. Yet gtk2hs > forces you to bind a "unref" finalizer to it. > > One practical solution is to use constructNewGObject, which > calls objectRefSink and artificially increments the ref_count. > When Haskell clean its memory, it finalizes the object with > g_object_unref. The ref_count decreases and goes back to its > normal value. > > It just works fine™, although I still do not understand how > g_object_object_ref_sink performs internally ^^ > > 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. > 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've quickly tested the other parts of gtk2hs I am using (mainly > GValues) and all seems fine memory wise, so once again, many > thanks for your hard work and your help ! > 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). Cheers, Axel > regards, > /John, who have one more newbie haskell question for the cafe or > or beginners MLs … > < > glib > .gdb > > > ------------------------------------------------------------------------------ > Virtualization is moving to the mainstream and overtaking non- > virtualized > environment for deploying applications. Does it make network security > easier or more difficult to achieve? Read this whitepaper to > separate the > two and get a better understanding. > http://p.sf.net/sfu/hp-phase2-d2d_______________________________________________ > Gtk2hs-devel mailing list > Gtk...@li... > https://lists.sourceforge.net/lists/listinfo/gtk2hs-devel |
From: John O. <joh...@gm...> - 2010-10-05 20:07:25
|
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 |