In libgio/libg, how does one know what needs to be freed?

,

My problem centers mostly around GVariants.

For example, the DBusSignalCallback:

void
(* GDBusSignalCallback) (
  GDBusConnection* connection,
  const gchar* sender_name,
  const gchar* object_path,
  const gchar* interface_name,
  const gchar* signal_name,
  GVariant* parameters,
  gpointer user_data
)

The connection needs to survive in my case after the callback. user_data was created by me, so I know how to handle it.

Do I assume the rest of the parameters do not need to be freed by g_free, or g_variant_unref?

And when I decode GVariant, how do I know which pointers that are loaded need to be freed, and which do not?

I have seen errors generated deep in the library when some free function was called on something that couldn’t be freed.

Decode of GVariant parameters:

g_variant_get(parameters, "(&sa{sv}as)", &interface_name, &properties, &unknown);

So interface_name is byte array, which could have g_free() called on it, properties is a GVariantIter which could also be freed with g_variant_iter_free (but the doc suggests not to do it unless allocated by g_variant_iter_new() or g_variant_iter_copy(), but who knows what g_variant_get() actually does), and unknown is also a byte array.

Then you have a loop acquiring items from the properties dictionary:

while(g_variant_iter_next(properties, “{&sv}”, &key, &value)) {

So you have a char or byte array key, and a variant value. These could possibly be freed by g_free and g_variant_unref(). And if the variant has a byte array, that also could be freed by g_free().

I tried to run valgrind on my program, and it seemed to suggest that g_variant_get causes memory leaks. But I have no idea if valgrind is valid when linked into libg/libgio.

Does libgio/libg have anything for analyzing memory, and preventing memory leaks, or maybe double frees or frees of storage that cannot be freed?

It depends on the context. GVariants in particular can take a bit to get the hang of.

In almost every case parameters passed to callbacks should not be freed by the callee (i.e. inside the callback). The const in const gchar * is a dead giveaway for the string arguments, but with arguments like GDBusConnection * you should assume you must not free them unless explicity told so in the documentation.

For GVariant extractions you will want to read GVariant Format Strings carefully and re-visit the page when you’re not sure.

If you are running Valgrind on GLib code, be sure you are using the glib.supp suppression file included in GLib. Personally, I prefer AddressSanitizer because it has much lower overhead, but Valgrind may catch things ASan doesn’t.

In any case, the official documentation includes annotations for all functions, indicating whether the caller (code that invokes the function) or the callee (the function itself) is responsible for freeing arguments and return values.

Expecting the callee to free callback parameters could only work in the case where there is exactly one handler connected to the signal in question. Otherwise you’d either leak the memory (no handler) or run into double-free (multiple handlers).

So yeah, the memory of callback parameters is almost always owned by the signal emitter. I don’t recall ever seeing an exception, so it’s possible that “almost” is an understatement.

1 Like

The trouble is the doc doesn’t cover all functions on the storage allocation topic. Maybe they assume you know which functions the gio library is using?

g_variant_iter_next says:

format_string determines the C types that are used for unpacking the values and also determines if the values are copied or borrowed.

The format strings page says:

Upon encountering s, o or g, g_variant_get() takes a pointer to a (gchar *) (ie: (gchar **)) and sets it to a newly-allocated copy of the string. It is appropriate to free this copy using g_free(). NULL may also be passed to indicate that the value of the string should be ignored (in which case no copy is made).

So borrowed apparently refers to the & character:

Pointers

The & character is used to indicate that serialised data should be directly exchanged via a pointer.
Currently, the only use for this character is when it is applied to a string (ie: &s, &o or &g). For g_variant_new() this has absolutely no effect. The string is collected and duplicated normally. For g_variant_get() it means that instead of creating a newly allocated copy of the string, a pointer to the serialised data is returned. This pointer should not be freed. Validity checks are performed to ensure that the string data will always be properly nul-terminated.

So if we do:
g_variant_iter_next(&i, "s", &uuid)

We get a copy of the string?

And if we do this (we aren’t creating a new GVariant here):
g_variant_iter_next(&i, "&s", &uuid)

We get the same storage the iterator points to?

Thanks for the Valgrind tip.

No: the assumption is that you don’t free/release references unless explicitly stated.

In other words: the assumption is that the documentation makes it explicit—either by directly saying it, or by using the “transfer” annotation—who owns what, and who is responsible for the data.

For instance, all “in” arguments—arguments that are passed to a function—are not owned by you, unless the ownership is explicitly transferred using the “transfer full” annotation.

For functions that are somewhat complicated to annotate in order to make them machine parseable, you have to rely on the documentation being explicit. In this case, the documentation for the GVariant format strings explicitly states the direction and ownership.

Yes, and yes.

Emmanuele Bassi wrote:

either by directly saying it, or by using the “transfer” annotation

What is the transfer annotation? Could you show me an example?

For instance g_dbus_connection_call_sync() shows a parameter GError **error:

Paramters for g_dbus_connection_call_sync

There is nothing explicit about **error being transferred to the user. There does exist g_error_free(). GError is not declared const. So I would think I am not to free it?

Thanks very much for your help.

No, you do need to free errors. This is clear from the documentation of GError itself.

Though, since gi-docgen generates the whole error documentation out of whole cloth (as it’s elided by the introspection data) I guess I can make it more clear—especially because it’s missing a link.

1 Like

Okay, this should be a bit better:

The argument will be set to NULL by the function if there are no errors.

Surely the caller must initialize the error to NULL?

That’s why the line above says “the argument can be NULL”.

I’m not going to specify that you have to set the error as NULL: that’s what the GError docs are for. Repeating how GError works everywhere we use GError is just going to make the documentation very hard to read.

The once case that came to mind was GDBusMethodInvocation, although you usually get those in a vfunc, so not exactly a signal, true :slight_smile:

Even I as a Newbie quickly figured out that error must be set to NULL.

Parameter checking by the function will not allow non-null, at least for the functions I was dealing with.

That is not as difficult to deal with as trying to figure out when to free storage. Not freeing storage that is meant to be freed causes a leak, and freeing storage that is already free is unpredictable.

The documentation will either tell you something like: “the data is owned by the instance”, meaning you must not free it; or something like: “the caller of the method takes ownership of the data, and is responsible for freeing it”, which means exactly what it says.

The problem is it says “The argument will be set to NULL by the function if there are no errors” and that doesn’t seem correct? Functions normally don’t touch the error parameter at all unless there is an error?

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.