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 738122 - GjsDBusImplementation: unref the invocation passed to method_call()
GjsDBusImplementation: unref the invocation passed to method_call()
Status: RESOLVED FIXED
Product: gjs
Classification: Bindings
Component: general
unspecified
Other All
: Normal normal
: ---
Assigned To: gjs-maint
gjs-maint
Depends on:
Blocks:
 
 
Reported: 2014-10-07 23:16 UTC by Owen Taylor
Modified: 2014-10-28 01:23 UTC
See Also:
GNOME target: ---
GNOME version: ---


Attachments
GjsDBusImplementation: unref the invocation passed to method_call() (1.01 KB, patch)
2014-10-07 23:16 UTC, Owen Taylor
needs-work Details | Review
GjsDBusImplementation: unref the invocation passed to method_call() (1.22 KB, patch)
2014-10-09 19:11 UTC, Owen Taylor
committed Details | Review

Description Owen Taylor 2014-10-07 23:16:24 UTC
The method_call() virtual function unexpectedly expects the caller to
take ownership of the passed-in GDBusMethodInvocation.
Comment 1 Owen Taylor 2014-10-07 23:16:27 UTC
Created attachment 288018 [details] [review]
GjsDBusImplementation: unref the invocation passed to method_call()
Comment 2 Giovanni Campagna 2014-10-08 00:45:23 UTC
Review of attachment 288018 [details] [review]:

It would be good to update the glib documentation to reflect this.
Comment 3 Jasper St. Pierre (not reading bugmail) 2014-10-08 00:58:46 UTC
Review of attachment 288018 [details] [review]:

This seems wrong to me. g_dbus_method_invocation_return_value is the thing that unrefs it.
Comment 4 Jasper St. Pierre (not reading bugmail) 2014-10-08 01:01:28 UTC
Isn't g_dbus_method_invocation_return_value / return_bus_error supposed to be the thing that does the unref? It's an error to forget to call that.
Comment 5 Giovanni Campagna 2014-10-08 01:24:39 UTC
(In reply to comment #4)
> Isn't g_dbus_method_invocation_return_value / return_bus_error supposed to be
> the thing that does the unref? It's an error to forget to call that.

But it doesn't anymore, because we correctly protect it from eating a value that can be potentially owned by JS only.
Comment 6 Jasper St. Pierre (not reading bugmail) 2014-10-08 01:32:27 UTC
Huh? Where do we do that?
Comment 7 Giovanni Campagna 2014-10-08 02:58:07 UTC
(In reply to comment #6)
> Huh? Where do we do that?

In function.cpp, we recognize that the instance ownership transfer is EVERYTHING, so we reference the argument one extra time (so that the C function will not eat our reference)
Comment 8 Owen Taylor 2014-10-08 03:05:21 UTC
The return_value invocation is:

 * g_dbus_method_invocation_return_value:
 * @invocation: (transfer full): A #GDBusMethodInvocation.
 * @parameters: (allow-none): A #GVariant tuple with out parameters 

Since the ref-eating is in the annotation, language bindings will balance it out and the object won't get an extra ref.

You could say that this annotation is wrong - but it does seem to me that the extra reference count is meant to be a convenience for C programmers - if you were writing this strictly for language bindings, there would be no point in doing it - so it's probably better annotated away.
Comment 9 Owen Taylor 2014-10-08 03:05:58 UTC
(In reply to comment #8)
> The return_value invocation is:
> 
>  * g_dbus_method_invocation_return_value:
>  * @invocation: (transfer full): A #GDBusMethodInvocation.
>  * @parameters: (allow-none): A #GVariant tuple with out parameters 
> 
> Since the ref-eating is in the annotation, language bindings will balance it
> out and the object won't get an extra ref.
                                        ^^^^^
                                        unref
Comment 10 Jasper St. Pierre (not reading bugmail) 2014-10-08 03:20:46 UTC
As I understand it, from the C side, it's supposed to work like this:

  Get a GDBusMethodInvocation in your method_call with a refcount of 1.
  You somehow hang onto it, and then call g_dbus_method_invocation_return_value, which eats the ref and destroys it.

With GJS added, it looks like this:

  libgjs-private gets a GDBusMethodInvocation with a refcount of 1. It emits a signal.
  To handle the signal, GJS creates a proxy wrapper for the object, which bumps the refcount up to 2 and adds a toggle ref.
  JS code shoves the GDBusMethodInvocation around, and eventually ends up calling return_value. This doesn't add any refs since it's (transfer full) and bumps the proxy wrapper refcount back to 1, which activates the toggle ref and unroots the object.
  GC eventually collects the method invocation.

So why is your patch needed, Owen?
Comment 11 Giovanni Campagna 2014-10-08 06:21:53 UTC
(In reply to comment #10)
> As I understand it, from the C side, it's supposed to work like this:
> 
>   Get a GDBusMethodInvocation in your method_call with a refcount of 1.
>   You somehow hang onto it, and then call
> g_dbus_method_invocation_return_value, which eats the ref and destroys it.
> 
> With GJS added, it looks like this:
> 
>   libgjs-private gets a GDBusMethodInvocation with a refcount of 1. It emits a
> signal.
>   To handle the signal, GJS creates a proxy wrapper for the object, which bumps
> the refcount up to 2 and adds a toggle ref.
>   JS code shoves the GDBusMethodInvocation around, and eventually ends up
> calling return_value. This doesn't add any refs since it's (transfer full) and
> bumps the proxy wrapper refcount back to 1, which activates the toggle ref and
> unroots the object.

Your analysis is wrong here. gjs will add one ref exactly because it is (transfer full). So the proxy wrapper will have a refcount of 3 at the beginning of the return_value() call, and 2 at the end (and that keeps the object rooted forever).

>   GC eventually collects the method invocation.
> 
> So why is your patch needed, Owen?
Comment 12 Owen Taylor 2014-10-09 19:11:16 UTC
Created attachment 288171 [details] [review]
GjsDBusImplementation: unref the invocation passed to method_call()

Here's a version with an updated commit message that describes what is
going on better and references a GLib bug I filed that updates the
GDBus documentation/annotation to clarify what is going on.

===

In C code, the reference passed to the method_call() virtual function
would be passed to the return_value/error function that is eventually
called. But since these functions are called from JS, and the calling
convention is normalized, we need to unref reference passed to
method_call ourselves. See GLib bug 738259 for a more complete discussion.
Comment 13 Jasper St. Pierre (not reading bugmail) 2014-10-09 19:44:28 UTC
Review of attachment 288171 [details] [review]:

OK.
Comment 14 Owen Taylor 2014-10-28 01:23:07 UTC
Attachment 288171 [details] pushed as 152c679 - GjsDBusImplementation: unref the invocation passed to method_call()