diff options
author | Simon McVittie <simon.mcvittie@collabora.co.uk> | 2008-03-03 17:07:16 +0000 |
---|---|---|
committer | Simon McVittie <simon.mcvittie@collabora.co.uk> | 2008-03-03 17:07:16 +0000 |
commit | da13135100a070a9492b3305dd33836fd2483a4d (patch) | |
tree | ffb45ff34c14cbad708babfcff34aa1c015d00ff /telepathy-glib/proxy-signals.c | |
parent | 073f361382fd9fa1098a42cd26216101f860fe35 (diff) | |
download | telepathy-glib-da13135100a070a9492b3305dd33836fd2483a4d.tar.gz |
Move method- and signal-specific bits of TpProxy to separate source files
20080303170716-53eee-e28f1cb9cf42f66efd23538e0f1c3a076f586132.gz
Diffstat (limited to 'telepathy-glib/proxy-signals.c')
-rw-r--r-- | telepathy-glib/proxy-signals.c | 417 |
1 files changed, 417 insertions, 0 deletions
diff --git a/telepathy-glib/proxy-signals.c b/telepathy-glib/proxy-signals.c new file mode 100644 index 000000000..8afacd23e --- /dev/null +++ b/telepathy-glib/proxy-signals.c @@ -0,0 +1,417 @@ +/* + * Copyright (C) 2007-2008 Collabora Ltd. <http://www.collabora.co.uk/> + * Copyright (C) 2007-2008 Nokia Corporation + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "telepathy-glib/proxy-subclass.h" + +#define DEBUG_FLAG TP_DEBUG_PROXY +#include "telepathy-glib/debug-internal.h" + +#if 0 +#define MORE_DEBUG DEBUG +#else +#define MORE_DEBUG(...) G_STMT_START {} G_STMT_END +#endif + +/** + * TpProxySignalConnection: + * + * Opaque structure representing a D-Bus signal connection. + * + * Since: 0.7.1 + */ + +typedef struct _TpProxySignalInvocation TpProxySignalInvocation; + +struct _TpProxySignalInvocation { + TpProxySignalConnection *sc; + GValueArray *args; + guint idle_source; +}; + +struct _TpProxySignalConnection { + /* 1 if D-Bus has us + * 1 per member of @invocations + * 1 per callback being invoked right now */ + gsize refcount; + + /* borrowed ref (discarded when we see invalidated signal) + * + 1 per member of @invocations + * + 1 per callback being invoked (possibly nested!) right now */ + TpProxy *proxy; + + DBusGProxy *iface_proxy; + gchar *member; + GCallback collect_args; + TpProxyInvokeFunc invoke_callback; + GCallback callback; + gpointer user_data; + GDestroyNotify destroy; + GObject *weak_object; + /* queue of _TpProxySignalInvocation, not including any that are + * being invoked right now */ + GQueue invocations; +}; + +static void +tp_proxy_signal_connection_disconnect_dbus_glib (TpProxySignalConnection *sc) +{ + if (sc->proxy == NULL) + return; + + g_assert (sc->iface_proxy != NULL); + + dbus_g_proxy_disconnect_signal (sc->iface_proxy, sc->member, + sc->collect_args, (gpointer) sc); +} + +static void +tp_proxy_signal_connection_proxy_invalidated (TpProxy *proxy, + guint domain, + gint code, + const gchar *message, + TpProxySignalConnection *sc) +{ + g_assert (sc != NULL); + g_assert (domain != 0); + g_assert (message != NULL); + + DEBUG ("%p: TpProxy %p invalidated (I have %p): %s", sc, proxy, + sc->proxy, message); + g_assert (proxy == sc->proxy); + + tp_proxy_signal_connection_disconnect_dbus_glib (sc); +} + +static void +tp_proxy_signal_connection_lost_weak_ref (gpointer data, + GObject *dead) +{ + TpProxySignalConnection *sc = data; + + DEBUG ("%p: lost weak ref to %p", sc, dead); + + g_assert (dead == sc->weak_object); + + sc->weak_object = NULL; + + tp_proxy_signal_connection_disconnect (sc); +} + +/* Return TRUE if it dies. */ +static gboolean +tp_proxy_signal_connection_unref (TpProxySignalConnection *sc) +{ + if (--(sc->refcount) > 0) + { + MORE_DEBUG ("%p: %" G_GSIZE_FORMAT " refs left", sc, sc->refcount); + return FALSE; + } + + MORE_DEBUG ("destroying %p", sc); + + if (sc->proxy != NULL) + { + g_signal_handlers_disconnect_by_func (sc->proxy, + tp_proxy_signal_connection_proxy_invalidated, sc); + sc->proxy = NULL; + } + + g_assert (sc->invocations.length == 0); + + if (sc->destroy != NULL) + sc->destroy (sc->user_data); + + sc->destroy = NULL; + sc->user_data = NULL; + + if (sc->weak_object != NULL) + { + g_object_weak_unref (sc->weak_object, + tp_proxy_signal_connection_lost_weak_ref, sc); + sc->weak_object = NULL; + } + + g_free (sc->member); + + g_slice_free (TpProxySignalConnection, sc); + + return TRUE; +} + +/** + * tp_proxy_signal_connection_disconnect: + * @sc: a signal connection + * + * Disconnect the given signal connection. After this function returns, you + * must not assume that the signal connection remains valid, but you must not + * explicitly free it either. + * + * Since: 0.7.1 + */ +void +tp_proxy_signal_connection_disconnect (TpProxySignalConnection *sc) +{ + TpProxySignalInvocation *invocation; + + while ((invocation = g_queue_pop_head (&sc->invocations)) != NULL) + { + g_assert (invocation->sc == sc); + g_object_unref (invocation->sc->proxy); + invocation->sc = NULL; + g_source_remove (invocation->idle_source); + + if (tp_proxy_signal_connection_unref (sc)) + return; + } + + tp_proxy_signal_connection_disconnect_dbus_glib (sc); +} + +static void +tp_proxy_signal_invocation_free (gpointer p) +{ + TpProxySignalInvocation *invocation = p; + + if (invocation->sc != NULL) + { + /* this shouldn't really happen - it'll get run if the idle source + * is removed by something other than t_p_s_c_disconnect or + * t_p_s_i_run */ + g_warning ("%s: idle source removed by someone else", G_STRFUNC); + + g_queue_remove (&invocation->sc->invocations, invocation); + g_object_unref (invocation->sc->proxy); + tp_proxy_signal_connection_unref (invocation->sc); + } + + if (invocation->args != NULL) + g_value_array_free (invocation->args); + + g_slice_free (TpProxySignalInvocation, invocation); +} + +static gboolean +tp_proxy_signal_invocation_run (gpointer p) +{ + TpProxySignalInvocation *invocation = p; + TpProxySignalInvocation *popped = g_queue_pop_head + (&invocation->sc->invocations); + + /* if GLib is running idle handlers in the wrong order, then we've lost */ + MORE_DEBUG ("%p: popped %p", invocation->sc, popped); + g_assert (popped == invocation); + + invocation->sc->invoke_callback (invocation->sc->proxy, NULL, + invocation->args, invocation->sc->callback, invocation->sc->user_data, + invocation->sc->weak_object); + + /* the invoke callback steals args */ + invocation->args = NULL; + + /* there's one ref to the proxy per queued invocation, to keep it + * alive */ + MORE_DEBUG ("%p refcount-- due to %p run, sc=%p", invocation->sc->proxy, + invocation, invocation->sc); + g_object_unref (invocation->sc->proxy); + tp_proxy_signal_connection_unref (invocation->sc); + invocation->sc = NULL; + + return FALSE; +} + +static void +tp_proxy_signal_connection_dropped (gpointer p, + GClosure *unused) +{ + TpProxySignalConnection *sc = p; + + MORE_DEBUG ("%p (%u invocations queued)", sc, sc->invocations.length); + + tp_proxy_signal_connection_unref (sc); +} + +static void +_tp_proxy_signal_connection_dgproxy_destroy (DBusGProxy *iface_proxy, + TpProxySignalConnection *sc) +{ + g_assert (iface_proxy != NULL); + g_assert (sc != NULL); + g_assert (sc->iface_proxy == iface_proxy); + + DEBUG ("%p: DBusGProxy %p invalidated", sc, iface_proxy); + + sc->iface_proxy = NULL; + g_signal_handlers_disconnect_by_func (iface_proxy, + _tp_proxy_signal_connection_dgproxy_destroy, sc); + g_object_unref (iface_proxy); +} + +static void +collect_none (DBusGProxy *dgproxy, TpProxySignalConnection *sc) +{ + tp_proxy_signal_connection_v0_take_results (sc, NULL); +} + +/** + * tp_proxy_signal_connection_v0_new: + * @self: a proxy + * @interface: a quark whose string value is the D-Bus interface + * @member: the name of the signal to which we're connecting + * @expected_types: an array of expected GTypes for the arguments, terminated + * by %G_TYPE_INVALID + * @collect_args: a callback to be given to dbus_g_proxy_connect_signal(), + * which must marshal the arguments into a #GValueArray and use them to call + * tp_proxy_signal_connection_v0_take_results(); this callback is not + * guaranteed to be called by future versions of telepathy-glib, which might + * be able to implement its functionality internally. If no arguments are + * expected at all (expected_types = { G_TYPE_INVALID }) then this callback + * should instead be %NULL + * @invoke_callback: a function which will be called with @error = %NULL, + * which should invoke @callback with @user_data, @weak_object and other + * appropriate arguments taken from @args + * @callback: user callback to be invoked by @invoke_callback + * @user_data: user-supplied data for the callback + * @destroy: user-supplied destructor for the data, which will be called + * when the signal connection is disconnected for any reason, + * or will be called before this function returns if an error occurs + * @weak_object: if not %NULL, a #GObject which will be weakly referenced by + * the signal connection - if it is destroyed, the signal connection will + * automatically be disconnected + * @error: If not %NULL, used to raise an error if %NULL is returned + * + * Allocate a new structure representing a signal connection, and connect to + * the signal, arranging for @invoke_callback to be called when it arrives. + * + * 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 signal connection structure, or %NULL if the proxy does not + * have the desired interface or has become invalid + * + * Since: 0.7.1 + */ +TpProxySignalConnection * +tp_proxy_signal_connection_v0_new (TpProxy *self, + GQuark interface, + const gchar *member, + const GType *expected_types, + GCallback collect_args, + TpProxyInvokeFunc invoke_callback, + GCallback callback, + gpointer user_data, + GDestroyNotify destroy, + GObject *weak_object, + GError **error) +{ + TpProxySignalConnection *sc; + DBusGProxy *iface = tp_proxy_borrow_interface_by_id (self, + interface, error); + + if (iface == NULL) + { + if (destroy != NULL) + destroy (user_data); + + return NULL; + } + + if (expected_types[0] == G_TYPE_INVALID) + { + collect_args = G_CALLBACK (collect_none); + } + else + { + g_return_val_if_fail (collect_args != NULL, NULL); + } + + sc = g_slice_new0 (TpProxySignalConnection); + + MORE_DEBUG ("(proxy=%p, if=%s, sig=%s, collect=%p, invoke=%p, " + "cb=%p, ud=%p, dn=%p, wo=%p) -> %p", + self, g_quark_to_string (interface), member, collect_args, + invoke_callback, callback, user_data, destroy, weak_object, sc); + + sc->refcount = 1; + sc->proxy = self; + sc->iface_proxy = g_object_ref (iface); + sc->member = g_strdup (member); + sc->collect_args = collect_args; + sc->invoke_callback = invoke_callback; + sc->callback = callback; + sc->user_data = user_data; + sc->destroy = destroy; + sc->weak_object = weak_object; + + if (weak_object != NULL) + g_object_weak_ref (weak_object, tp_proxy_signal_connection_lost_weak_ref, + sc); + + g_signal_connect (self, "invalidated", + G_CALLBACK (tp_proxy_signal_connection_proxy_invalidated), sc); + + g_signal_connect (iface, "destroy", + G_CALLBACK (_tp_proxy_signal_connection_dgproxy_destroy), sc); + + dbus_g_proxy_connect_signal (iface, member, collect_args, sc, + tp_proxy_signal_connection_dropped); + + return sc; +} + +/** + * tp_proxy_signal_connection_v0_take_results: + * @sc: The signal connection + * @args: The arguments of the signal + * + * Feed the results of a signal invocation back into the signal connection + * machinery. + * + * This method should only be called from #TpProxy subclass implementations, + * in the callback that implements @collect_args. + * + * Since: 0.7.1 + */ +void +tp_proxy_signal_connection_v0_take_results (TpProxySignalConnection *sc, + GValueArray *args) +{ + TpProxySignalInvocation *invocation = g_slice_new0 (TpProxySignalInvocation); + /* FIXME: assert that the GValueArray is the right length, or + * even that it contains the right types? */ + + /* as long as there are queued invocations, we keep one ref to the TpProxy + * and one ref to the TpProxySignalConnection per invocation */ + MORE_DEBUG ("%p refcount++ due to %p, sc=%p", sc->proxy, invocation, sc); + g_object_ref (sc->proxy); + sc->refcount++; + + invocation->sc = sc; + invocation->args = args; + + g_queue_push_tail (&sc->invocations, invocation); + + MORE_DEBUG ("invocations: head=%p tail=%p count=%u", + sc->invocations.head, sc->invocations.tail, + sc->invocations.length); + + invocation->idle_source = g_idle_add_full (G_PRIORITY_HIGH, + tp_proxy_signal_invocation_run, invocation, + tp_proxy_signal_invocation_free); +} |