7602 Commits

Author SHA1 Message Date
Emmanuel Fleury
d16d780bf4 Fix signedness warning in glib/win_iconv.c
glib/win_iconv.c: In function 'iso2022jp_mbtowc':
glib/win_iconv.c:1818:18: warning: comparison of integer expressions of different signedness: 'DWORD' {aka 'long unsigned int'} and 'int'
     if (cv->mode != ISO2022_MODE(cs, shift))
                  ^~
glib/win_iconv.c: In function 'iso2022jp_wctomb':
glib/win_iconv.c:1889:18: warning: comparison of integer expressions of different signedness: 'DWORD' {aka 'long unsigned int'} and 'int'
     if (cv->mode == ISO2022_MODE(cs, shift))
                  ^~
2021-10-14 18:48:02 +02:00
Emmanuel Fleury
78af147721 Fix signedness warning in glib/gfileutils.c
glib/gfileutils.c: In function 'g_file_test':
glib/gfileutils.c:375:18: warning: comparison of integer expressions of different signedness: 'int' and 'long unsigned int'
   if (attributes == INVALID_FILE_ATTRIBUTES)
                  ^~
glib/gfileutils.c: In function 'g_get_current_dir':
glib/gfileutils.c:2882:40: warning: comparison of integer expressions of different signedness: 'DWORD' {aka 'long unsigned int'} and 'int'
   if (GetCurrentDirectoryW (len, wdir) == len - 1)
                                        ^~
2021-10-14 18:42:07 +02:00
Emmanuel Fleury
5471c13f65 Fix signedness warning in glib/gdate.c
glib/gdate.c: In function 'win32_strftime_helper':
glib/gdate.c:2582:12: warning: comparison of integer expressions of different signedness: 'gsize' {aka 'long long unsigned int'} and 'glong' {aka 'long int'}
   if (slen <= convlen)
            ^~
2021-10-14 18:40:32 +02:00
Philip Withnall
90b2ad80ee gutf8: Document that out args from g_utf16_to_utf8() are non-negative
Despite their type, the values returned will always be ≥ 0. It’s
unfortunate they weren’t declared with an unsigned type, but we can’t
change that now without breaking API.

Spotted in !2294.

Signed-off-by: Philip Withnall <pwithnall@endlessos.org>
2021-10-14 12:45:30 +01:00
Emmanuel Fleury
4ec06aa0c1 Fix missing initializer warning in glib/giowin32.c
glib/giowin32.c:1027:1: warning: missing initializer for field 'closure_callback' of 'GSourceFuncs' {aka 'struct _GSourceFuncs'}
 };
 ^
2021-10-13 18:38:59 +02:00
Emmanuel Fleury
a8a4bbf652 Fix signedness warning in glib/gspawn-win32.c
glib/gspawn-win32.c: In function 'read_helper_report':
glib/gspawn-win32.c:329:16: warning: comparison of integer expressions of different signedness: 'gint' {aka 'int'} and 'long long unsigned int'
   while (bytes < sizeof(gintptr)*2)
                ^
glib/gspawn-win32.c:366:13: warning: comparison of integer expressions of different signedness: 'gint' {aka 'int'} and 'long long unsigned int'
   if (bytes < sizeof(gintptr)*2)
             ^
2021-10-13 18:38:59 +02:00
Seungha Yang
a2f2745035 glib-private: Fix MSVC build with AddressSanitizer
MSVC supports AddressSanitizer as well via "/fsanitize=address" option,
but __lsan_ignore_object() equivalent feature is not supported.
Note that there's __declspec(no_sanitize_address) specifier which
provides a similar feature but that's not runtime behavior
so it's not directly applicable to g_ignore_leak() family.

See also https://docs.microsoft.com/en-us/cpp/sanitizers/asan-building?view=msvc-160
2021-10-13 18:18:08 +09:00
Emmanuel Fleury
cd04e0b7a4 Fix signedness warning in glib/gslice.c
glib/gslice.c: In function 'slice_config_init':
glib/gslice.c:394:16: warning: comparison of integer expressions of different signedness: 'int' and 'long long unsigned int'
   else if (len >= G_N_ELEMENTS (wvalue))
                ^~
2021-10-12 18:05:48 +02:00
Emmanuel Fleury
574842a12e Fix signedness warning in glib/genviron.c
glib/genviron.c: In function 'g_getenv':
glib/genviron.c:490:52: warning: comparison of integer expressions of different signedness: 'DWORD' {aka 'long unsigned int'} and 'int'
   if (GetEnvironmentVariableW (wname, wvalue, len) != len - 1)
                                                    ^~
glib/genviron.c:507:60: warning: comparison of integer expressions of different signedness: 'DWORD' {aka 'long unsigned int'} and 'int'
           if (ExpandEnvironmentStringsW (tem, wvalue, len) != len)
                                                            ^~
2021-10-12 18:05:48 +02:00
Emmanuel Fleury
affa41179a Fix signedness warning in glib/ghostutils.c
glib/ghostutils.c: In function 'punycode_encode':
glib/ghostutils.c:141:35: warning: comparison of integer expressions of different signedness: 'guint' {aka 'unsigned int'} and 'glong' {aka 'long int'}
   for (j = num_basic_chars = 0; j < input_length; j++)
                                   ^
glib/ghostutils.c:158:24: warning: comparison of integer expressions of different signedness: 'guint' {aka 'unsigned int'} and 'glong' {aka 'long int'}
   while (handled_chars < input_length)
                        ^
glib/ghostutils.c:161:36: warning: comparison of integer expressions of different signedness: 'guint' {aka 'unsigned int'} and 'glong' {aka 'long int'}
       for (m = G_MAXUINT, j = 0; j < input_length; j++)
                                    ^
glib/ghostutils.c:172:21: warning: comparison of integer expressions of different signedness: 'guint' {aka 'unsigned int'} and 'glong' {aka 'long int'}
       for (j = 0; j < input_length; j++)
                     ^
2021-10-12 18:05:48 +02:00
Emmanuel Fleury
74c3c5b75d Fix signedness warning in glib/gcharset.c
glib/gcharset.c:380:35: warning: comparison of integer expressions of different signedness: 'long long int' and 'long long unsigned int'
           else if (modifier - dot < sizeof (buf))
                                   ^
2021-10-12 18:05:48 +02:00
Emmanuel Fleury
33c2968669 Fix always false statement warning in glib/gdatetime.c
glib/gdatetime.c:896:27: warning: comparison is always false due to limited range of data type
   if ((gint64) tv->tv_sec > G_MAXINT64 - 1 ||
                           ^
2021-10-12 18:03:58 +02:00
Guillaume Gomez
0c6ed99403 Update g_source_remove documentation for the returned value 2021-10-11 11:22:44 +02:00
Guillaume Gomez
a3ac24c20d Update g_source_remove doc comment: the function doesn't always return TRUE
This comment was added in a919be3d in 2013. The function basically looked the same
as now at that point. g_main_context_find_source_by_id() can clearly return NULL if
the source id is invalid, so unclear where this comes from? The function in
question are approximately the same since e2fd4e2b in 2000.

However back then g_main_context_find_source_by_id() would actually always return
the last source if there was none with the given source id (wat, that's clearly
unintended?). This was changed in 393503ba in 2014, arguably an API change of that
function but more arguably a bugfix :)

So for a short time between 2013 and 2014, that comment was correct. Now it is not
anymore and can be removed.
2021-10-10 17:53:30 +02:00
Philip Withnall
a4c9049486 build: Don’t pass false to install_dir
This fixes a Meson deprecation warning which appeared in Meson 0.50.

Signed-off-by: Philip Withnall <pwithnall@endlessos.org>
2021-10-07 17:15:13 +01:00
Gleb Popov
709226d6f2 Do not try to access errno after calling getpwnam_r. 2021-09-28 10:36:42 +00:00
Philip Withnall
59ea3c53bd glib-private: Add begin/end ignore leak functions for AddressSanitizer
These just wrap the `__lsan_enable()` and `__lsan_disable()` functions
from the AddressSanitizer client API. They’re useful in situations where
the intended-to-be-leaked memory is being allocated in third-party code,
such as xdgmime. We can’t patch that code to call `g_ignore_leak()`.

Signed-off-by: Philip Withnall <pwithnall@endlessos.org>

Helps: #2310
2021-09-27 13:12:31 +01:00
liuyangming
2b8682191c fix uninitial variable 2021-09-27 18:35:37 +08:00
Emmanuel Fleury
f304df34c7 Coerce type cast to void* because it causes compiler warnings
glib/gtestutils.c:4261:11: warning: incompatible pointer types passing 'gpointer *' (aka 'void **') to parameter of type 'GSList **' (aka 'struct _GSList **')
  while (!g_atomic_pointer_compare_and_exchange (test_filename_free_list, node->next, node));
          ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
glib/gatomic.h:215:44: note: expanded from macro 'g_atomic_pointer_compare_and_exchange'
    __atomic_compare_exchange_n ((atomic), &gapcae_oldval, (newval), FALSE, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST) ? TRUE : FALSE; \
                                           ^~~~~~~~~~~~~~
2021-09-23 22:09:07 +02:00
Michael Catanzaro
969a26378b Merge branch 'fix_pw_name_segfault' into 'main'
gutils: Avoid segfault in g_get_user_database_entry

See merge request GNOME/glib!2244
2021-09-22 12:46:00 +00:00
Jamie Bainbridge
bb40105fe9 gutils: Avoid segfault in g_get_user_database_entry
g_get_user_database_entry() capitalises the first letter of pw_name
with g_ascii_toupper (pw->pw_name[0]).

However, the manpage for getpwnam() and getpwuid() says the result of
those calls "may point to a static area". GLib is then trying to edit
static memory which belongs to a shared library, so segfaults.

The reentrant variants of the above calls are supposed to fill the user
buffer supplied to them, however Michael Catanzaro also found a bug in
systemd where the data is not copied to the user buffer and still points
to static memory, resulting in the same sort of segfault. See:
https://github.com/systemd/systemd/issues/20679

Solve both these cases in GLib by copying pw_name off to a temporary
variable, set uppercase on that variable, and use the variable to join
into the desired string. Free the variable after it is no longer needed.

Signed-off-by: Jamie Bainbridge <jamie.bainbridge@gmail.com>
2021-09-22 11:40:45 +10:00
Vinícius dos Santos Oliveira
e26a8a5981 Add G_MAIN_CONTEXT_FLAGS_OWNERLESS_POLLING
It fixes a race. g_source_attach() had the following check to ensure a
loop blocked on poll() would wakeup.

  if (do_wakeup && context->owner && context->owner != G_THREAD_SELF)
    g_wakeup_signal (context->wakeup);

However it doesn't contemplate an implementation where poll()ing is a
non-blocking operation that will be scheduled while the thread is
released to perform other tasks. This scenario opens up several
different possibilities where the condition would fail to hold true. I
experienced two of such races.

The first race pertains to a mono-threaded application. Do keep in mind
that integrating GLib to a foreign event loop will make GLib act as a
slave in the new event loop. When you post a new work unit to execute in
the thread managed by the foreign event loop, you don't use
g_main_context_invoke(). In fact the only reason to integrate
GMainContext in a foreign event loop is to make the two of them
communicate. So from time to time, the foreign event loop will execute
callbacks that manipulate the GMainContext loop. An illustration
follows.

  // in this callback we translate an event from the foreign event loop
  // to an event in the GMainContext event loop (that runs in the same
  // thread)
  static void my_event_loop_callback(void* data)
  {
    GMainContext* ctx = /* ... */;
    // ...
    g_source_attach(source, ctx);
  }

  int main()
  {
    // ...
    my_event_loop_invoke(my_event_loop_callback, data);
    // ...

    // this function has all mechanisms in place to run the foreign
    // event loop and the hooks to call
    // g_main_context_{prepare,query,check,dispatch}
    my_event_loop_run();
  }

In this case, you would have the following series of calls:

1. g_main_context_prepare()
2. g_main_context_query()
3. A callback to my_event_loop is registered when any fd on the set is
   ready or the timeout is reached.
4. The thread is released to perform other tasks.
5. One of the tasks executed wishes to communicate with my_event_loop
   and enters my_event_loop_callback.
6. g_source_attach() is called.
7. g_source_attach() detects do_wakeup=TRUE, context->owner != NULL, and
   context->owner == G_THREAD_SELF so g_wakeup_signal() is skipped.
8. None of the fds on the GLib poll() set becomes ready nor the GLib
   timeout expires. The my_event_loop callback that would call
   g_main_context_check() is never executed. Deadlock.

A shallow analysis will fail to detect the race here. The explanation
seems to showcase a scenario that will deterministically fail with a
deadlock every time. However do keep in mind that my_event_loop_callback
could be invoked before or after g_main_context_prepare(). There is an
_event_ race here. Furthermore, some GLib libraries such as GDBus will
initialize objects from extra threads (GAsyncInitable interface) and
invoke the result on the original thread when ready (g_source_attach()
will eventually be called). Now you have scenarios closer to standard
race examples.

The other scenario where a race would manifest happens in a
multi-threaded application that has a concurrency design similar to the
actor model. No actor executes in two threads simultaneously, but it's
not guaranteed that it'll always wake-up in the same thread. It'd
perform steps 1-4 just as in the previous example, but before thread
control is returned to the pool, it'd call g_main_context_release(). Now
g_source_attach() would skip g_wakeup_signal() for a different reason:

7. g_source_attach() detects do_wakeup=TRUE, context->owner == NULL so
   g_wakeup_signal() is skipped.
8. Same as before.

Certainly there are other concurrency designs where this optimization
would cause a deadlock, but all of them have origin in the same place:
the optimization assumes the poll() implementation is a blocking
operation and the thread will never be released to perform other tasks
(possibly involving GLib calls) while result is not ready. They share
not only the same problem, but also the same solution: do not make
assumptions and just call g_wakeup_signal().

This patch implements this solution by introducing the
G_MAIN_CONTEXT_FLAGS_OWNERLESS_POLLING flag. This flag will force a call
to g_wakeup_signal() and fix the race on foreign event loops. The reason
to prevent changing this option after creation is to avoid other races
that would lead to event loss. Construction is the only proper time to
set this option.

The implementation design means we do not change **any** semantics for
current working code. If you don't set the new flag, the code won't
enter in different branches and current behavior won't be affected. The
patch is small and easy to follow too.
2021-09-21 14:50:30 +01:00
Vinícius dos Santos Oliveira
7ba86be594 Add g_main_context_new_with_flags()
This constructor is useful to set options that can't change after
creation.
2021-09-21 14:50:30 +01:00
Emmanuele Bassi
6fcad9d288 Merge branch 'open-2.71' into 'main'
Add version macros for GLib 2.72 and bump version to 2.71.0

See merge request GNOME/glib!2249
2021-09-21 13:36:42 +00:00
Philip Withnall
bbd1350beb Merge branch '#0434_GSequenceSlowsDown_counter' into 'main'
gsequence: make treap priorities more random to avoid worst-case scenarios

Closes #2468

See merge request GNOME/glib!2236
2021-09-21 10:40:48 +00:00
Matthias Clasen
ab895d91d5 Update to Unicode 14 2021-09-21 09:41:29 +00:00
Matthias Clasen
7fc7c57b6f GString: Bump minimum size
GString starts out at a size of 2, which is just
not useful. Bump the minimum size to 64 to cut
down on the number of tiny reallocations we do.
2021-09-18 20:16:57 -04:00
Philip Withnall
94b74c761d gversionmacros: Add version macros for GLib 2.72
Signed-off-by: Philip Withnall <pwithnall@endlessos.org>
2021-09-17 11:35:21 +01:00
Alexandr Miloslavskiy
59e5612339 gsequence: make treap priorities more random to avoid worst-case scenarios
Previously, priority was not randomly generated and was instead derived
from `GSequenceNode*` pointer value.

As a result, when a `GSequence` was freed and another was created, the
nodes were returned to memory allocator in such order that allocating
them again caused various performance problems in treap.

To my understanding, the problem develops like this :
1) Initially, memory allocator makes some nodes
2) For each node, priority is derived from pointer alone.
   Due to the hash function, initially the priorities are reasonably
   randomly distributed.
3) `GSequence` moves inserted nodes around to satisfy treap property.
   The priority for node must be >= than priorities of its children
4) When `GSequence` is freed, it frees nodes in a new order.
   It finds root node and then recursively frees left/right children.
   Due to (3), hashes of freed nodes become partially ordered.
   Note that this doesn't depend on choice of hash function.
5) Memory allocator will typically add freed chunks to free list.
   This means that it will reallocate nodes in same or inverse order.
6) This results in order of hashes being more and more non-random.
7) This order happens to be increasingly anti-optimal.
   That is, `GSequence` needs more `node_rotate` to maintain treap.
   This also causes the tree to become more and more unbalanced.
   The problem becomes worse with each iteration.

The solution is to use additional noise to maintain reasonable
randomness. This prevents "poisoning" the memory allocator.

On top of that, this patch somehow decreases average tree's height,
which is good because it speeds up various operations. I can't quite
explain why the height decreases with new code, probably the properties
of old hash function didn't quite match the needs of treap?

My averaged results for tree height with different sequence lengths:
  Items | before|         after |
--------+-------+---------------+
      2 |  2,69 |  2,67 -00,74% |
      4 |  3,71 |  3,80 +02,43% |
      8 |  5,30 |  5,34 +00,75% |
     16 |  7,45 |  7,22 -03,09% |
     32 | 10,05 |  9,38 -06,67% |
     64 | 12,97 | 11,72 -09,64% |
    128 | 16,01 | 14,20 -11,31% |
    256 | 19,11 | 16,77 -12,24% |
    512 | 22,03 | 19,39 -11,98% |
   1024 | 25,29 | 22,03 -12,89% |
   2048 | 28,43 | 24,82 -12,70% |
   4096 | 31,11 | 27,52 -11,54% |
   8192 | 34,31 | 30,30 -11,69% |
  16384 | 37,40 | 32,81 -12,27% |
  32768 | 40,40 | 35,84 -11,29% |
  65536 | 43,00 | 38,24 -11,07% |
 131072 | 45,50 | 40,83 -10,26% |
 262144 | 48,40 | 43,00 -11,16% |
 524288 | 52,40 | 46,80 -10,69% |

The memory cost of the patch is zero on 64-bit, because the new field
uses the alignment hole between two other fields.

Note: priorities can sometimes have collisions. This is fine, because
treap allows equal priorities, but these will gradually decrease
performance. The hash function that was used previously has just one
collision on 0xbfff7fff in 32-bit space, but such pointer will not
occur because `g_slice_alloc()` always aligns to sizeof(void*).
However, in 64-bit space the old hash function had collisions anyway,
because it only uses lower 32 bits of pointer.

Closes #2468
2021-09-09 23:34:16 +03:00
Evan Miller
508352339a Fix false deprecation warnings on old GCC/MSVC
Closes #2472
2021-09-07 11:21:20 +01:00
Philip Withnall
0eadf651fb tests: Rewrite thread-pool test for freeing queued items
The previous test was racy: it assumed that not all queued thread pool
jobs would start to be executed before `g_thread_pool_free()` was
called, whereas actually on some systems (particularly BSD ones), they
would all start (or even finish) being executed, and hence the free
function might never be called.

Rewrite the test to:
 • Synchronise the test function and worker thread functions more
   closely.
 • Not bother about ordering the shared and exclusive variants of the
   test differently. That seems to be a hangover from another test
   above.
 • Limit the number of worker threads to 1, rather than 2, since this
   makes the test easier to control.

This has been tested with `--repeat 10000` on Linux, and it succeeds all
of those runs whereas previously it failed quite reliably.

Signed-off-by: Philip Withnall <pwithnall@endlessos.org>

Fixes: #2456
2021-08-19 14:25:24 +01:00
Philip Withnall
b402f66c07 gthreadpool: Remove a dummy item from the queue before freeing
Now that `g_thread_pool_new_full()` can be used to set a user-provided
free function for queue elements, ensure that the internal dummy item
used to wake up the worker threads is removed from the queue before it’s
called.

Signed-off-by: Philip Withnall <pwithnall@endlessos.org>

Helps: #2456
2021-08-19 14:25:24 +01:00
Simon McVittie
052e335500 tests: Make use of g_test_fail_message()
Signed-off-by: Simon McVittie <smcv@collabora.com>
2021-08-19 09:49:11 +01:00
Simon McVittie
a076dbcb68 gtestutils: Allow failing a test with a printf-style message
This allows a pattern like

    g_test_message ("cannot reticulate splines: %s", error->message);
    g_test_fail ();

to be replaced by the simpler

    g_test_fail_printf ("cannot reticulate splines: %s", error->message);

with the secondary benefit of making the message available to TAP
consumers as part of the "not ok" message.

Signed-off-by: Simon McVittie <smcv@collabora.com>
2021-08-19 09:49:11 +01:00
Simon McVittie
182d9995ca gtestutils: Allow skipping tests with a printf-style message
Forming the g_test_skip() message from printf-style arguments seems
common enough to deserve a convenience function.

g_test_incomplete() is mechanically almost equivalent to g_test_skip()
(the semantics are different but the implementation is very similar),
so give it a similar mechanism for symmetry.

Signed-off-by: Simon McVittie <smcv@collabora.com>
2021-08-19 09:41:08 +01:00
Sebastian Dröge
336c747e59 Merge branch 'string-ctors' into 'main'
Annotate the GString constructors

See merge request GNOME/glib!2226
2021-08-17 07:10:54 +00:00
Avinash Sonawane
31f793b71a Docs: Mention that G_VA_COPY() must be followed by va_end() 2021-08-16 20:05:59 +00:00
Emmanuele Bassi
d5b6c55cfe Annotate the GString constructors
Otherwise the introspection scanner won't recognise them as
constructors, because the GString get_type function has a different
symbol prefix.

See: https://gitlab.gnome.org/GNOME/gobject-introspection/-/issues/399
2021-08-16 20:58:53 +01:00
Simon McVittie
b006403c4e tests: Fix error handling when testing gtestutils
We had two compensating bugs here. We didn't correctly clear the
error indicator after testing a test-case that calls g_test_fail(),
which meant we were leaving the error set to exit status 1 when
falling through to the next test; and then we didn't check the exit
status of the next test, but instead assumed that g_spawn_sync()
would fail (it does not).

Signed-off-by: Simon McVittie <smcv@collabora.com>
2021-08-05 12:09:53 +01:00
Philip Withnall
709df8eeb4 Merge branch 'docgen-fixes' into 'main'
Adapt documentation to gi-docgen

See merge request GNOME/glib!2206
2021-08-03 13:53:38 +00:00
Emmanuele Bassi
bed2da6cc2 docs: Break gtk-doc stanzas into paragraphs
Keep the first paragraph short, to act as a summary.
2021-08-02 16:00:12 +01:00
Emmanuele Bassi
4bbe7912a1 docs: Use the correct sigils for pre-processor symbols
And reduce the excessive whitespace in argument and return value blocks.
2021-08-02 15:59:43 +01:00
Emmanuele Bassi
97a6111c83 docs: Enable syntax highlighting on code examples 2021-08-02 15:56:42 +01:00
Emmanuele Bassi
3b5d3ed2e3 docs: Fix KeyFile annotations
Use the right gtk-doc sigil for pre-processor symbols.

Reduce the indentation in argument annotations, to avoid them being
parsed as code blocks.
2021-08-02 15:55:08 +01:00
Simon McVittie
37e5dfd3a2 Merge branch 'close-range-cloexec' into 'main'
gspawn: Use CLOSE_RANGE_CLOEXEC if available

See merge request GNOME/glib!2184
2021-08-02 14:49:27 +00:00
Emmanuele Bassi
1b666b7f12 docs: Clean up the GDate types description
Split the first paragraph.

Use the correct gtk-doc sigil for enumeration value.

Use the appropriate term for negative years in the Western calendar.
2021-08-02 14:54:34 +01:00
Emmanuele Bassi
2aedaf293b docs: Annotate glib_check_version()
Add introspection annotations.

Reduce the indentation for the return value documentation string.

Use the appropriate gtk-doc syntax for symbols.
2021-08-02 14:52:06 +01:00
Philip Withnall
92bdc92d6d Merge branch 'unicode-typo-fix' into 'main'
Fix a Unicode typo

See merge request GNOME/glib!2201
2021-08-02 13:44:20 +00:00
Philip Withnall
d6576e9781 Merge branch 'nfc-nfd-test' into 'main'
tests: Add a test for Unicode normalization

See merge request GNOME/glib!2204
2021-08-02 13:17:23 +00:00
Philip Withnall
fad0a6af87 Merge branch 'wip/smcv/2452-g-string-0-length-replace' into 'main'
g_string_replace: Don't replace empty string more than once per location

Closes #2452

See merge request GNOME/glib!2208
2021-08-02 12:47:03 +00:00