From e298f1a078e7c2268e79430b7e812ab73324e6b1 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Mon, 9 Oct 2023 23:17:19 +0100 Subject: [PATCH] docs: Move floating refs documentation to Markdown And add some new sections on strategies for avoiding designing APIs around floating refs. Helps: #3037 --- docs/reference/gobject/floating-refs.md | 128 ++++++++++++++++++++++++ docs/reference/gobject/gobject.toml.in | 1 + docs/reference/gobject/meson.build | 1 + gobject/gobject.c | 80 --------------- 4 files changed, 130 insertions(+), 80 deletions(-) create mode 100644 docs/reference/gobject/floating-refs.md diff --git a/docs/reference/gobject/floating-refs.md b/docs/reference/gobject/floating-refs.md new file mode 100644 index 000000000..62a54eae3 --- /dev/null +++ b/docs/reference/gobject/floating-refs.md @@ -0,0 +1,128 @@ +Title: Floating references + +# Floating references + +**Note**: Floating references are a C convenience API and should not be used +in modern GObject code. Language bindings in particular find the concept +highly problematic, as floating references are not identifiable through +annotations, and neither are deviations from the floating reference +behavior, like types that inherit from [class@GObject.InitiallyUnowned] and +still return a full reference from [`id@g_object_new`]. + +[class@GObject.InitiallyUnowned] is derived from [class@GObject.Object]. The +only difference between the two is that the initial reference of a +`GInitiallyUnowned` is flagged as a "floating" reference. This means that it +is not specifically claimed to be "owned" by any code portion. The main +motivation for providing floating references is C convenience. In +particular, it allows code to be written as: + +```c +container = create_container (); +container_add_child (container, create_child()); +``` + +If `container_add_child()` calls [`method@GObject.Object.ref_sink`] on the +passed-in child, no reference of the newly created child is leaked. Without +floating references, `container_add_child()` can only acquire a reference +the new child, so to implement this code without reference leaks, it would +have to be written as: + +```c +Child *child; +container = create_container (); +child = create_child (); +container_add_child (container, child); +g_object_unref (child); +``` + +The floating reference can be converted into an ordinary reference by +calling `g_object_ref_sink()`. For already sunken objects (objects that +don't have a floating reference anymore), `g_object_ref_sink()` is +equivalent to [`method@GObject.Object.ref`] and returns a new reference. + +Since floating references are useful almost exclusively for C convenience, +language bindings that provide automated reference and memory ownership +maintenance (such as smart pointers or garbage collection) should not expose +floating references in their API. The best practice for handling types that +have initially floating references is to immediately sink those references +after `g_object_new()` returns, by checking if the `GType` inherits from +`GInitiallyUnowned`. For instance: + +```c +GObject *res = g_object_new_with_properties (gtype, + n_props, + prop_names, + prop_values); + +// or: if (g_type_is_a (gtype, G_TYPE_INITIALLY_UNOWNED)) +if (G_IS_INITIALLY_UNOWNED (res)) + g_object_ref_sink (res); + +return res; +``` + +Some object implementations may need to save an object's floating state +across certain code portions (an example is `GtkMenu` in GTK3), to achieve +this, the following sequence can be used: + +```c +// save floating state +gboolean was_floating = g_object_is_floating (object); +g_object_ref_sink (object); +// protected code portion + +... + +// restore floating state +if (was_floating) + g_object_force_floating (object); +else + g_object_unref (object); // release previously acquired reference +``` + +## Replacing floating references with annotations + +You should avoid basing your object hierarchy on floating references, as +they are hard to understand even in C, and introduce additional limitations +when consuming a GObject-based API in other languages. + +One way to express the ownership transfer between ‘container’ and ‘child’ +instances is to use ownership transfer annotations in your documentation and +introspection data. For instance, you can implement this pattern: + +```c +container_add_child (container, create_child ()); +``` + +without leaking by having `container_add_child()` defined as: + +```c +/** + * container_add_child: + * @self: a container + * @child: (transfer full): the child to add to the container + * + * ... + */ +void +container_add_child (Container *container, + Child *child) +{ + container->children = g_list_append (container->children, child); +} +``` + +The container does not explicitly acquire a reference on the child; instead, +the ownership of the child is transferred to the container. The transfer +annotation will be used by language bindings to ensure that there are no +leaks; and documentation tools will explicitly note that the callee now owns +the value passed by the caller. + +## Replacing floating references with weak references + +Another option for replacing floating references is to use weak references +in place of strong ones. A ‘container’ can acquire a weak reference on the +‘child’ instance by using [`method@GObject.Object.weak_ref`]. Once the +child instance loses its last strong reference, the container holding a +weak reference is notified, and it can either remove the child from its +internal list, or turn a weak reference into a strong one. diff --git a/docs/reference/gobject/gobject.toml.in b/docs/reference/gobject/gobject.toml.in index 8f24462bf..6dc8a6b79 100644 --- a/docs/reference/gobject/gobject.toml.in +++ b/docs/reference/gobject/gobject.toml.in @@ -42,6 +42,7 @@ show_class_hierarchy = true urlmap_file = "urlmap.js" # The same order will be used when generating the index content_files = [ + "floating-refs.md", "boxed.md", "enum-types.md", ] diff --git a/docs/reference/gobject/meson.build b/docs/reference/gobject/meson.build index 136f5a5b9..d981c1482 100644 --- a/docs/reference/gobject/meson.build +++ b/docs/reference/gobject/meson.build @@ -73,6 +73,7 @@ endif expand_content_files = [ 'boxed.md', 'enum-types.md', + 'floating-refs.md', ] gobject_gir = meson.current_source_dir() / 'GObject-2.0.gir' diff --git a/gobject/gobject.c b/gobject/gobject.c index 96aeab22c..e69b91747 100644 --- a/gobject/gobject.c +++ b/gobject/gobject.c @@ -54,86 +54,6 @@ * GObjects and their methods, see the [GType conventions][gtype-conventions]. * For the high-level concepts behind GObject, read [Instantiatable classed types: * Objects][gtype-instantiatable-classed]. - * - * ## Floating references # {#floating-ref} - * - * **Note**: Floating references are a C convenience API and should not be - * used in modern GObject code. Language bindings in particular find the - * concept highly problematic, as floating references are not identifiable - * through annotations, and neither are deviations from the floating reference - * behavior, like types that inherit from #GInitiallyUnowned and still return - * a full reference from g_object_new(). - * - * GInitiallyUnowned is derived from GObject. The only difference between - * the two is that the initial reference of a GInitiallyUnowned is flagged - * as a "floating" reference. This means that it is not specifically - * claimed to be "owned" by any code portion. The main motivation for - * providing floating references is C convenience. In particular, it - * allows code to be written as: - * - * |[ - * container = create_container (); - * container_add_child (container, create_child()); - * ]| - * - * If container_add_child() calls g_object_ref_sink() on the passed-in child, - * no reference of the newly created child is leaked. Without floating - * references, container_add_child() can only g_object_ref() the new child, - * so to implement this code without reference leaks, it would have to be - * written as: - * - * |[ - * Child *child; - * container = create_container (); - * child = create_child (); - * container_add_child (container, child); - * g_object_unref (child); - * ]| - * - * The floating reference can be converted into an ordinary reference by - * calling g_object_ref_sink(). For already sunken objects (objects that - * don't have a floating reference anymore), g_object_ref_sink() is equivalent - * to g_object_ref() and returns a new reference. - * - * Since floating references are useful almost exclusively for C convenience, - * language bindings that provide automated reference and memory ownership - * maintenance (such as smart pointers or garbage collection) should not - * expose floating references in their API. The best practice for handling - * types that have initially floating references is to immediately sink those - * references after g_object_new() returns, by checking if the #GType - * inherits from #GInitiallyUnowned. For instance: - * - * |[ - * GObject *res = g_object_new_with_properties (gtype, - * n_props, - * prop_names, - * prop_values); - * - * // or: if (g_type_is_a (gtype, G_TYPE_INITIALLY_UNOWNED)) - * if (G_IS_INITIALLY_UNOWNED (res)) - * g_object_ref_sink (res); - * - * return res; - * ]| - * - * Some object implementations may need to save an objects floating state - * across certain code portions (an example is #GtkMenu), to achieve this, - * the following sequence can be used: - * - * |[ - * // save floating state - * gboolean was_floating = g_object_is_floating (object); - * g_object_ref_sink (object); - * // protected code portion - * - * ... - * - * // restore floating state - * if (was_floating) - * g_object_force_floating (object); - * else - * g_object_unref (object); // release previously acquired reference - * ]| */ /* --- macros --- */