diff options
author | Simon McVittie <simon.mcvittie@collabora.co.uk> | 2014-03-14 19:25:13 +0000 |
---|---|---|
committer | Simon McVittie <simon.mcvittie@collabora.co.uk> | 2014-03-26 18:20:59 +0000 |
commit | a31b0195cb9562bbedf4989b5f19b76a4b35521e (patch) | |
tree | cf1b8fd42ed36d8c854e9122771ebf77a43972da | |
parent | 4e6a4d983dae2202b9bd07cda1f2021d0a398334 (diff) |
TpProxy: rewrite async and reentrant calls
An outline of the significant differences:
* the old code used DBusGProxy, but the new code does not need a proxy
object of any sort, simplifying life-cycles
* the new code does not need to jump through hoops to push the reply
into an idle unless it is responding with an immediate error, because
GDBus always replies in an idle anyway
* old GValueArray replaced by new GVariant
* TpProxyInvokeFunc freed its GError or GValueArray argument,
TpProxyWrapperFunc does not
* the old codegen had to do the actual dbus-glib call itself because
dbus-glib's API was varargs, whereas the new codegen can rely on
tp_proxy_pending_call_v1_new() to do that on its behalf (reducing
API surface area, taking responsibility away from generated code,
and giving that responsibility to hand-written code)
* the old codegen had to generate a callback for dbus-glib to call
back into, whereas the new code can share one callback between
all possible calls
* the deprecated reentrant calls in the old codegen were open-coded,
whereas the new codegen produces a simple wrapper around the async
version
-rw-r--r-- | docs/reference/telepathy-glib/telepathy-glib-sections.txt | 6 | ||||
-rw-r--r-- | telepathy-glib/cli-channel.c | 1 | ||||
-rw-r--r-- | telepathy-glib/cli-connection.c | 1 | ||||
-rw-r--r-- | telepathy-glib/cli-misc.c | 1 | ||||
-rw-r--r-- | telepathy-glib/core-proxy.c | 44 | ||||
-rw-r--r-- | telepathy-glib/proxy-internal.h | 36 | ||||
-rw-r--r-- | telepathy-glib/proxy-methods.c | 541 | ||||
-rw-r--r-- | telepathy-glib/proxy-subclass.h | 19 | ||||
-rw-r--r-- | tools/glib-client-gen.py | 314 |
9 files changed, 289 insertions, 674 deletions
diff --git a/docs/reference/telepathy-glib/telepathy-glib-sections.txt b/docs/reference/telepathy-glib/telepathy-glib-sections.txt index f07c63f9c..8a6cac955 100644 --- a/docs/reference/telepathy-glib/telepathy-glib-sections.txt +++ b/docs/reference/telepathy-glib/telepathy-glib-sections.txt @@ -3009,11 +3009,9 @@ tp_proxy_check_interface_by_id tp_proxy_invalidate TpProxyInterfaceAddedCb <SUBSECTION> +TpProxyWrapperFunc TpProxyInvokeFunc -tp_proxy_pending_call_v0_new -tp_proxy_pending_call_v0_completed -tp_proxy_pending_call_v0_take_pending_call -tp_proxy_pending_call_v0_take_results +tp_proxy_pending_call_v1_new tp_proxy_signal_connection_v0_new tp_proxy_signal_connection_v0_take_results </SECTION> diff --git a/telepathy-glib/cli-channel.c b/telepathy-glib/cli-channel.c index a2f6226c2..8ecbc5079 100644 --- a/telepathy-glib/cli-channel.c +++ b/telepathy-glib/cli-channel.c @@ -25,5 +25,6 @@ #include <telepathy-glib/interfaces.h> #include <telepathy-glib/proxy-subclass.h> +#include <telepathy-glib/util.h> #include "_gen/tp-cli-channel-body.h" diff --git a/telepathy-glib/cli-connection.c b/telepathy-glib/cli-connection.c index cf75d7b70..51aa4bac4 100644 --- a/telepathy-glib/cli-connection.c +++ b/telepathy-glib/cli-connection.c @@ -25,5 +25,6 @@ #include <telepathy-glib/interfaces.h> #include <telepathy-glib/proxy-subclass.h> +#include <telepathy-glib/util.h> #include "_gen/tp-cli-connection-body.h" diff --git a/telepathy-glib/cli-misc.c b/telepathy-glib/cli-misc.c index f6532f84c..9aa005bf1 100644 --- a/telepathy-glib/cli-misc.c +++ b/telepathy-glib/cli-misc.c @@ -26,6 +26,7 @@ #include <telepathy-glib/interfaces.h> #include <telepathy-glib/proxy-subclass.h> +#include <telepathy-glib/util.h> #include "telepathy-glib/_gen/tp-cli-account-body.h" #include "telepathy-glib/_gen/tp-cli-account-manager-body.h" diff --git a/telepathy-glib/core-proxy.c b/telepathy-glib/core-proxy.c index 5abc6e2d0..bc8a57795 100644 --- a/telepathy-glib/core-proxy.c +++ b/telepathy-glib/core-proxy.c @@ -41,45 +41,22 @@ tp_proxy_check_interface_by_id (gpointer proxy, } TpProxyPendingCall * -tp_proxy_pending_call_v0_new (TpProxy *proxy, +tp_proxy_pending_call_v1_new (TpProxy *proxy, + gint timeout_ms, GQuark iface, const gchar *member, - DBusGProxy *iface_proxy, - TpProxyInvokeFunc invoke_callback, + GVariant *args, + const GVariantType *reply_type, + TpProxyWrapperFunc wrapper, GCallback callback, gpointer user_data, GDestroyNotify destroy, - GObject *weak_object, - gboolean cancel_must_raise) -{ - g_assert (_tp_proxy_implementation.version != NULL); - return _tp_proxy_implementation.pending_call_new (proxy, iface, member, - iface_proxy, invoke_callback, callback, user_data, destroy, - weak_object, cancel_must_raise); -} - -void -tp_proxy_pending_call_v0_take_pending_call (TpProxyPendingCall *pc, - DBusGProxyCall *pending_call) -{ - g_assert (_tp_proxy_implementation.version != NULL); - _tp_proxy_implementation.pending_call_take_pending_call (pc, pending_call); -} - -void -tp_proxy_pending_call_v0_completed (gpointer p) -{ - g_assert (_tp_proxy_implementation.version != NULL); - _tp_proxy_implementation.pending_call_completed (p); -} - -void -tp_proxy_pending_call_v0_take_results (TpProxyPendingCall *pc, - GError *error, - GValueArray *args) + GObject *weak_object) { g_assert (_tp_proxy_implementation.version != NULL); - _tp_proxy_implementation.pending_call_take_results (pc, error, args); + return _tp_proxy_implementation.pending_call_new (proxy, timeout_ms, + iface, member, args, reply_type, wrapper, + callback, user_data, destroy, weak_object); } TpProxySignalConnection * @@ -120,9 +97,6 @@ tp_private_proxy_set_implementation (TpProxyImplementation *impl) g_assert (impl->check_interface_by_id != NULL); g_assert (impl->pending_call_new != NULL); - g_assert (impl->pending_call_take_pending_call != NULL); - g_assert (impl->pending_call_take_results != NULL); - g_assert (impl->pending_call_completed != NULL); g_assert (impl->signal_connection_new != NULL); g_assert (impl->signal_connection_take_results != NULL); diff --git a/telepathy-glib/proxy-internal.h b/telepathy-glib/proxy-internal.h index ba65ddef1..8cd48a574 100644 --- a/telepathy-glib/proxy-internal.h +++ b/telepathy-glib/proxy-internal.h @@ -33,21 +33,16 @@ typedef struct { GError **); TpProxyPendingCall *(*pending_call_new) (TpProxy *, + gint, GQuark, const gchar *, - DBusGProxy *, - TpProxyInvokeFunc, + GVariant *, + const GVariantType *, + TpProxyWrapperFunc, GCallback, gpointer, GDestroyNotify, - GObject *, - gboolean); - void (*pending_call_take_pending_call) (TpProxyPendingCall *, - DBusGProxyCall *); - void (*pending_call_take_results) (TpProxyPendingCall *, - GError *, - GValueArray *); - GDestroyNotify pending_call_completed; + GObject *); TpProxySignalConnection *(*signal_connection_new) (TpProxy *, GQuark, @@ -70,25 +65,18 @@ gboolean _tp_proxy_check_interface_by_id (TpProxy *self, GQuark iface, GError **error); -TpProxyPendingCall *_tp_proxy_pending_call_new (TpProxy *self, +TpProxyPendingCall * +_tp_proxy_pending_call_v1_new (TpProxy *proxy, + gint timeout_ms, GQuark iface, const gchar *member, - DBusGProxy *iface_proxy, - TpProxyInvokeFunc invoke_callback, + GVariant *args, + const GVariantType *reply_type, + TpProxyWrapperFunc wrapper, GCallback callback, gpointer user_data, GDestroyNotify destroy, - GObject *weak_object, - gboolean cancel_must_raise); - -void _tp_proxy_pending_call_take_pending_call (TpProxyPendingCall *pc, - DBusGProxyCall *pending_call); - -void _tp_proxy_pending_call_take_results (TpProxyPendingCall *pc, - GError *error, - GValueArray *args); - -void _tp_proxy_pending_call_completed (gpointer p); + GObject *weak_object); TpProxySignalConnection *_tp_proxy_signal_connection_new (TpProxy *self, GQuark iface, diff --git a/telepathy-glib/proxy-methods.c b/telepathy-glib/proxy-methods.c index 00231cd65..7822c7024 100644 --- a/telepathy-glib/proxy-methods.c +++ b/telepathy-glib/proxy-methods.c @@ -24,6 +24,7 @@ #define DEBUG_FLAG TP_DEBUG_PROXY #include "telepathy-glib/debug-internal.h" +#include "telepathy-glib/errors.h" #include <telepathy-glib/util.h> #if 0 @@ -41,50 +42,14 @@ */ struct _TpProxyPendingCall { - /* This structure's "reference count" is implicit: - * - 1 if D-Bus has us (from creation until _completed) - * - 1 if results have come in but we haven't run the callback yet - * (idle_source is nonzero) - * - * In normal use, its life cycle should go like this: - * - Created by tp_proxy_pending_call_v0_new - * - Given to dbus-glib by generated code (actual call starts here) - * - tp_proxy_pending_call_v0_take_pending_call - * - (Phase 1) - * - tp_proxy_pending_call_v0_take_results - * - Idle handler queued - * - (Phase 2) - * - tp_proxy_pending_call_v0_completed - * - (Phase 3) - * - tp_proxy_pending_call_idle_invoke - * - tp_proxy_pending_call_free - * - * although we can't guarantee that idle_invoke won't go off before - * completed does, if the dbus-glib implementation changes. - * - * Exceptional conditions that can occur: - * - Weak object dies - * - Reference cleared, otherwise equivalent to explicit cancellation - * - Explicitly cancelled - * - All phases: callback invoked if cancel_must_raise, otherwise - * not - * - DBusGProxy destroy signal (or _completed before _take_results) - * - Phase 1: error callback queued - * - Phase 2: ignored, we use the results we've already got - * - Phase 3: ignored, we use the results we've already got - */ - - /* Always non-NULL */ TpProxy *proxy; /* Set to NULL after it's been invoked once, or if cancellation means * it should never be called. Supplied by the generated code */ - TpProxyInvokeFunc invoke_callback; + TpProxyWrapperFunc wrapper; - /* arguments for invoke_callback supplied by _take_results, by - * cancellation or by the destroy signal */ + /* error if no interface */ GError *error /* implicitly initialized */; - GValueArray *args; /* user-supplied arguments for invoke_callback */ GCallback callback; @@ -92,22 +57,8 @@ struct _TpProxyPendingCall { GDestroyNotify destroy; GObject *weak_object; - /* Non-NULL until either _completed or destroy, whichever comes first */ - DBusGProxy *iface_proxy; - DBusGProxyCall *pending_call; - - /* Nonzero if _idle_invoke has been queued (even if it has already - * happened), i.e. if results have been taken or the DBusGProxy - * was destroyed */ - guint idle_source; - - /* If TRUE, invoke the callback even on cancellation */ - unsigned cancel_must_raise:1; - - /* If TRUE, the idle_invoke callback has either run or been cancelled */ - unsigned idle_completed:1; - /* If TRUE, dbus-glib no longer holds a reference to us */ - unsigned dbus_completed:1; + /* Used to cancel the call early */ + GCancellable *cancellable; /* Marker to indicate that this is, in fact, a valid TpProxyPendingCall */ gconstpointer priv; @@ -125,175 +76,38 @@ tp_proxy_pending_call_lost_weak_ref (gpointer data, g_assert (pc->priv == pending_call_magic); g_assert (dead == pc->weak_object); - pc->weak_object = NULL; - if (!pc->idle_completed) - tp_proxy_pending_call_cancel (pc); + tp_proxy_pending_call_cancel (pc); } static gboolean -tp_proxy_pending_call_idle_invoke (gpointer p) +tp_proxy_pending_call_idle_error (gpointer p) { TpProxyPendingCall *pc = p; - TpProxyInvokeFunc invoke = pc->invoke_callback; - - MORE_DEBUG ("%p", pc); + TpProxyWrapperFunc wrapper = pc->wrapper; - if (invoke == NULL) + if (wrapper == NULL || pc->proxy == NULL || + g_cancellable_is_cancelled (pc->cancellable)) { - /* either already invoked (bug?), or cancelled */ + DEBUG ("%p: ignoring result due to invalidation, weak object " + "disappearance or cancellation", pc); return FALSE; } - MORE_DEBUG ("%p: invoking user callback", pc); + g_assert (pc->error != NULL); - g_assert (pc->proxy != NULL); - g_assert (pc->error == NULL || pc->args == NULL); - g_assert (!pc->idle_completed); + DEBUG ("%p: %s #%d: %s", pc, g_quark_to_string (pc->error->domain), + pc->error->code, pc->error->message); - pc->invoke_callback = NULL; - invoke (pc->proxy, pc->error, pc->args, pc->callback, + pc->wrapper = NULL; + wrapper (pc->proxy, pc->error, NULL, pc->callback, pc->user_data, pc->weak_object); - pc->error = NULL; - pc->args = NULL; - - /* don't clear pc->idle_source here! tp_proxy_pending_call_v0_completed - * compares it to 0 to determine whether to free the object */ + g_clear_error (&pc->error); return FALSE; } -static void _tp_proxy_pending_call_idle_completed (gpointer p); - -static void -_tp_proxy_pending_call_dgproxy_destroy (DBusGProxy *iface_proxy, - TpProxyPendingCall *pc) -{ - g_assert (iface_proxy != NULL); - g_assert (pc != NULL); - g_assert (pc->iface_proxy == iface_proxy); - g_assert (pc->proxy != NULL); - - DEBUG ("%p: DBusGProxy %p invalidated", pc, iface_proxy); - - if (pc->idle_source == 0) - { - /* we haven't already received and queued a reply, so synthesize - * one */ - g_assert (pc->args == NULL); - g_assert (pc->error == NULL); - - pc->error = g_error_new_literal (TP_DBUS_ERRORS, - TP_DBUS_ERROR_NAME_OWNER_LOST, "Name owner lost (service crashed?)"); - - pc->idle_source = g_idle_add_full (G_PRIORITY_HIGH, - tp_proxy_pending_call_idle_invoke, pc, - _tp_proxy_pending_call_idle_completed); - } - - g_signal_handlers_disconnect_by_func (pc->iface_proxy, - _tp_proxy_pending_call_dgproxy_destroy, pc); - g_object_unref (pc->iface_proxy); - pc->iface_proxy = NULL; -} - -/** - * tp_proxy_pending_call_v0_new: - * @self: a proxy - * @iface: a quark whose string value is the D-Bus interface - * @member: the name of the method being called - * @iface_proxy: the interface-specific #DBusGProxy for @iface, - * or %NULL if the call will immediately fail - * @invoke_callback: an implementation of #TpProxyInvokeFunc which will - * invoke @callback with appropriate arguments - * @callback: a callback to be called when the call completes - * @user_data: user-supplied data for the callback - * @destroy: user-supplied destructor for the data - * @weak_object: if not %NULL, a #GObject which will be weakly referenced by - * the signal connection - if it is destroyed, the pending call will - * automatically be cancelled - * @cancel_must_raise: if %TRUE, the @invoke_callback will be run with - * error %TP_DBUS_ERROR_CANCELLED if the call is cancelled by a call to - * tp_proxy_pending_call_cancel() or by destruction of the @weak_object; - * if %FALSE, the @invoke_callback will not be run at all in these cases - * - * Allocate a new pending call structure. After calling this function, the - * caller must start an asynchronous D-Bus call and give the resulting - * DBusGProxyCall to the pending call object using - * tp_proxy_pending_call_v0_take_pending_call(). - * - * If dbus-glib gets a reply to the call before it's cancelled, the caller - * must arrange for tp_proxy_pending_call_v0_take_results() to be called - * with the results (the intention is for this to be done immediately - * after dbus_g_proxy_end_call in the callback supplied to dbus-glib). - * - * When dbus-glib discards its reference to the user_data supplied in the - * asynchronous D-Bus call (i.e. after the call is cancelled or a reply - * arrives), tp_proxy_pending_call_v0_completed must be called (the intention - * is for the #TpProxyPendingCall to be the @user_data in the async call, - * and for tp_proxy_pending_call_v0_completed to be the #GDestroyNotify - * passed to the same async call). - * - * This function is for use by #TpProxy subclass implementations only, and - * should usually only be called from code generated by - * tools/glib-client-gen.py. - * - * Returns: a new pending call structure - * - * Since: 0.7.1 - */ - -/* that's implemented in the core library, but it calls this: */ - -TpProxyPendingCall * -_tp_proxy_pending_call_new (TpProxy *self, - GQuark iface, - const gchar *member, - DBusGProxy *iface_proxy, - TpProxyInvokeFunc invoke_callback, - GCallback callback, - gpointer user_data, - GDestroyNotify destroy, - GObject *weak_object, - gboolean cancel_must_raise) -{ - TpProxyPendingCall *pc; - - g_return_val_if_fail (invoke_callback != NULL, NULL); - g_return_val_if_fail ((gpointer) iface_proxy != (gpointer) self, NULL); - - pc = g_slice_new0 (TpProxyPendingCall); - - MORE_DEBUG ("(proxy=%p, if=%s, meth=%s, ic=%p; cb=%p, ud=%p, dn=%p, wo=%p)" - " -> %p", self, g_quark_to_string (iface), member, invoke_callback, - callback, user_data, destroy, weak_object, pc); - - pc->proxy = g_object_ref (self); - pc->invoke_callback = invoke_callback; - pc->callback = callback; - pc->user_data = user_data; - pc->destroy = destroy; - pc->weak_object = weak_object; - pc->iface_proxy = NULL; - pc->pending_call = NULL; - pc->priv = pending_call_magic; - pc->cancel_must_raise = cancel_must_raise; - - if (weak_object != NULL) - g_object_weak_ref (weak_object, tp_proxy_pending_call_lost_weak_ref, pc); - - if (iface_proxy != NULL) - { - pc->iface_proxy = g_object_ref (iface_proxy); - - g_signal_connect (iface_proxy, "destroy", - G_CALLBACK (_tp_proxy_pending_call_dgproxy_destroy), pc); - } - - return pc; -} - /** * tp_proxy_pending_call_cancel: * @pc: a pending call @@ -310,54 +124,16 @@ tp_proxy_pending_call_cancel (TpProxyPendingCall *pc) DEBUG ("%p", pc); g_return_if_fail (pc->priv == pending_call_magic); - g_return_if_fail (pc->proxy != NULL); - /* If the callback has already run, it's too late to cancel */ - g_return_if_fail (!pc->idle_completed); - if (pc->cancel_must_raise) - { - if (pc->error != NULL) - g_error_free (pc->error); - - pc->error = g_error_new_literal (TP_DBUS_ERRORS, - TP_DBUS_ERROR_CANCELLED, "Re-entrant D-Bus call cancelled"); - - if (pc->args != NULL) - { - tp_value_array_free (pc->args); - pc->args = NULL; - } - } - else - { - pc->invoke_callback = NULL; - } - - /* If we're calling the callback due to cancellation, we must free the - * pending call object afterwards. Otherwise, we must free the pending - * call object later anyway, in case this function was called due to - * weak refs (like fd.o #14750). */ - if (pc->idle_source == 0) - { - pc->idle_source = g_idle_add_full (G_PRIORITY_HIGH, - tp_proxy_pending_call_idle_invoke, pc, - _tp_proxy_pending_call_idle_completed); - } - - if (!pc->dbus_completed && pc->pending_call != NULL) - { - /* Implicitly asserts that iface_proxy is non-NULL */ - DBusGProxy *iface_proxy = g_object_ref (pc->iface_proxy); - - dbus_g_proxy_cancel_call (iface_proxy, pc->pending_call); - g_object_unref (iface_proxy); - } + g_cancellable_cancel (pc->cancellable); + g_clear_object (&pc->proxy); + pc->wrapper = NULL; } static void tp_proxy_pending_call_free (TpProxyPendingCall *pc) { - MORE_DEBUG ("%p", pc); + DEBUG ("%p", pc); g_assert (pc->priv == pending_call_magic); @@ -367,171 +143,166 @@ tp_proxy_pending_call_free (TpProxyPendingCall *pc) pc->destroy = NULL; pc->user_data = NULL; - if (pc->error != NULL) - g_error_free (pc->error); - - pc->error = NULL; - - if (pc->args != NULL) - tp_value_array_free (pc->args); - - pc->args = NULL; + g_clear_error (&pc->error); if (pc->weak_object != NULL) g_object_weak_unref (pc->weak_object, tp_proxy_pending_call_lost_weak_ref, pc); - if (pc->iface_proxy != NULL) - { - g_signal_handlers_disconnect_by_func (pc->iface_proxy, - _tp_proxy_pending_call_dgproxy_destroy, pc); - g_object_unref (pc->iface_proxy); - pc->iface_proxy = NULL; - } - - g_assert (pc->proxy != NULL); - g_object_unref (pc->proxy); - pc->proxy = NULL; - + g_clear_object (&pc->cancellable); + g_clear_object (&pc->proxy); g_slice_free (TpProxyPendingCall, pc); } -/** - * tp_proxy_pending_call_v0_completed: - * @p: a #TpProxyPendingCall allocated with tp_proxy_pending_call_v0_new() - * - * Indicate that dbus-glib has finished with this pending call, and therefore - * either tp_proxy_pending_call_v0_take_results() has already been called, - * or it will never be called. See tp_proxy_pending_call_v0_new(). - * - * The signature is chosen to match #GDestroyNotify. - * - * This function is for use by #TpProxy subclass implementations only, and - * should usually only be called from code generated by - * tools/glib-client-gen.py. - * - * Since: 0.7.1 - */ - -/* that's implemented in the core library, but it calls this: */ - -void -_tp_proxy_pending_call_completed (gpointer p) +static void +tp_proxy_pending_call_async_ready_cb (GObject *source_object, + GAsyncResult *result, + gpointer user_data) { - TpProxyPendingCall *pc = p; - - MORE_DEBUG ("%p", pc); + TpProxyPendingCall *pc = user_data; + GVariant *args; + GError *error = NULL; - g_return_if_fail (pc->priv == pending_call_magic); - g_return_if_fail (!pc->dbus_completed); - g_return_if_fail (pc->proxy != NULL); - - /* dbus-glib frees its user_data *before* it emits destroy; if we - * haven't yet queued the callback, assume that's what's going on. */ - if (pc->idle_source == 0 && pc->iface_proxy != NULL) + if (pc->proxy == NULL || + pc->wrapper == NULL || + g_cancellable_is_cancelled (pc->cancellable)) { - MORE_DEBUG ("Looks like this pending call hasn't finished, assuming " - "the DBusGProxy is about to die"); - /* this causes the pending call to be freed */ - _tp_proxy_pending_call_dgproxy_destroy (pc->iface_proxy, pc); - - g_assert (pc->iface_proxy == NULL); + DEBUG ("%p: ignoring result due to invalidation, weak object " + "disappearance or cancellation", pc); + goto finally; } - pc->dbus_completed = TRUE; + args = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source_object), + result, &error); - /* If the idle callback has been run already, we can go away */ - if (pc->idle_completed) - tp_proxy_pending_call_free (pc); -} - -/** - * tp_proxy_pending_call_v0_take_pending_call: - * @pc: A pending call on which this function has not yet been called - * @pending_call: The underlying dbus-glib pending call - * - * Set the underlying pending call to be used by this object. - * See also tp_proxy_pending_call_v0_new(). - * - * This function is for use by #TpProxy subclass implementations only, and - * should usually only be called from code generated by - * tools/glib-client-gen.py. - * - * This function may not be called if @pc was constructed with a %NULL - * #DBusGProxy. - * - * Since: 0.7.1 - */ - -/* that's implemented in the core library, but it calls this: */ + if (args != NULL) + { + DEBUG ("%p: success", pc); + pc->wrapper (pc->proxy, NULL, args, + pc->callback, pc->user_data, pc->weak_object); + g_variant_unref (args); + } + else + { + DEBUG ("%p: %s #%d: %s", pc, + g_quark_to_string (error->domain), error->code, + error->message); + pc->wrapper (pc->proxy, error, NULL, + pc->callback, pc->user_data, pc->weak_object); + g_clear_error (&error); + } -void -_tp_proxy_pending_call_take_pending_call (TpProxyPendingCall *pc, - DBusGProxyCall *pending_call) -{ - g_return_if_fail (pc->priv == pending_call_magic); - g_return_if_fail (pc->pending_call == NULL); - g_return_if_fail (pc->proxy != NULL); - g_return_if_fail (pc->iface_proxy != NULL); + pc->wrapper = NULL; - pc->pending_call = pending_call; +finally: + tp_proxy_pending_call_free (pc); } -static void -_tp_proxy_pending_call_idle_completed (gpointer p) +TpProxyPendingCall * +_tp_proxy_pending_call_v1_new (TpProxy *self, + gint timeout_ms, + GQuark iface, + const gchar *member, + GVariant *args, + const GVariantType *reply_type, + TpProxyWrapperFunc wrapper, + GCallback callback, + gpointer user_data, + GDestroyNotify destroy, + GObject *weak_object) { - TpProxyPendingCall *pc = p; + TpProxyPendingCall *pc = NULL; - MORE_DEBUG ("%p", pc); + g_return_val_if_fail (callback != NULL || user_data == NULL, NULL); + g_return_val_if_fail (callback != NULL || destroy == NULL, NULL); + g_return_val_if_fail (callback != NULL || weak_object == NULL, NULL); + g_return_val_if_fail (iface != 0, NULL); + g_return_val_if_fail (member != NULL, NULL); + g_return_val_if_fail (args != NULL, NULL); + g_return_val_if_fail (reply_type != NULL, NULL); + g_return_val_if_fail (callback == NULL || wrapper != NULL, NULL); - pc->idle_completed = TRUE; + g_variant_ref_sink (args); - if (pc->dbus_completed) - tp_proxy_pending_call_free (pc); -} + if (callback == NULL) + { + if (tp_proxy_has_interface_by_id (self, iface)) + { + DEBUG ("%s.%s on %s:%s %p, ignoring reply", + g_quark_to_string (iface), member, + tp_proxy_get_bus_name (self), tp_proxy_get_object_path (self), + self); + + g_dbus_connection_call (tp_proxy_get_dbus_connection (self), + tp_proxy_get_bus_name (self), + tp_proxy_get_object_path (self), + g_quark_to_string (iface), + member, + args, + reply_type, + G_DBUS_CALL_FLAGS_NONE, + timeout_ms, + NULL, + NULL, + NULL); + } + else + { + DEBUG ("%s.%s on %s:%s %p would fail, but ignoring reply", + g_quark_to_string (iface), member, + tp_proxy_get_bus_name (self), tp_proxy_get_object_path (self), + self); + } -/** - * tp_proxy_pending_call_v0_take_results: - * @pc: A pending call on which this function has not yet been called - * @error: %NULL if the call was successful, or an error (whose ownership - * is taken over by the pending call object). Because of dbus-glib - * idiosyncrasies, this must be the error produced by dbus-glib, not a copy. - * @args: %NULL if the call failed or had no "out" arguments, or an array - * of "out" arguments (whose ownership is taken over by the pending call - * object) - * - * Set the "out" arguments (return values) from this pending call. - * See also tp_proxy_pending_call_v0_new(). - * - * This function is for use by #TpProxy subclass implementations only, and - * should usually only be called from code generated by - * tools/glib-client-gen.py. - * - * Since: 0.7.1 - */ + goto finally; + } -/* that's implemented in the core library, but it calls this: */ + pc = g_slice_new0 (TpProxyPendingCall); + pc->proxy = g_object_ref (self); + pc->wrapper = wrapper; + pc->callback = callback; + pc->user_data = user_data; + pc->destroy = destroy; + pc->weak_object = weak_object; + pc->priv = pending_call_magic; + pc->cancellable = g_cancellable_new (); -void -_tp_proxy_pending_call_take_results (TpProxyPendingCall *pc, - GError *error, - GValueArray *args) -{ - g_return_if_fail (pc->proxy != NULL); - g_return_if_fail (pc->priv == pending_call_magic); - g_return_if_fail (pc->args == NULL); - g_return_if_fail (pc->error == NULL); - g_return_if_fail (pc->idle_source == 0); - g_return_if_fail (error == NULL || args == NULL); + DEBUG ("%p: %s.%s on %s:%s %p", + pc, g_quark_to_string (iface), member, tp_proxy_get_bus_name (self), + tp_proxy_get_object_path (self), self); - MORE_DEBUG ("%p (error: %s)", pc, - error == NULL ? "(none)" : error->message); + if (weak_object != NULL) + g_object_weak_ref (weak_object, tp_proxy_pending_call_lost_weak_ref, pc); - pc->args = args; - pc->error = _tp_proxy_take_and_remap_error (pc->proxy, error); + /* very slight optimization: intra-library call to the real implementation + * rather than calling across library boundaries via the core library */ + if (_tp_proxy_check_interface_by_id (self, iface, &pc->error)) + { + DEBUG ("... doing GDBus call"); + + g_dbus_connection_call (tp_proxy_get_dbus_connection (self), + tp_proxy_get_bus_name (self), + tp_proxy_get_object_path (self), + g_quark_to_string (iface), + member, + /* consume floating ref */ + args, + reply_type, + G_DBUS_CALL_FLAGS_NONE, + timeout_ms, + pc->cancellable, + tp_proxy_pending_call_async_ready_cb, + pc); + } + else + { + DEBUG ("... raising error immediately"); + g_idle_add_full (G_PRIORITY_HIGH, + tp_proxy_pending_call_idle_error, pc, + (GDestroyNotify) tp_proxy_pending_call_free); + } - /* queue up the actual callback to run after we go back to the event loop */ - pc->idle_source = g_idle_add_full (G_PRIORITY_HIGH, - tp_proxy_pending_call_idle_invoke, pc, - _tp_proxy_pending_call_idle_completed); +finally: + g_variant_unref (args); + return pc; } diff --git a/telepathy-glib/proxy-subclass.h b/telepathy-glib/proxy-subclass.h index 7f2c5b402..c485f01e0 100644 --- a/telepathy-glib/proxy-subclass.h +++ b/telepathy-glib/proxy-subclass.h @@ -29,23 +29,18 @@ G_BEGIN_DECLS +typedef void (*TpProxyWrapperFunc) (TpProxy *self, + const GError *error, GVariant *args, + GCallback callback, gpointer user_data, GObject *weak_object); typedef void (*TpProxyInvokeFunc) (TpProxy *self, GError *error, GValueArray *args, GCallback callback, gpointer user_data, GObject *weak_object); -TpProxyPendingCall *tp_proxy_pending_call_v0_new (TpProxy *self, - GQuark iface, const gchar *member, DBusGProxy *iface_proxy, - TpProxyInvokeFunc invoke_callback, +TpProxyPendingCall *tp_proxy_pending_call_v1_new (TpProxy *self, + gint timeout_ms, GQuark iface, const gchar *member, + GVariant *args, const GVariantType *reply_type, TpProxyWrapperFunc wrapper, GCallback callback, gpointer user_data, GDestroyNotify destroy, - GObject *weak_object, gboolean cancel_must_raise); - -void tp_proxy_pending_call_v0_take_pending_call (TpProxyPendingCall *pc, - DBusGProxyCall *pending_call); - -void tp_proxy_pending_call_v0_take_results (TpProxyPendingCall *pc, - GError *error, GValueArray *args); - -void tp_proxy_pending_call_v0_completed (gpointer p); + GObject *weak_object); TpProxySignalConnection *tp_proxy_signal_connection_v0_new (TpProxy *self, GQuark iface, const gchar *member, diff --git a/tools/glib-client-gen.py b/tools/glib-client-gen.py index 4d1d0bfa7..8eba83c31 100644 --- a/tools/glib-client-gen.py +++ b/tools/glib-client-gen.py @@ -30,7 +30,7 @@ from getopt import gnu_getopt from libtpcodegen import file_set_contents, key_by_name, u from libglibcodegen import (Signature, type_to_gtype, get_docstring, xml_escape, get_deprecated, copy_into_gvalue, - value_getter, move_into_gvalue) + value_getter) NS_TP = "http://telepathy.freedesktop.org/wiki/DbusSpec#extensions-v0" @@ -382,6 +382,8 @@ class Generator(object): ret_count = 0 in_args = [] out_args = [] + in_sig = [] + out_sig = [] for arg in method.getElementsByTagName('arg'): name = arg.getAttribute('name') @@ -406,6 +408,7 @@ class Generator(object): if direction != 'out': in_args.append((name, info, tp_type, arg)) else: + out_sig.append(type) out_args.append((name, info, tp_type, arg)) # Async reply callback type @@ -476,109 +479,19 @@ class Generator(object): iface_lc, member_lc) - collect_callback = '_%s_%s_collect_callback_%s' % (self.prefix_lc, - iface_lc, - member_lc) - - # This is needed by both reentrant and non-reentrant calls - if self.split_reentrants: - collector = lambda x: (self.b(x), self.rb(x)) - else: - collector = self.b - - # The callback called by dbus-glib; this ends the call and collects - # the results into a GValueArray. - collector('static void') - collector('%s (DBusGProxy *proxy,' % collect_callback) - collector(' DBusGProxyCall *call,') - collector(' gpointer user_data)') - collector('{') - collector(' GError *error = NULL;') - - if len(out_args) > 0: - collector(' GValueArray *args;') - collector(' GValue blank = { 0 };') - collector(' guint i;') - - for arg in out_args: - name, info, tp_type, elt = arg - ctype, gtype, marshaller, pointer = info - - # "We handle variants specially; the caller is expected to - # have already allocated storage for them". Thanks, - # dbus-glib... - if gtype == 'G_TYPE_VALUE': - collector(' GValue *%s = g_new0 (GValue, 1);' % name) - else: - collector(' %s%s;' % (ctype, name)) - - collector('') - collector(' dbus_g_proxy_end_call (proxy, call, &error,') - - for arg in out_args: - name, info, tp_type, elt = arg - ctype, gtype, marshaller, pointer = info - - if gtype == 'G_TYPE_VALUE': - collector(' %s, %s,' % (gtype, name)) - else: - collector(' %s, &%s,' % (gtype, name)) - - collector(' G_TYPE_INVALID);') - - if len(out_args) == 0: - collector(' tp_proxy_pending_call_v0_take_results (user_data, error,' - 'NULL);') - else: - collector('') - collector(' if (error != NULL)') - collector(' {') - collector(' tp_proxy_pending_call_v0_take_results (user_data, error,') - collector(' NULL);') - - for arg in out_args: - name, info, tp_type, elt = arg - ctype, gtype, marshaller, pointer = info - if gtype == 'G_TYPE_VALUE': - collector(' g_free (%s);' % name) - - collector(' return;') - collector(' }') - collector('') - collector(' G_GNUC_BEGIN_IGNORE_DEPRECATIONS') - collector('') - collector(' args = g_value_array_new (%d);' % len(out_args)) - collector(' g_value_init (&blank, G_TYPE_INT);') - collector('') - collector(' for (i = 0; i < %d; i++)' % len(out_args)) - collector(' g_value_array_append (args, &blank);') - collector('') - collector(' G_GNUC_END_IGNORE_DEPRECATIONS') - - for i, arg in enumerate(out_args): - name, info, tp_type, elt = arg - ctype, gtype, marshaller, pointer = info - - collector('') - collector(' g_value_unset (args->values + %d);' % i) - collector(' g_value_init (args->values + %d, %s);' - % (i, gtype)) - collector(' ' + move_into_gvalue('args->values + %d' % i, - gtype, marshaller, name)) - - collector(' tp_proxy_pending_call_v0_take_results (user_data, ' - 'NULL, args);') - - collector('}') - self.b('static void') self.b('%s (TpProxy *self,' % invoke_callback) - self.b(' GError *error,') - self.b(' GValueArray *args,') + self.b(' const GError *error,') + self.b(' GVariant *args,') self.b(' GCallback generic_callback,') self.b(' gpointer user_data,') self.b(' GObject *weak_object)') self.b('{') + + if out_args: + self.b(' GValue args_val = G_VALUE_INIT;') + self.b(' GValueArray *args_va;') + self.b(' %s callback = (%s) generic_callback;' % (callback_name, callback_name)) self.b('') @@ -598,9 +511,14 @@ class Generator(object): self.b(' 0,') self.b(' error, user_data, weak_object);') - self.b(' g_error_free (error);') self.b(' return;') self.b(' }') + self.b('') + + if out_args: + self.b(' dbus_g_value_parse_g_variant (args, &args_val);') + self.b(' args_va = g_value_get_boxed (&args_val);') + self.b('') self.b(' callback ((%s) self,' % self.proxy_cls) @@ -609,18 +527,13 @@ class Generator(object): ctype, gtype, marshaller, pointer = info getter = value_getter(gtype, marshaller) - self.b(' %s (args->values + %d),' % (getter, i)) + self.b(' %s (args_va->values + %d),' % (getter, i)) self.b(' error, user_data, weak_object);') - self.b('') - self.b(' G_GNUC_BEGIN_IGNORE_DEPRECATIONS') - if len(out_args) > 0: - self.b(' g_value_array_free (args);') - else: - self.b(' if (args != NULL)') - self.b(' g_value_array_free (args);') - self.b(' G_GNUC_END_IGNORE_DEPRECATIONS') + if out_args: + self.b('') + self.b(' g_value_unset (&args_val);') self.b('}') self.b('') @@ -717,80 +630,42 @@ class Generator(object): self.b(' GDestroyNotify destroy,') self.b(' GObject *weak_object)') self.b('{') - self.b(' GError *error = NULL;') - self.b(' GQuark interface = %s;' % self.get_iface_quark()) - self.b(' DBusGProxy *iface;') + self.b(' TpProxyPendingCall *ret;') + self.b(' GValue args_val = G_VALUE_INIT;') self.b('') - self.b(' g_return_val_if_fail (callback != NULL || ' - 'user_data == NULL, NULL);') - self.b(' g_return_val_if_fail (callback != NULL || ' - 'destroy == NULL, NULL);') - self.b(' g_return_val_if_fail (callback != NULL || ' - 'weak_object == NULL, NULL);') - self.b('') - self.b(' iface = tp_proxy_get_interface_by_id (') - self.b(' (TpProxy *) proxy,') - self.b(' interface, (callback == NULL ? NULL : &error));') - self.b('') - self.b(' if (callback == NULL)') - self.b(' {') - self.b(' if (iface == NULL)') - self.b(' return NULL;') - self.b('') - self.b(' dbus_g_proxy_call_no_reply (iface, "%s",' % member) + self.b(' g_value_init (&args_val, ' + 'dbus_g_type_get_struct ("GValueArray",') for arg in in_args: name, info, tp_type, elt = arg ctype, gtype, marshaller, pointer = info + self.b(' %s,' % gtype) - const = pointer and 'const ' or '' - - self.b(' %s, %s,' % (gtype, name)) - - self.b(' G_TYPE_INVALID);') - self.b(' return NULL;') - self.b(' }') - self.b(' else') - self.b(' {') - self.b(' TpProxyPendingCall *data;') - self.b('') - self.b(' data = tp_proxy_pending_call_v0_new ((TpProxy *) proxy,') - self.b(' interface, "%s", iface,' % member) - self.b(' %s,' % invoke_callback) - self.b(' G_CALLBACK (callback), user_data, destroy,') - self.b(' weak_object, FALSE);') - self.b('') - # If iface is NULL then the only valid thing we can do is to - # terminate the call with an error. Go through the machinery - # we'd use for dbus-glib anyway, to stop it being re-entrant. - self.b(' if (iface == NULL)') - self.b(' {') - self.b(' tp_proxy_pending_call_v0_take_results (data,') - self.b(' error, NULL);') - self.b(' tp_proxy_pending_call_v0_completed (data);') - self.b(' return data;') - self.b(' }') - self.b('') - self.b(' tp_proxy_pending_call_v0_take_pending_call (data,') - self.b(' dbus_g_proxy_begin_call_with_timeout (iface,') - self.b(' "%s",' % member) - self.b(' %s,' % collect_callback) - self.b(' data,') - self.b(' tp_proxy_pending_call_v0_completed,') - self.b(' timeout_ms,') + self.b(' G_TYPE_INVALID));') + self.b(' g_value_take_boxed (&args_val,') + self.b(' tp_value_array_build (%d,' % len(in_args)) for arg in in_args: name, info, tp_type, elt = arg ctype, gtype, marshaller, pointer = info + self.b(' %s, %s,' % (gtype, name)) - const = pointer and 'const ' or '' - - self.b(' %s, %s,' % (gtype, name)) - - self.b(' G_TYPE_INVALID));') + self.b(' G_TYPE_INVALID));') self.b('') - self.b(' return data;') - self.b(' }') + self.b(' ret = tp_proxy_pending_call_v1_new ((TpProxy *) proxy,') + self.b(' timeout_ms,') + self.b(' %s,' % self.get_iface_quark()) + self.b(' "%s",' % member) + self.b(' /* consume floating ref */') + self.b(' dbus_g_value_build_g_variant (&args_val),') + self.b(' G_VARIANT_TYPE ("(%s)"),' % (''.join(out_sig))) + self.b(' %s,' % invoke_callback) + self.b(' G_CALLBACK (callback),') + self.b(' user_data,') + self.b(' destroy,') + self.b(' weak_object);') + self.b(' g_value_unset (&args_val);') + self.b(' return ret;') self.b('}') self.b('') @@ -827,7 +702,7 @@ class Generator(object): self.h('#endif /* __GTK_DOC_IGNORE__ */') self.do_method_reentrant(method, iface_lc, member, member_lc, - in_args, out_args, collect_callback) + in_args, out_args, out_sig) # leave a gap for the end of the method self.d('') @@ -835,7 +710,7 @@ class Generator(object): self.h('') def do_method_reentrant(self, method, iface_lc, member, member_lc, in_args, - out_args, collect_callback): + out_args, out_sig): # Reentrant blocking calls # Example: # gboolean tp_cli_properties_interface_run_get_properties @@ -882,12 +757,18 @@ class Generator(object): b('static void') b('%s (TpProxy *self G_GNUC_UNUSED,' % reentrant_invoke) - b(' GError *error,') - b(' GValueArray *args,') - b(' GCallback unused G_GNUC_UNUSED,') - b(' gpointer user_data G_GNUC_UNUSED,') - b(' GObject *unused2 G_GNUC_UNUSED)') + b(' const GError *error,') + b(' GVariant *args_variant,') + b(' GCallback dummy G_GNUC_UNUSED,') + b(' gpointer user_data,') + b(' GObject *weak_object G_GNUC_UNUSED)') b('{') + + if out_args: + b(' GValue args_val = G_VALUE_INIT;') + b(' GValueArray *args;') + b('') + b(' _%s_%s_run_state_%s *state = user_data;' % (self.prefix_lc, iface_lc, member_lc)) b('') @@ -898,14 +779,16 @@ class Generator(object): b(' if (error != NULL)') b(' {') b(' if (state->error != NULL)') - b(' *state->error = error;') - b(' else') - b(' g_error_free (error);') + b(' *state->error = g_error_copy (error);') b('') b(' return;') b(' }') b('') + if out_args: + b(' dbus_g_value_parse_g_variant (args_variant, &args_val);') + b(' args = g_value_get_boxed (&args_val);') + for i, arg in enumerate(out_args): name, info, tp_type, elt = arg ctype, gtype, marshaller, pointer = info @@ -926,13 +809,8 @@ class Generator(object): b('') - b(' G_GNUC_BEGIN_IGNORE_DEPRECATIONS') - if len(out_args) > 0: - b(' g_value_array_free (args);') - else: - b(' if (args != NULL)') - b(' g_value_array_free (args);') - b(' G_GNUC_END_IGNORE_DEPRECATIONS') + if out_args: + b(' g_value_unset (&args_val);') b('}') b('') @@ -1029,7 +907,6 @@ class Generator(object): b(' GError **error,') b(' GMainLoop **loop)') b('{') - b(' DBusGProxy *iface;') b(' GQuark interface = %s;' % self.get_iface_quark()) b(' TpProxyPendingCall *pc;') b(' _%s_%s_run_state_%s state = {' @@ -1042,43 +919,52 @@ class Generator(object): b(' %s,' % name) b(' FALSE /* completed */, FALSE /* success */ };') + b(' GValue args_val = G_VALUE_INIT;') + b('') + b(' g_value_init (&args_val, dbus_g_type_get_struct ("GValueArray",') + + for arg in in_args: + name, info, tp_type, elt = arg + ctype, gtype, marshaller, pointer = info + b(' %s,' % gtype) + + b(' G_TYPE_INVALID));') + + b(' g_value_take_boxed (&args_val,') + b(' tp_value_array_build (%d,' % len(in_args)) + + for arg in in_args: + name, info, tp_type, elt = arg + ctype, gtype, marshaller, pointer = info + b(' %s, %s,' % (gtype, name)) + + b(' G_TYPE_INVALID));') b('') b(' g_return_val_if_fail (%s (proxy), FALSE);' % self.proxy_assert) b('') - b(' iface = tp_proxy_get_interface_by_id') - b(' ((TpProxy *) proxy, interface, error);') - b('') - b(' if (iface == NULL)') + b(' if (!tp_proxy_check_interface_by_id') + b(' ((TpProxy *) proxy, interface, error))') b(' return FALSE;') b('') b(' state.loop = g_main_loop_new (NULL, FALSE);') b('') - b(' pc = tp_proxy_pending_call_v0_new ((TpProxy *) proxy,') - b(' interface, "%s", iface,' % member) - b(' %s,' % reentrant_invoke) - b(' NULL, &state, NULL, NULL, TRUE);') - b('') b(' if (loop != NULL)') b(' *loop = state.loop;') b('') - b(' tp_proxy_pending_call_v0_take_pending_call (pc,') - b(' dbus_g_proxy_begin_call_with_timeout (iface,') - b(' "%s",' % member) - b(' %s,' % collect_callback) - b(' pc,') - b(' tp_proxy_pending_call_v0_completed,') - b(' timeout_ms,') - - for arg in in_args: - name, info, tp_type, elt = arg - ctype, gtype, marshaller, pointer = info - - const = pointer and 'const ' or '' - - b(' %s, %s,' % (gtype, name)) - - b(' G_TYPE_INVALID));') + b(' pc = tp_proxy_pending_call_v1_new ((TpProxy *) proxy,') + b(' timeout_ms,') + b(' interface,') + b(' "%s",' % member) + b(' /* consume floating ref */') + b(' dbus_g_value_build_g_variant (&args_val),') + b(' G_VARIANT_TYPE ("(%s)"),' % (''.join(out_sig))) + b(' %s,' % reentrant_invoke) + b(' (void *) 1, /* any non-NULL pointer */') + b(' &state,') + b(' NULL,') + b(' NULL);') + b(' g_value_unset (&args_val);') b('') b(' if (!state.completed)') b(' g_main_loop_run (state.loop);') |