In my quest to make my code completely correct and beautiful, largely through the liberal application of obscure and anal-retentive attributes like
__malloc__, I've hit a problem. Is it safe and correct to use
__pure__ with GObject convenience getter functions?
As far as the literature explains it, “pure” C functions (no relation to pure mathematical or functional functions) can access pointers and global memory, but must not write to them, and cannot use system resources. In return, gcc can apply extra optimisations to calls to pure functions, since it can assume the return value of a pure function (for a given set of arguments) will not change, no matter how many times the function is called, until memory is written or a non-pure function is called. This allows for elimination of redundant calls to pure functions (since they're known to not have any side-effects) or, conversely, for gcc to add in calls to a pure function in preference to storing the result in memory, if it thinks that will perform better. Furthermore, gcc can perform loop optimisations on loops which just use pure functions.
This is all good, and I think I've done a good job of regurgitating the gcc manual here, but it doesn't answer the question. There are loads of GObject-based libraries out there, and none of them (as far as I can tell) are using pure convenience getters. I must be missing something from the wisdom of the masses.
The only situation I can think of where using the
__pure__ attribute on a convenience getter function could result in incorrect code is if the object in question is modified from a thread (2) other than the one (1) calling the getter. For example, thread 1's
frobnicate() function calls
my_object_get_property_foo() a couple of times in a function at the same time as thread 2 calls
my_object_set_property_foo(). Normally, let's say, the first call to
my_object_get_property_foo() would return the old value, and the second call would return the new value. If
my_object_get_property_foo() was a pure function, though, the compiler could potentially optimise out the second call, and so thread 1 would never see the new value of the “foo” property.
There are fairly few places where this is the desired behaviour with or without use of
__pure__, though. The
frobnicate() function is likely to have locking in this situation, which would prevent the race condition as normal, while still allowing the compiler to optimise out some of the calls to the pure
In summary, what I'm proposing is that people use the
G_GNUC_PURE attribute on their GObject convenience getter functions as much as appropriate. From my tests adding the attribute to functions in libgdata, it doesn't make much of a difference in performance but does reduce the code size of applications which use libgdata. (These tests weren't particularly thorough, though; they were actually just done against one function, looking at the disassembly of a test program which called it.)
Just as an example, here's a pure convenience getter function I wrote earlier:
GList *gdata_entry_get_categories (GDataEntry *self) G_GNUC_PURE;
gdata_entry_get_categories (GDataEntry *self)
g_return_val_if_fail (GDATA_IS_ENTRY (self), NULL);
On another note, that return type should really be
const GList*, but GLib's list functions aren't const-correct.