Your _get_type() function is not G_GNUC_CONST

It’s not uncommon in GNOME to annotate the _get_type() function declaration of a GObject with G_GNUC_CONST. Like so:

GType ephy_download_get_type (void) G_GNUC_CONST;

What does this do? It expands to __attribute__((__const__)) if the compiler is GCC (or a compiler that pretends to be GCC, like Clang); otherwise, it expands to nothing. What does that attribute do? I could point you at the GCC documentation, but GLib’s documentation is simpler: “Declaring a function as const enables better optimization of calls to the function. A const function doesn’t examine any values except its parameters, and has no effects except its return value.” That’s really all there is to it. What’s important to keep in mind is that if your function doesn’t meet the preconditions for the attribute, the compiler is free to make optimizations that break your code.

Since G_DEFINE_TYPE defines our _get_type() functions for us, it can be easy to forget what’s actually in there. Here’s the canonical example, from the GObject documentation:

GType maman_bar_get_type (void)
{
  static GType type = 0;
  if (type == 0) {
    const GTypeInfo info = {
      /* You fill this structure. */
    };
    type = g_type_register_static (G_TYPE_OBJECT,
                                   "MamanBarType",
                                   &info, 0);
  }
  return type;
}

The first thing you should notice is that it examines a value (type) that’s not a parameter. Next, you should notice that it has an effect other than its return value: it modifies type, and then registers with the type system. Obviously G_GNUC_CONST is not appropriate here. Fix your headers. Update: If you scroll down to the first comment below, Giovanni recommends using G_GNUC_CONST anyway and also g_type_ensure as a workaround for if you don’t use the return value of the function.

Note that the new, highly-recommendable G_DECLARE_FINAL_TYPE and G_DECLARE_DERIVABLE_TYPE macros declare this function for you, so future code should be immune to this problem. Update: Those macros do not use G_GNUC_CONST, but maybe they will in the future? Who can say!

P.S. I’m not the one who noticed this — it was brought up by somebody (Christian?) at the Boston Summit last year — but I don’t think anybody has blogged about it yet. Update: It was pointed out in the comments that this was noticed long ago. Here’s a GLib bug report about breakage in Glade, and my colleague Andy Wingo has a blog post about a GStreamer bug this caused.

8 Replies to “Your _get_type() function is not G_GNUC_CONST”

  1. __attribute__((const)) means:
    1) The function always returns the same value with the same arguments (same as __attribute__((pure)))
    2) The function has no side effects (kind of like constexpr in C++)

    This boils down to
    1) The compiler is allowed to CSE ((const)) function calls
    2) The compiler is allowed to dead-code eliminate ((const)) function calls if you don’t use the result

    1) is correct for _get_type(): you can call them a hundred times, you can call them in arbitrary order, the GObject type system enforces the results are the same
    (unless you’re doing something funky, like having two libraries that register the same type, which is wrong)
    2) is not correct for _get_type(), because the function has a side effect, it registers the type.
    But! In the majority of code, you do use the result of a _get_type() (either you pass it down to g_object_new() or g_type_is_a() or you compare it to the g_type of the object in the cast macros fast path), which means the compiler will not completely eliminate the call (but it will happily reuse a register for it if it can).
    The only case this breaks is where you call _get_type() for the side effects, which is why g_type_ensure() exists. g_type_ensure() does nothing, but the compiler does not know (it’s an external function) and it must assume it uses the value, so somehow, somewhere, it must call the _get_type() before calling g_type_ensure() – which gives you the side effect you need (plus the atomics in the _get_type() give you a happens-before, if you need that)

    Summing up, as long as you use the result of the _get_type(), or you call g_type_ensure() for the side effects, the compiler will not break your code when a _get_type() is ((const)), and there is a noticeable performance benefit from CSE-ing it (it’s an expensive external call + atomic read, that’s called often multiple times in a function because you have casts and precondition checks), which means that yes, _get_type() SHOULD be annotated with G_GNUC_CONST.

  2. I thought this usage of G_GNUC_CONST was (while technically not correct), absolutely fine because the function is essentially one-shot, and after that it returns a const answer (the GType which doesn’t change once registered). So the compiler is free to memoise and elide all but the first call to the *_get_type() function, but won’t elide the first call because it needs the initial result.

    Or have I misunderstood?

    1. The issue, as Giovanni explained, is when you call a _get_type *without* using the GType that it returned. eg., when you want to force the GTypes corresponding to the built-in implementations of a GIOExtensionPoint to be created, or when using GTK+ widget templates.

  3. After the first call of this method none of this side effects will ever happen right?

    So in fact the compiler should be able to optimize this correctly? Or am I underestimating what compilers can/will do with it?

  4. Interestingly, the gcc documentation’s definition of the “pure” attribute [1] seems like it could fit, since it requires that the function be immune to common subexpression elimination.

    That said, it’s not actually a pure function because of the side effects, but i wonder whether gcc requires this (I’d err on the side of caution and not use this, anyway).

    [1] https://gcc.gnu.org/onlinedocs/gcc-5.2.0/gcc/Function-Attributes.html#Function-Attributes

Comments are closed.