Commit Graph

422 Commits

Author SHA1 Message Date
Thomas Haller
5609370de9 gbitlock: add g_pointer_bit_lock_and_get()
Usually, after g_pointer_bit_lock() we want to read the pointer that we
have. In many cases, when we g_pointer_bit_lock() a pointer, we can
access it afterwards without atomic, as nobody is going to modify the
pointer then.

However, gdataset also supports g_datalist_set_flags(), so the pointer
may change at any time and we must always use atomics to read it. For
that reason, g_datalist_lock_and_get() does an atomic read right after
g_pointer_bit_lock().

g_pointer_bit_lock() can easily access the value that it just set. Add
g_pointer_bit_lock_and_get() which can return the value that gets set
afterwards.

Aside from saving the second atomic-get in certain scenarios, the
returned value is also atomically the one that we just set.
2024-01-16 12:50:31 +01:00
Thomas Haller
c4c76d77cb gbitlock: add g_pointer_bit_unlock_and_set() and g_pointer_bit_lock_mask_ptr()
The existing g_pointer_bit_lock() and g_pointer_bit_unlock() API
requires the user to understand/reimplement how bits of the pointer get
mangled. Add helper functions for that.

The useful thing to do with g_pointer_bit_lock() API is to get/set
pointers while having it locked. For example, to set the pointer a user
can do:

    g_pointer_bit_lock (&lockptr, lock_bit);
    ptr2 = set_bit_pointer_as_if_locked(ptr, lock_bit);
    g_atomic_pointer_set (&lockptr, ptr2);
    g_pointer_bit_unlock (&lockptr, lock_bit);

That has several problems:

- it requires one extra atomic operations (3 instead of 2, in the
  non-contended case).

- the first g_atomic_pointer_set() already wakes blocked threads,
  which find themselves still being locked and needs to go back to
  sleep.

- the user needs to re-implement how bit-locking mangles the pointer so
  that it looks as if it were locked.

- while the user tries to re-implement what glib does to mangle the
  pointer for bitlocking, there is no immediate guarantee that they get
  it right.

Now we can do instead:

  g_pointer_bit_lock(&lockptr, lock_bit);
  g_pointer_bit_unlock_and_set(&lockptr, lock_bit, ptr, 0);

This will also emit a critical if @ptr has the locked bit set.
g_pointer_bit_lock() really only works with pointers that have a certain
alignment, and the lowest bits unset. Otherwise, there is no space to
encode both the locking and all pointer values. The new assertion helps
to catch such bugs.

Also, g_pointer_bit_lock_mask_ptr() is here, so we can do:

  g_pointer_bit_lock(&lockptr, lock_bit);
  /* set a pointer separately, when g_pointer_bit_unlock_and_set() is unsuitable. */
  g_atomic_pointer_set(&lockptr, g_pointer_bit_lock_mask_ptr(ptr, lock_bit, TRUE, 0, NULL));
  ...
  g_pointer_bit_unlock(&lockptr, lock_bit);

and:

  g_pointer_bit_lock(&lockptr, lock_bit);
  /* read the real pointer after getting the lock. */
  ptr = g_pointer_bit_lock_mask_ptr(lockptr, lock_bit, FALSE, 0, NULL));
  ...
  g_pointer_bit_unlock(&lockptr, lock_bit);
2024-01-16 12:16:42 +01:00
Thomas Haller
e05623001b gobject: fix deadlock with g_weak_ref_set()
In general, we must not call out to external, unknown code while holding
a lock. That is prone to dead lock.

g_object_ref() can emit a toggle notification. In g_weak_ref_set(), we
must not do that while holding the lock.
2024-01-04 16:42:59 +01:00
Thomas Haller
6c389738d3 gobject/tests/reference: add tests for concurrent g_weak_ref_set()
GWeakRef methods are thread safe. Add test for calling g_weak_ref_set()
concurrently.
2024-01-04 16:32:09 +01:00
Thomas Haller
787861761d gobject/tests/reference: add test for deadlock related to GWeakRef and toggle reference
GWeakRef calls g_object_ref() while holding a read lock.
g_object_ref() can emit a toggle notification.

If you then inside the toggle notification setup a GWeakRef
(to the same or another object), the code will try to get
a write lock. Deadlock will happen.

Add a test to "show" that (the breaking part is commented out).
Will be fixed next.
2024-01-04 16:17:13 +01:00
Philip Withnall
dd2b6d7e3f Merge branch 'th/g-object-unref-ref' into 'main'
[th/g-object-unref-ref] fix race in g_object_unref()

Closes #3064

See merge request GNOME/glib!3769
2024-01-03 23:39:24 +00:00
Philip Withnall
bf7ccbde9e tests: Fix a minor leak in the new GParamSpecPool test
Signed-off-by: Philip Withnall <pwithnall@gnome.org>
2024-01-03 12:04:16 +00:00
Tanmay
dbbd9c60ba Add boxed GType for GRand
Signed-off-by: Tanmay Patil <tanmaynpatil105@gmail.com>
2023-12-31 09:09:48 +00:00
Thomas Haller
2952cfd7a7 gobject: drop clearing quark_weak_locations from g_object_real_dispose()
In g_object_unref(), we call _object_unref_clear_weak_locations() before
and after dispose() already. At those places it is necessary to do.

Calling it a third time during g_object_real_dispose() seems not useful,
has unnecessary overhead and actually undesirable.

In particular, because g_object_real_dispose() is the implementation for
the virtual function GObject.dispose(). For subclasses that override dispose(),
it's not well defined at which point they should chain up the parent
implementation (for dispose(), I'd argue that usually they chain up at
the end of their own code). If they chain up at the end, this has no
effect.

This only really matters if you try to register GWeakRef during dipose
and/or resurrect the object.

        static void dispose(GObject *object)
        {
            g_weak_ref_set(&global_weak_ref, object);
            global_ref = g_object_ref(object);
            G_OBJECT_CLASS (parent_class)->dispose (object);
        }

the object was resurrected, but g_object_real_dispose() would clear the
weak ref. That is not desirable, nor does it make sense.

Instead, the virtual function dispose() is called from two places, from
g_object_unref() and g_object_run_dispose(). In both cases, it is
ensured that weak locations are cleared *after* dispatching the virtual
function. Don't do it somewhere in the middle from
g_object_real_dispose().
2023-12-30 00:20:17 +01:00
Thomas Haller
408dc69186 gobject: fix race in g_object_unref() related to toggle references
The previous g_object_unref() was racy. There were three places where we
decremented the ref count, but still accessed the object afterwards
(while assuming that somebody else might still hold a reference). For
example:

      if (!g_atomic_int_compare_and_exchange_full ((int *) &object->ref_count,
                                                   old_ref, old_ref - 1,
                                                   &old_ref))
        continue;

      TRACE (GOBJECT_OBJECT_UNREF (object, G_TYPE_FROM_INSTANCE (object), old_ref));

      /* if we went from 2->1 we need to notify toggle refs if any */
      if (old_ref == 2 && OBJECT_HAS_TOGGLE_REF (object))
        {
          /* The last ref being held in this case is owned by the toggle_ref */
          toggle_refs_notify (object, TRUE);
        }

After we decrement the reference count (and gave up our reference), we
are only allowed to access object if we know we have the only possible
reference to it. In particular, if old_ref is larger than 1, then
somebody else holds references and races against destroying object.
The object might be a dangling pointer already.

This is slightly complicated due to toggle references and clearing of
weak-locations.

For toggle references, we must take a lock on the mutex. Luckily, that
is only necessary, when the current reference count is exactly 2.

Note that we emit the TRACE() after the ref count was already decreased.
If another thread unrefs the object, inside the TRACE() we might have a
dangling pointer. That would only be fixable, by emitting the TRACE()
before the actual unref (which has its own problems). This problem
already existed previously.

The change to the test is necessary and correct. Before this patch,
g_object_unref() would call dispose() and decrement the reference count
right after.
In the test case at gobject/tests/reference.c:1108, the reference count
after dispose and decrement is 1. Then it thaws the queue notification,
which emits a property changed signal. The test then proceeds to
reference the object again and notifying the toggle reference.
Previously, the toggle reference was notified 3 times.
After this change, the property changed signal is emitted before
decreasing the reference count. Taking a reference then does not cause
an additional toggle on+off, so in total only one toggle happens.
That accounts for the change in the test. The new behavior is
correct.
2023-12-28 09:36:56 +01:00
Emmanuele Bassi
4a270c947c tests: Check thread safety of GParamSpecPool
Aside from checking that we're accessing the global GParamSpecPool
without necessarily initializing GObjectClass, we should also verify
that we're doing so safely without the class init lock.
2023-12-19 22:48:03 +00:00
Emmanuele Bassi
62477118e7 tests: Verify GParamSpecPool creation
Ensure that the fix in commit af024b6d7e7d3fbef23c1f7d1f7704fc90ac4fb1
works, by replicating what gobject-introspection does:

- initialise the default GTypeInterface from a GType without also
  initialising GObjectClass
  - install a property for the interface
- find the GParamSpec using g_object_interface_find_property()
2023-12-19 19:53:48 +00:00
Emmanuele Bassi
540ba432fb tests: Add unit for GParamSpecPool
Check that the API contract is respected, even though nobody should be
using GParamSpecPool to implement properties outside of GObject.
2023-12-19 19:53:48 +00:00
Lukáš Tyrychtr
a473d5aea0 mkenums: Allow , in a character literal
This required adding a higher precedence character literal choice.

Fixes #3103
2023-11-21 11:54:42 +00:00
Philip Withnall
e87f8e9c5a Merge branch '3158-info-critical' into 'main'
gio-tool-info: Fix critical warning when --attributes are specified and add basic unit tests

Closes #3158

See merge request GNOME/glib!3684
2023-11-07 14:37:11 +00:00
Philip Withnall
077104e63b tests: Run processes under test with G_DEBUG=fatal-warnings
Modify all the similar Python test wrappers to set
`G_DEBUG=fatal-warnings` in the environment of the program being tested,
so we can catch unexpected warnings/criticals.

Adding this because I noticed it was missing, not because I noticed a
warning/critical was being ignored.

Signed-off-by: Philip Withnall <philip@tecnocode.co.uk>
2023-11-07 13:51:56 +00:00
Alexander Slobodeniuk
7880d87091 gvalue: add g_value_steal_string()
This call is needed to avoid an extra copy after
serialization of the data.
2023-11-04 10:40:30 +00:00
Kleis Auke Wolthuizen
9ec6c19342 gsignalgroup: Avoid function call with side effect in g_return_* macro 2023-11-01 10:53:55 +00:00
Damien Zammit
f25a9ca10c Initial test of Hurd CI - (run_tests.sh status ignored) 2023-10-18 23:33:04 +00:00
Alex Richardson
5ecd3cbe52 gobject: use g_once_init_enter_pointer for GType initializers
GType is either an integer or a pointer, so we have to use the _pointer
version here to support architectures such as Morello.

Helps: https://gitlab.gnome.org/GNOME/glib/-/issues/2842
See also: https://gitlab.gnome.org/GNOME/glib/-/merge_requests/3578
2023-10-04 14:50:54 +01:00
Christian Hergert
eb8a33625e gobject: Separate GWeakRef from GWeakNotify
This patch is based upon Garrett Regier's work from 2015 to provide
some reliability and predictability to how disposal handles weak
reference state.

A primary problem is that GWeakRef and GWeakNotify state is shared and
therefore you cannot rely on GWeakRef status due to a GWeakNotify
calling into second-degree code.

It's important to ensure that both weak pointer locations and GWeakRef
will do the proper thing before user callbacks are executed during
disposal. Otherwise, user callbacks cannot rely on the status of their
weak pointers. That would be mostly okay but becomes an issue when
second degree objects are then disposed before any notification of
dependent object disposal.

Consider objects A and B.

`A` contains a reference to `B` and `B` contains a `GWeakRef` to `A`.
When `A` is disposed, `B` may be disposed as a consequence but has not
yet been notified that `A` has been disposed. It's `GWeakRef` may also
cause liveness issues if `GWeakNotify` on `A` result in tertiary code
running which wants to interact with `B`.

This example is analagous to how `GtkTextView` and `GtkTextBuffer` work
in text editing applications.

To provide application and libraries the ability to handle this using
already existing API, `GWeakRef` is separated into it's own GData quark
so that weak locations and `GWeakRef` are cleared before user code is
executed as a consequence of `GData` cleanup.

# Conflicts:
#	gobject/tests/signals.c
2023-08-25 14:25:47 -07:00
Philip Withnall
c2df874e0b tests: Add some more tests for g_type_query()
Signed-off-by: Philip Withnall <pwithnall@endlessos.org>
2023-07-30 13:51:00 +03:00
Philip Withnall
d9b23c5466 gtype: Allow g_type_query() to be called on dynamic types
There’s no reason that anyone can think of that this should be
disallowed. It’s useful for language runtimes like GJS to be able to
find out the allocation size of dynamic GObjects.

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

Fixes: #623
2023-07-30 11:25:42 +03:00
Chun-wei Fan
cb1eb57581 gobject tests: Fix running custom-dispatch on 32-bit Windows
UAC will terminate this test program from running in 32-bit x86 builds as
it believes that it will alter Windows.  In order to make this run, we
create a manifest file for 32-bit Windows builds in order to tell UAC
that this program should not need admin privileges.

This will allow the entire test suite for GLib to run on 32-bit Windows
builds.
2023-06-30 11:32:54 +08:00
Philip Withnall
5c50aec060 Merge branch 'glib-mkenums_parse_trigraph' into 'main'
Take double-quote characters into account when parsing trigraph

Closes #65

See merge request GNOME/glib!3452
2023-05-30 14:45:54 +00:00
Emmanuel Fleury
7e5607d534 Take double-quote characters into account when parsing trigraph
For now, the function parse_trigraph() defined in gobject/glib-mkenums
script was not taking double-quotes characters into account:

>>> parse_trigraph('name="eek, a comma"')
{'name': '"eek', 'a': None}

This patch take double-quotes characters into account:

>>> parse_trigraph('name="eek, a comma"')
{'name': 'eek, a comma'}

Closes issue #65
2023-05-30 15:22:05 +02:00
Marco Trevisan (Treviño)
bfc599b8a2 gsignal: Reduce lock/unlock operations when calling signal_emit_unlocked_R
We used to call this function as unlocked, with a node value that
could be invalid at the point of the call, so let's ensure that when
we call such function it's defined, and then reduce the access to the
signal node members when we're unlocked or after a lock/unlock operation
that may have changed it.

As per this, add more tests handling multiple signal hooks cases that we
did not cover before.
2023-05-30 13:52:08 +01:00
Marco Trevisan (Treviño)
0bc725d4fe gobject/tests/signals: Add tests for g_object_emitv
It's very much used by bindings but we didn't really test it locally.
2023-05-30 13:52:07 +01:00
Eric Blake
2ab2ce57e6 gtestutils: Improve g_assert_cmpuint
While x86_64 has enough precision in long double to do a round trip
from guint64 to long double and back, this is platform-specific, and
is a disservice to users trying to debug failing unit tests on other
architectures where it loses precision for g_assert_cmp{int,uint,hex}.
See also https://bugzilla.gnome.org/show_bug.cgi?id=788385 which
mentions having to add casts to specifically silence the compiler on
platforms where the precision loss occurs.

Meanwhile, g_assert_cmpuint() does an unsigned comparison, but outputs
signed values if the comparison fails, which is confusing.

Fix both issues by introducing a new g_assertion_message_cmpint()
function with a new 'u' numtype.  For backwards compatibility, the
macros still call into the older g_assertion_message_cmpnum() when not
targetting 2.78, and that function still works when passed 'i' and 'x'
types even though code compiled for 2.78 and later will never invoke
it with numtype anything other than 'f'.  Note that g_assert_cmpmem
can also take advantage of the new code, even though in practice,
comparison between two size_t values representing array lengths that
can actually be compiled is unlikely to have ever hit the precision
loss.  The macros in signals.c test code does not have to worry about
versioning, since it is not part of the glib library proper.

Closes #2997
Signed-off-by: Eric Blake <eblake@redhat.com>
2023-05-09 08:28:09 -05:00
Przemyslaw Gorszkowski
74d49e447f Test g_signal_handlers_disconnect_matched for G_SIGNAL_MATCH_ID match
Signed-off-by: Przemyslaw Gorszkowski <pgorszkowski@igalia.com>
2023-04-18 12:50:55 +02:00
Przemyslaw Gorszkowski
b264585f3c gsignal: Support G_SIGNAL_MATCH_ID in g_signal_handlers_block/unblock/disconnect_matched()
Calling g_signal_handlers_block/unblock/disconnect_matched with only G_SIGNAL_MATCH_ID
do not match any handlers and return 0.

Fixes: #2980

Signed-off-by: Przemyslaw Gorszkowski <pgorszkowski@igalia.com>
2023-04-14 15:27:11 +01:00
Philip Withnall
2d8e38c00d gsignal: Clarify documentation for GSignalMatchType matching
The use of ‘OR’ in the existing documentation suggests that the matching
is disjunctive, but it’s actually conjunctive. Clarify that in the
documentation and add a test.

Spotted while reviewing
https://gitlab.gnome.org/GNOME/glib/-/merge_requests/3376.

Signed-off-by: Philip Withnall <pwithnall@endlessos.org>
2023-04-14 13:55:20 +01:00
Philip Withnall
d1eb9c840c tests: Add type flag tests for interfaces
Signed-off-by: Philip Withnall <pwithnall@endlessos.org>

Helps: #252
2023-04-13 15:39:37 +01:00
Philip Withnall
3d7e88a77d build: Drop old .gitignore files from test directories
They just listed built files. Since the move to Meson, these are all
kept in a separate build directory, not the source tree, so don’t need
to be ignored.

Signed-off-by: Philip Withnall <pwithnall@endlessos.org>
2023-03-20 12:09:02 +00:00
Philip Withnall
0e84e28978 tests: Don’t run the GObject performance tests under valgrind
They take too long and time out, and are not particularly useful to run
under valgrind because they aren’t designed to test code coverage.

Signed-off-by: Philip Withnall <pwithnall@endlessos.org>
2023-02-16 12:52:30 +00:00
Marco Trevisan (Treviño)
1594cf77eb meson: Use exitcode protocol for some c++ and gobject definition tests
In all these cases we don't really care about running the test file,
while building and basic execution it is relevant.

Also they don't support TAP at all.
2023-01-17 21:08:48 +01:00
Marco Trevisan (Treviño)
58031feb17 meson: Use 'tap' test protocol by default
Meson supports tap protocol results parsing, allowing us to track better
the tests that are running (and the ones that are actually skipped) without
manually parsing the test output.

However this also implies that using the verbose mode for a test doesn't
show its output by default (unless there are failures).
2023-01-17 21:08:48 +01:00
Philip Withnall
251bab3e71 gobject: Add a missing NULL check for the return from lookup_type_node_I()
This can cause a `NULL` dereference on the next line if there is no
`TypeNode` for `iface_type`, for example if `iface_type ==
G_TYPE_INVALID`.

Unlikely, but possible since this API is public.

Spotted by Coverity.

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

Coverity CID: #1501602
2022-12-21 19:16:10 +00:00
Marco Trevisan (Treviño)
bc59ed8fba gobject: Avoid the ref/unref dance if assigning the same to a value
g_value_set_object could lead to perform unneeded ref/unref operations in
case we were trying to set again an object to a GValue
2022-12-20 17:48:21 +01:00
Marco Trevisan (Treviño)
e9979b540f meson: Add test to check for strict cast alignments if supported 2022-12-16 20:48:47 +01:00
Thomas Haller
938a1caf89 gtype: avoid "-Wcast-align" warning with optimized G_TYPE_CHECK_INSTANCE_CAST()
We can get a "-Wcast-align", if the target type that we cast to ("ct") has a
larger alignment than GTypeInstance.

That can happen on i686 architecture, if the GObject type has larger
alignment than the parent struct (or GObject). Since on i686, embeding
a "long long" or a "long double" in a struct still does not increase
the alignment beyond 4 bytes, this usually only happens when using the
__attribute__() to increase the alignment (or to have a field that has
the alignment increased).

It can happen on x86_64 when having a "long double" field.

The compiler warning is hard to avoid but not very useful, because it purely
operates on the pointer types at compile time. G_TYPE_CHECK_INSTANCE_CAST()
instead asserts (in non-optimized mode) that the pointer really points
to the expected GTypeInstance (and if that's the case, then the alignment
should be suitable already).

This is like in commit ed553e8e30 ('gtype: Eliminate -Wcast-align warnings
with G_TYPE_CHECK_INSTANCE_CAST'). But also fix the optimized code path.

With the unpatched G_TYPE_CHECK_INSTANCE_CAST() macro, the unit test would
now show the problem (with gcc-9.3.1-2.fc30.i686 or
gcc-12.2.1-4.fc37.x86_64):

  $ export G_DISABLE_CAST_CHECKS=1
  $ export CFLAGS='-Wcast-align=strict'
  $ meson build
  $ ninja -C build
  ...
  In file included from ../gobject/gobject.h:26,
                   from ../gobject/gbinding.h:31,
                   from ../glib/glib-object.h:24,
                   from ../gobject/tests/objects-refcount1.c:2:
  ../gobject/tests/objects-refcount1.c: In function ‘my_test_dispose’:
  ../gobject/gtype.h:2523:42: warning: cast increases required alignment of target type [-Wcast-align]
   2523 | #  define _G_TYPE_CIC(ip, gt, ct)       ((ct*) ip)
        |                                          ^
  ../gobject/gtype.h:517:66: note: in expansion of macro ‘_G_TYPE_CIC’
    517 | #define G_TYPE_CHECK_INSTANCE_CAST(instance, g_type, c_type)    (_G_TYPE_CIC ((instance), (g_type), c_type))
        |                                                                  ^~~~~~~~~~~
  ../gobject/tests/objects-refcount1.c:9:37: note: in expansion of macro ‘G_TYPE_CHECK_INSTANCE_CAST’
      9 | #define MY_TEST(test)              (G_TYPE_CHECK_INSTANCE_CAST ((test), G_TYPE_TEST, GTest))
        |                                     ^~~~~~~~~~~~~~~~~~~~~~~~~~
  ../gobject/tests/objects-refcount1.c:96:10: note: in expansion of macro ‘MY_TEST’
     96 |   test = MY_TEST (object);
        |          ^~~~~~~
2022-12-16 20:48:06 +01:00
Marco Trevisan (Treviño)
c6f252108c gobject/tests/performance: Add object get/set performance tests 2022-12-14 03:05:50 +01:00
Marco Trevisan (Treviño)
77a2d26ea2 gobject/tests/performance: Add object notify performance tests 2022-12-14 03:05:50 +01:00
Marco Trevisan (Treviño)
6a0591f06c gobject/tests/type-flags: Add tests for type final type flags
Ensure that final flag is properly checked and used.
2022-12-12 19:03:48 +01:00
Marco Trevisan (Treviño)
0918ce013a gobject: Do not call toggle down notifications if current refcount is not 1
When an object is revitalized and a notify callbacks increased the reference
counter of the object, we are calling the toggle notifier twice, while it
should only happen if also the actual reference count value is 1 (after
having been decremented from 2).
2022-12-06 04:38:26 +01:00
Marco Trevisan (Treviño)
1f852863ec gobject: Check for toggle references only if the old ref is relevant
If an object gets revitalized during the dispose vfunc, we need to call
toggle refs notifiers only if we had 2 references and if the object has
the toggle references enabled.

This may change in case an object notifier handler changes this status,
so do this check only after we've called the notifiers so that in case
toggle notifications are enabled afterwards we still call the handlers.
2022-12-06 04:28:31 +01:00
Marco Trevisan (Treviño)
5e2b288033 gobject/tests/reference: Add test for notify during dispose
We need to check whether notifications and toggle references are working
properly if an object gets revitalized during the dispose vfunc.
2022-12-06 04:28:31 +01:00
Marco Trevisan (Treviño)
ea0c4d45b2 gobject/tests/reference: Add test for toggle reference up/down during dispose 2022-12-06 03:32:51 +01:00
Simon McVittie
b13e12b363 type-flags test: Force G_ENABLE_DIAGNOSTIC=1 to be set
Otherwise this test will succeed at build-time, but will fail when run
as an as-installed test via ginsttest-runner.

Signed-off-by: Simon McVittie <smcv@collabora.com>
2022-11-14 12:36:32 +00:00
Matthias Clasen
fd0dd9e93c gobject: Add G_TYPE_FLAG_DEPRECATED
This can be used to mark entire types as deprecated,
and trigger a warning when they are instantiated
and `G_ENABLE_DIAGNOSTIC=1` is set in the environment.

There's currently no convenient macros for defining
types with the new flag, but you can do:

```c
_G_DEFINE_TYPE_EXTENDED_BEGIN (GtkAppChooserWidget,
                               gtk_app_chooser_widget,
                               GTK_TYPE_WIDGET,
                               G_TYPE_FLAG_DEPRECATED)
...
_G_DEFINE_TYPE_EXTENDED_END ()
```

Includes a unit test by Philip Withnall.
2022-11-09 12:07:31 +00:00