summaryrefslogtreecommitdiff
path: root/telepathy-glib/proxy-methods.c
diff options
context:
space:
mode:
authorSimon McVittie <simon.mcvittie@collabora.co.uk>2008-03-04 16:11:34 +0000
committerSimon McVittie <simon.mcvittie@collabora.co.uk>2008-03-04 16:11:34 +0000
commit47a82fb28953a1270a61b1d88dfb78b73ffbfd45 (patch)
tree671a9bba1be6347cd523f6986d301f1bbbd4094a /telepathy-glib/proxy-methods.c
parent641959fa9b94864f6938a2aac38266890fa2c649 (diff)
downloadtelepathy-glib-47a82fb28953a1270a61b1d88dfb78b73ffbfd45.tar.gz
Refactor TpProxyPendingCall to avoid revenge-of-#14750.
Also don't crash if the order of _completed and _idle_invoke gets reversed by a hypothetical dbus-glib behaviour change. 20080304161134-53eee-7aab6787298d9372da1ee4776a90284f43c9498c.gz
Diffstat (limited to 'telepathy-glib/proxy-methods.c')
-rw-r--r--telepathy-glib/proxy-methods.c191
1 files changed, 117 insertions, 74 deletions
diff --git a/telepathy-glib/proxy-methods.c b/telepathy-glib/proxy-methods.c
index 19984434f..88fb8b119 100644
--- a/telepathy-glib/proxy-methods.c
+++ b/telepathy-glib/proxy-methods.c
@@ -74,8 +74,8 @@ struct _TpProxyPendingCall {
/* Always non-NULL */
TpProxy *proxy;
- /* Set to NULL after it's been invoked once, so we can assert that it
- * doesn't get called again. Supplied by the generated code */
+ /* 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;
/* arguments for invoke_callback supplied by _take_results, by
@@ -94,12 +94,18 @@ struct _TpProxyPendingCall {
DBusGProxyCall *pending_call;
/* Nonzero if _idle_invoke has been queued (even if it has already
- * happened) */
+ * happened), i.e. if results have been taken or the DBusGProxy
+ * was destroyed */
guint idle_source;
/* If TRUE, invoke the callback even on cancellation */
gboolean cancel_must_raise:1;
+ /* If TRUE, the idle_invoke callback has either run or been cancelled */
+ gboolean idle_completed:1;
+ /* If TRUE, dbus-glib no longer holds a reference to us */
+ gboolean dbus_completed:1;
+
/* Marker to indicate that this is, in fact, a valid TpProxyPendingCall */
gconstpointer priv;
};
@@ -118,7 +124,9 @@ tp_proxy_pending_call_lost_weak_ref (gpointer data,
g_assert (dead == pc->weak_object);
pc->weak_object = NULL;
- tp_proxy_pending_call_cancel (pc);
+
+ if (!pc->idle_completed)
+ tp_proxy_pending_call_cancel (pc);
}
static gboolean
@@ -127,10 +135,20 @@ tp_proxy_pending_call_idle_invoke (gpointer p)
TpProxyPendingCall *pc = p;
TpProxyInvokeFunc invoke = pc->invoke_callback;
- g_return_val_if_fail (pc->invoke_callback != NULL, FALSE);
+ MORE_DEBUG ("%p", pc);
+
+ if (invoke == NULL)
+ {
+ /* either already invoked (bug?), or cancelled */
+ return FALSE;
+ }
MORE_DEBUG ("%p: invoking user callback", pc);
+ g_assert (pc->proxy != NULL);
+ g_assert (pc->error == NULL || pc->args == NULL);
+ g_assert (!pc->idle_completed);
+
pc->invoke_callback = NULL;
invoke (pc->proxy, pc->error, pc->args, pc->callback,
pc->user_data, pc->weak_object);
@@ -143,7 +161,7 @@ tp_proxy_pending_call_idle_invoke (gpointer p)
return FALSE;
}
-static void tp_proxy_pending_call_free (gpointer p);
+static void _tp_proxy_pending_call_idle_completed (gpointer p);
static void
_tp_proxy_pending_call_dgproxy_destroy (DBusGProxy *iface_proxy,
@@ -152,6 +170,7 @@ _tp_proxy_pending_call_dgproxy_destroy (DBusGProxy *iface_proxy,
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);
@@ -166,7 +185,8 @@ _tp_proxy_pending_call_dgproxy_destroy (DBusGProxy *iface_proxy,
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_free);
+ tp_proxy_pending_call_idle_invoke, pc,
+ _tp_proxy_pending_call_idle_completed);
}
g_signal_handlers_disconnect_by_func (pc->iface_proxy,
@@ -234,6 +254,7 @@ tp_proxy_pending_call_v0_new (TpProxy *self,
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);
@@ -274,83 +295,92 @@ tp_proxy_pending_call_v0_new (TpProxy *self,
void
tp_proxy_pending_call_cancel (TpProxyPendingCall *pc)
{
- gpointer iface;
- TpProxyInvokeFunc invoke = pc->invoke_callback;
-
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);
- /* Mark the pending call as expired */
- pc->invoke_callback = NULL;
-
- if (invoke != NULL && pc->cancel_must_raise)
+ if (pc->cancel_must_raise)
{
- GError *error = g_error_new_literal (TP_DBUS_ERRORS,
+ 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");
- MORE_DEBUG ("Telling user callback");
- invoke (pc->proxy, error, NULL, pc->callback, pc->user_data,
- pc->weak_object);
+ if (pc->args != NULL)
+ {
+ g_value_array_free (pc->args);
+ pc->args = NULL;
+ }
+ }
+ else
+ {
+ pc->invoke_callback = NULL;
}
- if (pc->idle_source != 0)
+ /* 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)
{
- /* we aren't actually doing dbus-glib things any more anyway */
- MORE_DEBUG ("Removing idle source");
- g_source_remove (pc->idle_source);
- return;
+ pc->idle_source = g_idle_add_full (G_PRIORITY_HIGH,
+ tp_proxy_pending_call_idle_invoke, pc,
+ _tp_proxy_pending_call_idle_completed);
}
- /* It's possible that the DBusGProxy is only reffed by the TpProxy, and
- * the TpProxy is only reffed by this TpProxyPendingCall, which will be
- * freed as a side-effect of cancelling the DBusGProxyCall halfway through
- * dbus_g_proxy_cancel_call (), so temporarily ref the DBusGProxy to ensure
- * that it survives for the duration. (fd.o #14576) */
- iface = g_object_ref (pc->iface_proxy);
- MORE_DEBUG ("Cancelling pending call %p on DBusGProxy %p",
- pc->pending_call, iface);
- dbus_g_proxy_cancel_call (iface, pc->pending_call);
- MORE_DEBUG ("... done");
- g_object_unref (iface);
+ if (!pc->dbus_completed)
+ {
+ /* 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);
+ }
}
static void
-tp_proxy_pending_call_free (gpointer p)
+tp_proxy_pending_call_free (TpProxyPendingCall *pc)
{
- TpProxyPendingCall *pc = p;
-
MORE_DEBUG ("%p", pc);
- g_return_if_fail (pc->priv == pending_call_magic);
+ g_assert (pc->priv == pending_call_magic);
- if (pc->proxy != NULL)
- {
- g_object_unref (pc->proxy);
- pc->proxy = NULL;
- }
+ if (pc->destroy != NULL)
+ pc->destroy (pc->user_data);
- 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;
- }
+ pc->destroy = NULL;
+ pc->user_data = NULL;
if (pc->error != NULL)
g_error_free (pc->error);
+ pc->error = NULL;
+
if (pc->args != NULL)
g_value_array_free (pc->args);
- if (pc->destroy != NULL)
- pc->destroy (pc->user_data);
+ pc->args = NULL;
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_slice_free (TpProxyPendingCall, pc);
}
@@ -375,34 +405,29 @@ tp_proxy_pending_call_v0_completed (gpointer p)
{
TpProxyPendingCall *pc = p;
- MORE_DEBUG ("%p", p);
+ MORE_DEBUG ("%p", pc);
g_return_if_fail (pc->priv == pending_call_magic);
+ g_return_if_fail (!pc->dbus_completed);
+ g_return_if_fail (pc->proxy != NULL);
- if (pc->idle_source != 0)
+ /* 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)
{
- /* we've kicked off an idle function, so we don't want to die until
- * that function runs */
- MORE_DEBUG ("Refusing to die til the idle function runs");
- return;
- }
+ 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);
- if (pc->proxy != NULL)
- {
- /* dbus-glib frees its user_data *before* it emits destroy; if we
- * haven't yet run the callback, assume that's what's going on. */
- if (pc->invoke_callback != NULL)
- {
- 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);
- return;
- }
+ g_assert (pc->iface_proxy == NULL);
}
- MORE_DEBUG ("Freeing myself");
- tp_proxy_pending_call_free (pc);
+ pc->dbus_completed = TRUE;
+
+ /* If the idle callback has been run already, we can go away */
+ if (pc->idle_completed)
+ tp_proxy_pending_call_free (pc);
}
/**
@@ -424,9 +449,25 @@ tp_proxy_pending_call_v0_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);
+
pc->pending_call = pending_call;
}
+static void
+_tp_proxy_pending_call_idle_completed (gpointer p)
+{
+ TpProxyPendingCall *pc = p;
+
+ MORE_DEBUG ("%p", pc);
+
+ pc->idle_completed = TRUE;
+
+ if (pc->dbus_completed)
+ tp_proxy_pending_call_free (pc);
+}
+
/**
* tp_proxy_pending_call_v0_take_results:
* @pc: A pending call on which this function has not yet been called
@@ -451,6 +492,7 @@ tp_proxy_pending_call_v0_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);
@@ -465,5 +507,6 @@ tp_proxy_pending_call_v0_take_results (TpProxyPendingCall *pc,
/* 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_free);
+ tp_proxy_pending_call_idle_invoke, pc,
+ _tp_proxy_pending_call_idle_completed);
}