Tag Archives: GLib

g_array_steal() and g_ptr_array_steal() in GLib 2.63.1

Another set of new APIs in the upcoming GLib 2.63.1 release allow you to steal all the contents of a GArray, GPtrArray or GByteArray, and continue using the array container to add more contents to in future.

This is work by Paolo Bonzini and Emmanuel Fleury, and will be available in the soon-to-be-released 2.63.1 release.

Here’s a quick example using GPtrArray — usage is similar in GArray and GByteArray:

g_autoptr(GPtrArray) chunk_buffer = g_ptr_array_new_with_free_func (g_bytes_unref);

/* Some part of your application appends a number of chunks to the pointer array. */
g_ptr_array_add (chunk_buffer, g_bytes_new_static ("hello", 5));
g_ptr_array_add (chunk_buffer, g_bytes_new_static ("world", 5));

…

/* Periodically, the chunks need to be sent as an array-and-length to some other part of the program. */
GBytes **chunks;
gsize n_chunks;

chunks = g_ptr_array_steal (chunk_buffer, &n_chunks);
for (gsize i = 0; i < n_chunks; i++)
  {
    /* Do something with each chunk here, and then free them, since g_ptr_array_steal() transfers ownership of all the elements and the array to the caller. */
    …

    g_bytes_unref (chunks[i]);
  }

g_free (chunks);

/* After calling g_ptr_array_steal(), the pointer array can be reused for the next set of chunks. */
g_assert (chunk_buffer->len == 0);

GTimeVal deprecation in GLib 2.61.2

tl;dr: GTimeVal and g_get_current_time() are not year-2038-safe and have been deprecated; use GDateTime and g_get_real_time() instead.

One of the latest changes in GLib (released in 2.61.2) is the deprecation of GTimeVal, g_get_current_time(), and a number of other time functions. This is because we can’t guarantee they’re wide enough on all platforms to be year-2038-safe.

Instead, you should use GDateTime or, if you just need to store epoch time, guint64. They are year-2038-safe — and with that, GLib should be entirely year-2038-safe.

GTimeVal is used in a number of places, and widespread (but simple) changes will need to be made to stop using it. You will likely have already seen some deprecation warnings popping up to inform you of this, if you use any C-based and GLib-based libraries.

If you can’t allocate time to fixing these deprecation warnings yet, you can silence them by explicitly stating your minimum and maximum supported versions of GLib. If your minimum supported version of GLib is older than 2.62, you won’t see deprecation warnings for GTimeVal (since it was deprecated in 2.62, and your code is claiming to need to support older GLib versions than that).

You can do that by setting the following in meson.build:

common_c_args = [
  '-DGLIB_VERSION_MIN_REQUIRED=GLIB_VERSION_2_44',
  '-DGLIB_VERSION_MAX_ALLOWED=GLIB_VERSION_2_60',
]

and then using common_c_args in the c_args field of every executable and library you define in Meson. Your minimum required GLib version should match the version you list in your dependency('glib-2.0').

So,

GTimeVal tv;
g_get_current_time (&tv);
g_message ("Current time: %lu", tv.tv_sec);

becomes

gint64 tv;
tv = g_get_real_time ();
g_message ("Current time: %" G_GINT64_FORMAT, tv / G_USEC_PER_SEC);

And

GTimeVal tv;
if (!g_time_val_from_iso8601 ("2019-08-24T10:47:05Z", &tv))
  g_error ("Conversion failed");

becomes

g_autoptr(GDateTime) dt = g_date_time_new_from_iso8601 ("2019-08-24T10:47:05Z", NULL);
if (dt == NULL)
  {
    g_error ("Conversion failed");
    return;
  }

gint64 time_val = g_date_time_to_unix (dt);
/* or (simpler) use the #GDateTime directly */

Finally

GTimeVal tv = some timeval;
g_autofree gchar *tv_in_iso8601 = g_time_val_to_iso8601 (&tv);
g_message ("In ISO 8601 format: %s", tv_in_iso8601);

becomes the following, using the new g_date_time_format_iso8601() function:

g_autoptr(GDateTime) dt = some GDateTime;
g_autofree gchar *dt_in_iso8601 = g_date_time_format_iso8601 (dt);
g_message ("In ISO 8601 format: %s", dt_in_iso8601);

g_assert_finalize_object() in GLib 2.61.2

One more API in this mini-series! g_assert_finalize_object(), which is available in GLib 2.61.2, which was released today.

This one’s useful when writing tests (and only when writing tests). It’s been put together by Simon McVittie to implement the common pattern needed in tests, where you want to unref a GObject and assert that you just dropped the final reference to the object — i.e., check that no references to the object have been leaked in the test.

Use it in place of g_object_unref(). If G_DISABLE_ASSERT is defined, it will actually just be a call to g_object_unref().

Here’s an example usage of it, straight out of the GLib unit test for it:

static void
test_assert_finalize_object (void)
{
  GObject *obj = g_object_new (G_TYPE_OBJECT, NULL);

  /* do some things with the obj here */

  g_assert_finalize_object (obj);
}

g_array_binary_search in GLib 2.61.2

The final API so far in this mini-series on new APIs in the GLib 2.62 series is g_array_binary_search(), put together by Emmanuel Fleury and based on code by Christian Hergert. It’s due to be released in 2.61.2 soon. But first, a reminder about GLib version numbering.

Like the rest of GNOME’s official module set, GLib follows an odd/even versioning scheme, where every odd minor version number, like 2.61.x, is an unstable release building up to an even minor version number, like 2.62.x, which is stable. APIs may be added in unstable releases. They may be modified or even removed (if they haven’t been in a stable release yet). So all of the APIs I’ve blogged about recently still have a chance to be tweaked or dropped if people find problems with them. So if you see a problem or think that one of these APIs would be awkward to use in some way, please say, sooner rather than later! They need fixing before they’re in a stable release.

Back to today’s API, g_array_binary_search(). As its name suggests, this does a binary search on an array (which it requires is already sorted). You can use it like this:

static gint
compare_guint64 (gconstpointer a,
                 gconstpointer b)
{
  guint64 uint64_a = *((guint64 *) a);
  guint64 uint64_b = *((guint64 *) b);

  if (uint64_a < uint64_b)
    return -1;
  else if (uint64_a > uint64_b)
    return 1;
  else
    return 0;
}

g_autoptr(GArray) my_array = g_array_new (FALSE, TRUE, sizeof (guint64));

for (guint i = 0; i < 100; i++)
  {
    guint64 random_uint64 = ( (guint64) g_random_int () << 32) | g_random_int ();
    g_array_append_val (my_array, random_uint64);
  }

g_array_sort (my_array, compare_guint64);

/* Is ‘1234’ in the array? If so, where? */
const guint64 search_uint64 = 1234;
guint search_index;
if (g_array_binary_search (my_array, &search_uint64, compare_guint64, &search_index))
  g_message ("Found ‘1234’ at index %u", search_index);
else
  g_message ("Didn’t find ‘1234’");

As all computer science algorithms courses will tell you, a binary search is faster than a linear search, so you should use this in preference to iterating over an array to find an element in it, where possible.

(That’s not entirely true: the overheads of accounting for the binary search bounds, and the slowness of scattered memory loads from the array in a binary search vs sequential access in a linear search, will probably make it slower than a linear search for small arrays. But both will be fast, and if you need to care about that level of performance, you should be using a custom data structure rather than GArray.)

Array copying and extending in GLib 2.61.2

A slightly more in-depth post in the mini-series this time, about various new functions which Emmanuel Fleury has landed in GLib 2.61.2 (which is due to be released soon), based on some old but not-quite-finished patches from others.

There’s g_ptr_array_copy() and g_array_copy(); and also g_ptr_array_extend() and g_ptr_array_extend_and_steal().

g_ptr_array_copy() and g_array_copy() are obvious functions and it’s not clear why they haven’t been added before. They allow you to copy a GPtrArray or a GArray, including its contents.
When copying a GPtrArray, you pass in a GCopyFunc to copy each element (for example, by increasing its reference count). If the GCopyFunc is NULL, the element is copied by value.

For example,

g_autoptr(GPtrArray) object_array = g_ptr_array_new_with_free_func (g_object_unref);

for (gsize i = 0; i < 10; i++)
  g_ptr_array_add (object_array, create_new_object (i));

object_array_copy = g_ptr_array_copy (object_array, g_object_ref, NULL);
/* object_array and object_array_copy now contain copies of the same elements, but
 * modifying one array will not modify the other */

The g_ptr_array_extend() functions are used to join one array onto the end of another. This means you can turn the following code to join the GObject elements of array2 onto the end of array1 and ref them all:

for (gsize i = 0; i < array2->len; i++)
  g_ptr_array_add (array1, g_object_ref (g_ptr_array_index (array2, i)));

into

g_ptr_array_extend (array1, array2, g_object_ref, NULL);

If you no longer need array2, you can go further and use g_ptr_array_extend_and_steal() to avoid copying each element. This might be particularly beneficial when using string arrays, where each copy (a g_strdup()) is more expensive. So the following code:

g_autoptr(GPtrArray) array1 = g_ptr_array_new_with_free_func (g_free);
for (guint i = 0; i < 10; i++)
  g_ptr_array_add (array1, g_strdup_printf ("array1 %u", i));

g_autoptr(GPtrArray) array2 = g_ptr_array_new_with_free_func (g_free);
for (guint i = 100; i < 110; i++)
  g_ptr_array_add (array2, g_strdup_printf ("array2 %u", i));

for (gsize i = 0; i < array2->len; i++)
  g_ptr_array_add (array1, g_strdup (g_ptr_array_index (array2, i)));

would become:

g_autoptr(GPtrArray) array1 = g_ptr_array_new_with_free_func (g_free);
for (guint i = 0; i < 10; i++)
  g_ptr_array_add (array1, g_strdup_printf ("array1 %u", i));

g_autoptr(GPtrArray) array2 = g_ptr_array_new_with_free_func (g_free);
for (guint i = 100; i < 110; i++)
  g_ptr_array_add (array2, g_strdup_printf ("array2 %u", i));

g_ptr_array_extend_and_steal (array1, g_steal_pointer (&array2));
/* array2 has now been destroyed */

g_test_summary and g_get_console_charset in GLib 2.61.2

Another short post about new APIs, this time from the upcoming 2.61.2 release. This time it’s two unrelated new APIs, which I’m covering together because they’re fairly short.

g_test_summary() is a new API along the same lines as the existing g_test_bug() function. It’s to be called from within a unit test to provide a summary of the test to the test harness. In contrast, g_test_bug() provides a bug reference for the unit test. In this fashion, the two can be used to provide documentation within the test code of what the test is testing, how it goes about testing it, and which bug it’s checking for regressions in. The summary passed to g_test_summary() might be printed out as a comment in the test logs.

The summary passed to g_test_summary() should contain a brief overview of what the test does, and should note any particularly non-obvious things about how the test is implemented.

For example:

g_test_summary ("Test g_queue_push_nth_link() with various combinations of position (before, "
                "in the middle of, or at the end of the queue) and various existing queues "
                "(empty, single element, multiple elements).");

or:

g_test_summary ("Ensure that we successfully return IPv4 results even when they come significantly later than an IPv6 failure.");

The second API, g_get_console_charset(), is more useful only on Windows. On Linux, it returns the same value as g_get_charset(). On Windows, its result may differ if the console the current process is attached to has a different character encoding from that of the current locale. In those situations, g_get_console_charset() will return something which should result in correct character decoding by the console. If the process is not attached to a console, it returns UTF-8.

When should you care? Use g_get_console_charset() whenever you need to work out the character encoding for printing to the console (stdout or stderr), even on Linux (if you want your code to be portable). Use g_get_charset() whenever you need the character encoding for other strings related to the system locale, such as dates or file names. GLib correctly uses the two functions internally so that you should generally never have to care (for example, you don’t need to care when you use g_log(), g_date_time_format() or g_filename_to_utf8()).

g_queue_insert_before_link() in GLib 2.61.1

The second post in a little mini-series on new APIs in the GLib 2.62 series, this one’s about Christian Hergert’s g_queue_insert_before_link().

This is a new helper function for inserting elements at arbitrary positions in a queue, without needing to allocate a new container element for them. Previously, using g_queue_insert_before(), a new GList container would have been allocated. The new function means that elements can be moved from one position in a queue to another, without any allocations; and statically allocated GList elements can be used in a GQueue correctly.

The new function comes with friends, too: g_queue_insert_after_link() and g_list_insert_before_link(). They behave similarly.

One example of using the new function would be to bump the priority of an element in the middle of a queue:

GList *element_to_bump = g_queue_find (queue, important_data);
GList *old_sibling = element_to_bump->prev;
if (old_sibling != NULL)
  {
    g_queue_unlink (queue, element_to_bump);
    g_queue_insert_before_link (queue, old_sibling, element_to_bump);
  }
else
  {
    g_debug ("Already at the head of the queue");
  }

Christian is using it in GNOME Builder to arrange embedded structures in a queue using embedded GList instances, kernel-style.

g_clear_signal_handler() in GLib 2.61.1

It’s been a long time since I’ve blogged, so I thought I’d do a quick series on new APIs in the upcoming 2.62 release series of GLib.

Today, it’s the g_clear_signal_handler() function added by Marco Trevisan. This is a simple helper function along the same lines as g_clear_pointer(), g_clear_error() and g_clear_handle_id(). Given a GObject and a signal handler ID, it disconnects the signal handler and clears the signal handler ID variable to zero.

This turns the following standard pattern of code:

if (obj != NULL && handler_id != 0)
  {
    g_signal_handler_disconnect (obj, handler_id);
    handler_id = 0;
  }

to this:

g_clear_signal_handler (&handler_id, obj);

Nothing earth-shattering, but it does allow code to become a little more compact.

GUADEC 2018 thoughts

GUADEC this year was another good one; thank you to the organisers for putting on a great and welcoming conference, and to Endless for sending me.

Unfortunately I couldn’t make the first two days due to a prior commitment, but I arrived on the Sunday in time to give my talks. I’ve got a lot of catching up to do with the talks on Friday and Saturday — looking forward to seeing the recordings online!

The slides for my talk on the state of GLib are here and the notes are here (source for them is here). I think the talk went fairly well, although I imagine it was quite boring for most involved — I’m not sure how to make new APIs particularly interesting to listen to!

The slides for my talk on download management on metered connections (the ‘Mogwai’ project) are here and the notes are here (source for them is here). I think this talk also went fairly well, and I’m pleased by how many people turned up and asked insightful questions. As I said in the talk, my time to spend on this project is currently limited, but I am interested in mentoring new contributors on it. Get in touch if you’re interested.

During the birds of a feather days, I spent most of my time on GLib, clearing out old bugs. We had the GLib BoF during the GTK+ one on Monday. The notes are here. Emmanuele has already done a good writeup of the results of the BoF here; and Matthias has written up the GTK+ BoF itself here.

There were some good discussions over dinner during the BoF days about people’s niggles with GLib, which has set a few ideas in motion in my head which I will try and explore over the coming few months, once the 2.58 release is out of the way.

It was good to catch up with everyone, great to see Almería and sample its food and drink, and nice to finally meet some of my colleagues from Endless for the first time!