After an evaluation, GNOME has moved from Bugzilla to GitLab. Learn more about GitLab.
No new issues can be reported in GNOME Bugzilla anymore.
To report an issue in a GNOME project, go to GNOME GitLab.
Do not go to GNOME Gitlab for: Bluefish, Doxygen, GnuCash, GStreamer, java-gnome, LDTP, NetworkManager, Tomboy.
Bug 742456 - object: Add g_steal_pointer() convenience function to mark ownership transfer
object: Add g_steal_pointer() convenience function to mark ownership transfer
Status: RESOLVED FIXED
Product: glib
Classification: Platform
Component: gobject
unspecified
Other Linux
: Normal enhancement
: ---
Assigned To: gtkdev
gtkdev
Depends on:
Blocks:
 
 
Reported: 2015-01-06 13:33 UTC by Will Manley
Modified: 2018-05-22 15:48 UTC
See Also:
GNOME target: ---
GNOME version: ---


Attachments
Work in progress patch: gobject: Add g_object_move() convenience function to mark ownership transfer (3.57 KB, patch)
2015-01-06 13:33 UTC, Will Manley
rejected Details | Review
Add new API g_take_pointer() (2.29 KB, patch)
2015-02-06 11:43 UTC, Allison Karlitskaya (desrt)
reviewed Details | Review
tests: add a test case for g_take_pointer() (1.52 KB, patch)
2015-02-06 11:43 UTC, Allison Karlitskaya (desrt)
reviewed Details | Review
Add new API g_steal_pointer() (2.57 KB, patch)
2015-02-06 14:21 UTC, Allison Karlitskaya (desrt)
committed Details | Review
tests: add a test case for g_steal_pointer() (1.64 KB, patch)
2015-02-06 14:21 UTC, Allison Karlitskaya (desrt)
committed Details | Review

Description Will Manley 2015-01-06 13:33:12 UTC
Created attachment 293921 [details] [review]
Work in progress patch: gobject: Add g_object_move() convenience function to mark ownership transfer

In combination with g_object_ref() and g_clear_object(), g_object_move()
is a convenience function to set GObject references to NULL when
transferring ownership e.g. via a call to a transfer-full function.

By using this in combination with NULL initialisation and #g_clear_object
you can be sure that whenever a GObject in your code[1] has a non-%NULL
value it has a reference to the underlying reference:

    gboolean
    example (void)
    {
      GObject* obj = NULL;
      gboolean result = FALSE;
      if (!some_possibly_failing_operation())
        goto out;
      obj = my_object_new ();
      if (!some_possibly_failing_operation())
        goto out;
      if (!transfer_full_function(g_object_ref(obj)))
        goto out;
      if (!transfer_full_function(g_move_object(&obj)))
        goto out;
      if (!some_possibly_failing_operation())
        goto out;
      result = TRUE;
    out:
      g_clear_object (obj);
      return result;
    }

This is currently only a request for comment intended to gauge interest.
I haven't even attempted to compile the code or write any tests yet, so
don't waste your time doing detailed code review. If there is interest in
having this in glib proper I'll tidy it up, write some tests and resubmit
an improved patch.
Comment 1 Colin Walters 2015-01-06 14:06:19 UTC
I do this a slightly different way:

See:

https://git.gnome.org/browse/libgsystem/tree/src/libgsystem.h#n28

Here's an example of a function which uses it:

https://git.gnome.org/browse/ostree/tree/src/libostree/ostree-repo-commit.c#n1599

This works particularly well with the local allocation macros, but doesn't depend on them.  Basically you have an additional intermediate local variable, and only set the out value when done.
Comment 2 Will Manley 2015-01-06 14:26:03 UTC
Colin: Unless I've misunderstood I think your example covers a slightly different use case to mine.  You use-case covers returning values from a function via out-arguments whereas mine is about passing objects to a function which take ownership of those objects.

You could argue some overlap in that you could use g_move_object in your implementation of gs_transfer_out_value to save one line of code but I think the use-cases are mostly separate.

I do like your technique of only setting the out arguments at the end.  I like to use that transactional style myself.  It's also useful if locking is required as you can hold locks for the shortest possible time over operations that cannot fail.
Comment 3 Allison Karlitskaya (desrt) 2015-01-06 16:16:31 UTC
Review of attachment 293921 [details] [review]:

I'm sorry to say that I don't think this very much.

From the description, I though that this would be a macro like g_set_object() that took the reference handed to it (but still handled cleaning up the existing reference in the variable).  I would probably even have said 'no' to that, and it would still be more generally useful than this.

Functions that (transfer-full) their 'in' arguments are not common and I don't really want to make them any easier to use.  Sorry :(
Comment 4 Will Manley 2015-01-06 16:58:19 UTC
Fair enough.  I find myself needing this kind of thing fairly regularly when interacting with GStreamer, but come to think of it that's mostly when passing types like GstBuffer, GstCaps, GstEvent, etc. which are all GstMiniObjects rather than GObjects.

I suppose transfer-full is more natural with a streaming-like API like in GStreamer where objects move down the pipeline rather than more traditional APIs with more usual call and return semantics.
Comment 5 Colin Walters 2015-01-06 17:01:20 UTC
It's probably also because in a performance-sensitive library like gstreamer, doing constant atomic refcount ops is very noticeable, where with transfer-in-full you don't touch the refcount.
Comment 6 Philip Withnall 2015-01-08 16:13:09 UTC
For the record, I think this is a reasonable idea, but not for the use-case of passing values to (transfer full) parameters (I agree with Ryan here — that’s a pretty niche use-case).

I think it would be useful for Colin’s (transfer full) return value use-case, but also for variable assignments which transfer ownership, e.g.:

    GObject *found_object = NULL;  /* owned */

    foreach (i in some_container until found_object != NULL) {
        GObject *element = wrap_element_and_return_ref (i);

        if (some_predicate (element)) {
            found_object = g_move_object (&element);
        }

        g_clear_object (&element);
    }

It’s a bit of a contrived example, but I have come across cases in the real world where it would be useful, typically where a variable is updated on one code path but not on another (and the code paths aren’t trivial).

That all said, this can easily be handled using g_object_ref() for all use cases except high-performance ones, as Ryan says. So g_move_object() really isn’t general purpose enough to be in GLib. I guess it could usefully be in GStreamer as gst_mini_object_move() though, or something like that.
Comment 7 Allison Karlitskaya (desrt) 2015-02-06 10:41:55 UTC
Reopening this because of comment 6.  Now that we have g_autoptr() this functionality suddenly seems extremely useful.

I'd prefer if we called it g_take_pointer(), though.  It has nothing to do with GObject in any way and 'take' is the name we typically use for reference-stealing in our platform.
Comment 8 Allison Karlitskaya (desrt) 2015-02-06 11:43:28 UTC
Created attachment 296263 [details] [review]
Add new API g_take_pointer()

This is particularly nice when used with g_autoptr().  See examples in
the docs.
Comment 9 Allison Karlitskaya (desrt) 2015-02-06 11:43:55 UTC
Created attachment 296264 [details] [review]
tests: add a test case for g_take_pointer()

Just some basic checking to make sure it works as intended.
Comment 10 Simon McVittie 2015-02-06 11:44:54 UTC
(In reply to comment #7)
> I'd prefer if we called it g_take_pointer(), though.  It has nothing to do with
> GObject in any way and 'take' is the name we typically use for
> reference-stealing in our platform.

Isn't it more like steal than take? As far as I can see, foo_take_bar() is normally

    /**
     * foo_take_bar:
     * @bar: (transfer full): a Bar
     *
     * Set foo's contents to bar, taking ownership.
     */
    void foo_take_bar (Foo *self, Bar *bar);

whereas foo_steal_bar() is normally more like

    /**
     * foo_steal_bar:
     *
     * Remove and return foo's contents.
     *
     * Returns: (transfer full): foo's bar
     */
    Bar *foo_steal_bar (Foo *self);

(although I see from Gst docs that they sometimes use "take" for both models.)
Comment 11 Philip Withnall 2015-02-06 12:48:21 UTC
Review of attachment 296263 [details] [review]:

::: glib/gmem.h
@@ +178,3 @@
+ */
+static inline gpointer
+g_take_pointer (gpointer pp)

Shouldn’t that be (g_take_pointer) to prevent macro expansion? I realise the macro is defined later, but probably best to be safe.

@@ +191,3 @@
+/* type safety */
+#define g_take_pointer(pp) \
+  (0 ? (*(pp)) : g_take_pointer(pp))

And here too.
Comment 12 Philip Withnall 2015-02-06 12:51:39 UTC
Review of attachment 296264 [details] [review]:

It would be good to see some tests of the non-macro version too. See /object/set and /object/set-function in gobject/tests/reference.c.
Comment 13 Dan Winship 2015-02-06 13:45:23 UTC
(In reply to comment #10)
> (In reply to comment #7)
> > I'd prefer if we called it g_take_pointer(), though.

> Isn't it more like steal than take?

To further muddy the naming waters, it's basically the same operation as g_task_propagate_pointer() (where "propagate" came from g_propagate_error(), although that doesn't NULL out the original unfortunately). There was some reason I didn't use "steal" there, though I don't remember what it was. (Maybe it was just because "steal_int" and "steal_boolean" sounded silly.)
Comment 14 Allison Karlitskaya (desrt) 2015-02-06 13:52:38 UTC
(In reply to comment #11)
> Review of attachment 296263 [details] [review]:
> 
> ::: glib/gmem.h
> @@ +178,3 @@
> + */
> +static inline gpointer
> +g_take_pointer (gpointer pp)
> 
> Shouldn’t that be (g_take_pointer) to prevent macro expansion? I realise the
> macro is defined later, but probably best to be safe.

Ya.  Okay.

> 
> @@ +191,3 @@
> +/* type safety */
> +#define g_take_pointer(pp) \
> +  (0 ? (*(pp)) : g_take_pointer(pp))
> 
> And here too.

It's really not required here on account of being inside of the macro expansion of the same name, but it doesn't hurt either.

(In reply to comment #13)
> > Isn't it more like steal than take?
> 
> To further muddy the naming waters, it's basically the same operation as
> g_task_propagate_pointer() (where "propagate" came from g_propagate_error(),
> although that doesn't NULL out the original unfortunately). There was some
> reason I didn't use "steal" there, though I don't remember what it was. (Maybe
> it was just because "steal_int" and "steal_boolean" sounded silly.)

If I had to pick then I'd say "steal" is the more stetic of the two.
Comment 15 Allison Karlitskaya (desrt) 2015-02-06 14:21:35 UTC
Created attachment 296274 [details] [review]
Add new API g_steal_pointer()

This is particularly nice when used with g_autoptr().  See examples in
the docs.

This patch is based upon an idea (and original patch submission) from
Will Manley <will@williammanley.net>.
Comment 16 Allison Karlitskaya (desrt) 2015-02-06 14:21:45 UTC
Created attachment 296275 [details] [review]
tests: add a test case for g_steal_pointer()

Just some basic checking to make sure it works as intended.
Comment 17 Allison Karlitskaya (desrt) 2015-02-06 14:25:22 UTC
Attachment 296274 [details] pushed as e668796 - Add new API g_steal_pointer()
Attachment 296275 [details] pushed as aa68b3d - tests: add a test case for g_steal_pointer()
Comment 18 Colin Walters 2015-02-07 15:04:24 UTC
Who reviewed the this patch before commit?  Or are you counting it as an update of someone else's patch submission?
Comment 19 Allison Karlitskaya (desrt) 2015-02-08 09:33:35 UTC
I am counting it as a trivial update (ie: renaming and adding of extra parens) of the patches submitted in comment 8 and comment 9 and reviewed by Dan and Philip.
Comment 20 Allison Karlitskaya (desrt) 2015-02-08 09:34:04 UTC
Sorry -- forgot to mention Simon as well.
Comment 21 Colin Walters 2015-02-08 09:47:28 UTC
Ok, that's reasonable, thanks.
Comment 22 Fan, Chun-wei 2015-02-09 07:46:45 UTC
Hi,

I have opened up Bug 744190 as the "Add new API g_steal_pointer()" broke builds on compilers which did not have "inline" in C-mode.

With blessings, thank you!
Comment 23 Peter Bloomfield 2018-05-21 22:48:14 UTC
Comment on attachment 296274 [details] [review]
Add new API g_steal_pointer()

Is the g_steal_pointer() macro type-safe? The code snippet

    int a = 1;
    int *b = &a;
    float *c;

    c = g_steal_pointer (&b);

causes no warnings from gcc (gcc (GCC) 8.1.1 20180502 (Red Hat 8.1.1-1)). The type-safe macro for g_object_ref() uses typeof(), and https://bugzilla.gnome.org/show_bug.cgi?id=790697#c37 noted that the '0 ?...' alternative fails to raise warnings for incompatible pointer types.

The typeof version is:

#define g_steal_pointer(pp) ((__typeof__(*pp)) (g_steal_pointer) (pp))

and does correctly warn in the above case. It's a compiler extension, but glib already uses it in g_object_ref().
Comment 24 Philip Withnall 2018-05-22 08:52:33 UTC
(In reply to Peter Bloomfield from comment #23)
> Comment on attachment 296274 [details] [review] [review]
> Add new API g_steal_pointer()
> 
> Is the g_steal_pointer() macro type-safe? The code snippet
> 
>     int a = 1;
>     int *b = &a;
>     float *c;
> 
>     c = g_steal_pointer (&b);
> 
> causes no warnings from gcc (gcc (GCC) 8.1.1 20180502 (Red Hat 8.1.1-1)).
> The type-safe macro for g_object_ref() uses typeof(), and
> https://bugzilla.gnome.org/show_bug.cgi?id=790697#c37 noted that the '0
> ?...' alternative fails to raise warnings for incompatible pointer types.
> 
> The typeof version is:
> 
> #define g_steal_pointer(pp) ((__typeof__(*pp)) (g_steal_pointer) (pp))
> 
> and does correctly warn in the above case. It's a compiler extension, but
> glib already uses it in g_object_ref().

This seems like it would be a good improvement. Could you please file a new bug report about it? If you could attach a tested patch, that would get things moving a lot faster, since we’re currently quite strained on development resources. Thanks!
Comment 25 Peter Bloomfield 2018-05-22 15:48:17 UTC
https://bugzilla.gnome.org/show_bug.cgi?id=796341