[Telepathy-commits] [telepathy-glib/master] TpConnection: add handle ref/hold/release functionality
Simon McVittie
simon.mcvittie at collabora.co.uk
Tue Oct 7 07:07:38 PDT 2008
This centralizes handle ref tracking, because otherwise calling
ReleaseHandles is inherently unsafe - you never know whether
another part of the process is still using the handles.
In this implementation the handles are stored in a data-slot on the
DBusConnection.
See mailing list thread "Uniqueness of objects", starting
with Message-ID: <20080930200125.GA15304 at carbon.pseudorandom.co.uk>
---
docs/reference/telepathy-glib-sections.txt | 5 +
telepathy-glib/Makefile.am | 1 +
telepathy-glib/connection-handles.c | 619 ++++++++++++++++++++++++++++
telepathy-glib/connection-internal.h | 2 +
telepathy-glib/connection.c | 3 +
telepathy-glib/connection.h | 25 ++
6 files changed, 655 insertions(+), 0 deletions(-)
create mode 100644 telepathy-glib/connection-handles.c
diff --git a/docs/reference/telepathy-glib-sections.txt b/docs/reference/telepathy-glib-sections.txt
index b6aa801..81bd554 100644
--- a/docs/reference/telepathy-glib-sections.txt
+++ b/docs/reference/telepathy-glib-sections.txt
@@ -2053,6 +2053,11 @@ TpConnectionWhenReadyCb
tp_connection_call_when_ready
tp_connection_is_ready
tp_connection_get_status
+TpConnectionRequestHandlesCb
+tp_connection_request_handles
+TpConnectionHoldHandlesCb
+tp_connection_hold_handles
+tp_connection_unref_handles
tp_connection_init_known_interfaces
tp_connection_presence_type_cmp_availability
TpConnection
diff --git a/telepathy-glib/Makefile.am b/telepathy-glib/Makefile.am
index f05ef50..821e4df 100644
--- a/telepathy-glib/Makefile.am
+++ b/telepathy-glib/Makefile.am
@@ -212,6 +212,7 @@ libtelepathy_glib_internal_la_SOURCES = \
channel-manager.c \
connection.c \
connection-internal.h \
+ connection-handles.c \
connection-manager.c \
contacts-mixin.c \
dbus.c \
diff --git a/telepathy-glib/connection-handles.c b/telepathy-glib/connection-handles.c
new file mode 100644
index 0000000..9b53959
--- /dev/null
+++ b/telepathy-glib/connection-handles.c
@@ -0,0 +1,619 @@
+/* Helper to hold Telepathy handles.
+ *
+ * Copyright (C) 2008 Collabora Ltd. <http://www.collabora.co.uk/>
+ * Copyright (C) 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/connection-internal.h"
+
+#include <dbus/dbus.h>
+#include <dbus/dbus-glib-lowlevel.h>
+
+#define DEBUG_FLAG TP_DEBUG_HANDLES
+#include "telepathy-glib/debug-internal.h"
+
+/*
+ * We allocate a global libdbus DBusConnection slot for this module. If
+ * used, it contains a
+ * GHashTable<gchar *Connection_object_path => Bucket>.
+ *
+ * A Bucket is basically just an array of GHashTable<handle => refcount>.
+ *
+ * This is external to the TpConnection because it has to be - if there
+ * are two TpConnection instances for the same Connection (perhaps they're
+ * of different subclasses), we need to share handle references between them.
+ */
+
+typedef struct {
+ GHashTable *refcounts[NUM_TP_HANDLE_TYPES];
+} Bucket;
+
+static inline void oom (void) G_GNUC_NORETURN;
+
+static inline void
+oom (void)
+{
+ g_error ("Out of memory in libdbus. Cannot have a bucket");
+}
+
+static void
+bucket_free (gpointer p)
+{
+ Bucket *b = p;
+ guint i;
+
+ /* [0] is never allocated (handle type NONE), so start at [1] */
+ g_assert (b->refcounts[0] == NULL);
+
+ for (i = 1; i < NUM_TP_HANDLE_TYPES; i++)
+ {
+ if (b->refcounts[i] != NULL)
+ g_hash_table_destroy (b->refcounts[i]);
+ }
+
+ g_slice_free (Bucket, p);
+}
+
+static Bucket *
+bucket_new (void)
+{
+ return g_slice_new0 (Bucket);
+}
+
+static dbus_int32_t connection_handle_refs_slot = -1;
+
+static void
+_tp_connection_ref_handles (TpConnection *connection,
+ TpHandleType handle_type,
+ const GArray *handles)
+{
+ TpProxy *as_proxy = (TpProxy *) connection;
+ DBusGConnection *g_connection = as_proxy->dbus_connection;
+ const gchar *object_path = as_proxy->object_path;
+ GHashTable *table, *refcounts;
+ Bucket *bucket = NULL;
+ guint i;
+
+ g_assert (as_proxy->invalidated == NULL);
+ g_assert (handle_type < NUM_TP_HANDLE_TYPES);
+
+ DEBUG ("%p: %u handles of type %u", connection, handles->len, handle_type);
+
+ /* MT: libdbus protects us, if so configured */
+ if (!dbus_connection_allocate_data_slot (&connection_handle_refs_slot))
+ oom ();
+
+ /* MT: if we become thread safe, the rest of this function needs a lock */
+ table = dbus_connection_get_data (dbus_g_connection_get_connection (
+ g_connection), connection_handle_refs_slot);
+
+ if (table == NULL)
+ {
+ table = g_hash_table_new_full (g_str_hash, g_str_equal, g_free,
+ bucket_free);
+
+ if (!dbus_connection_set_data (dbus_g_connection_get_connection (
+ g_connection), connection_handle_refs_slot, table,
+ (DBusFreeFunction) g_hash_table_destroy))
+ oom ();
+ }
+ else
+ {
+ bucket = g_hash_table_lookup (table, object_path);
+ }
+
+ if (bucket == NULL)
+ {
+ bucket = bucket_new ();
+ g_hash_table_insert (table, g_strdup (object_path), bucket);
+ }
+
+ if (bucket->refcounts[handle_type] == NULL)
+ bucket->refcounts[handle_type] = g_hash_table_new (g_direct_hash,
+ g_direct_equal);
+
+ refcounts = bucket->refcounts[handle_type];
+
+ for (i = 0; i < handles->len; i++)
+ {
+ gpointer handle = GUINT_TO_POINTER (g_array_index (handles, guint, i));
+ gsize r = GPOINTER_TO_SIZE (g_hash_table_lookup (refcounts, handle));
+
+ g_hash_table_insert (refcounts, handle, GSIZE_TO_POINTER (r + 1));
+ }
+}
+
+
+static void
+post_unref (TpConnection *connection,
+ const GError *error,
+ gpointer user_data,
+ GObject *weak_object)
+{
+ GArray *arr = user_data;
+
+ if (error == NULL)
+ {
+ DEBUG ("Released %u handles", arr->len);
+ }
+ else
+ {
+ guint i;
+
+ g_message ("Failed to release %u handles: %s %u: %s",
+ arr->len, g_quark_to_string (error->domain), error->code,
+ error->message);
+
+ for (i = 0; i < arr->len; i++)
+ {
+ g_message (" %u", g_array_index (arr, guint, i));
+ }
+ }
+}
+
+
+static void
+array_free_TRUE (gpointer p)
+{
+ g_array_free (p, TRUE);
+}
+
+
+/* Clean up after the connection is invalidated */
+void
+_tp_connection_clean_up_handle_refs (TpConnection *self)
+{
+ TpProxy *as_proxy = (TpProxy *) self;
+ DBusGConnection *g_connection = as_proxy->dbus_connection;
+ const gchar *object_path = as_proxy->object_path;
+ GHashTable *table;
+
+ DEBUG ("%p", self);
+
+ g_assert (as_proxy->invalidated != NULL);
+
+ if (connection_handle_refs_slot == -1)
+ return;
+
+ /* MT: if we become thread safe, the rest of this function needs a lock */
+ table = dbus_connection_get_data (dbus_g_connection_get_connection (
+ g_connection), connection_handle_refs_slot);
+
+ if (table == NULL)
+ return;
+
+ if (g_hash_table_remove (table, object_path) &&
+ g_hash_table_size (table) == 0)
+ {
+ /* this calls the destructor, g_hash_table_destroy */
+ dbus_connection_set_data (dbus_g_connection_get_connection (
+ g_connection), connection_handle_refs_slot, NULL, NULL);
+ }
+}
+
+
+/**
+ * tp_connection_unref_handles:
+ * @self: a connection
+ * @handle_type: a handle type
+ * @n_handles: the number of handles in @handles
+ * @handles: an array of @n_handles handles
+ *
+ * Release the reference to the handles in @handles that was obtained by
+ * calling tp_connection_hold_handles() or tp_connection_request_handles().
+ */
+void
+tp_connection_unref_handles (TpConnection *self,
+ TpHandleType handle_type,
+ guint n_handles,
+ const TpHandle *handles)
+{
+ TpProxy *as_proxy = (TpProxy *) self;
+ DBusGConnection *g_connection = as_proxy->dbus_connection;
+ const gchar *object_path = as_proxy->object_path;
+ GHashTable *table, *refcounts;
+ Bucket *bucket = NULL;
+ guint i;
+ GArray *unref;
+
+ DEBUG ("%p: %u handles of type %u", self, n_handles, handle_type);
+
+ if (as_proxy->invalidated != NULL)
+ {
+ return;
+ }
+
+ g_assert (handle_type < NUM_TP_HANDLE_TYPES);
+
+ /* MT: libdbus protects us, if so configured */
+ if (!dbus_connection_allocate_data_slot (&connection_handle_refs_slot))
+ oom ();
+
+ /* MT: if we become thread safe, the rest of this function needs a lock */
+ table = dbus_connection_get_data (dbus_g_connection_get_connection (
+ g_connection), connection_handle_refs_slot);
+
+ /* if there's no table, then we can't have a ref to the handles -
+ * user error */
+ g_return_if_fail (table != NULL);
+
+ bucket = g_hash_table_lookup (table, object_path);
+
+ /* if there's no bucket, then we can't have a ref to the handles -
+ * user error */
+ g_return_if_fail (bucket != NULL);
+ g_return_if_fail (bucket->refcounts[handle_type] != NULL);
+
+ refcounts = bucket->refcounts[handle_type];
+
+ unref = g_array_sized_new (FALSE, FALSE, sizeof (guint), n_handles);
+
+ for (i = 0; i < n_handles; i++)
+ {
+ gpointer handle = GUINT_TO_POINTER (handles[i]);
+ gsize r = GPOINTER_TO_SIZE (g_hash_table_lookup (refcounts, handle));
+
+ g_return_if_fail (handles[i] != 0);
+ /* if we have no refs, it's user error */
+ g_return_if_fail (r != 0);
+
+ if (r == 1)
+ {
+ DEBUG ("releasing handle %u", handles[i]);
+ g_array_append_val (unref, handles[i]);
+ g_hash_table_remove (refcounts, handle);
+ }
+ else
+ {
+ DEBUG ("decrementing handle %u to %" G_GSIZE_FORMAT, handles[i],
+ r - 1);
+ g_hash_table_insert (refcounts, handle, GSIZE_TO_POINTER (r - 1));
+ }
+ }
+
+ /* fire off the unref call asynchronously, ignore error if any.
+ * FIXME: perhaps this should be done idly, so we can combine unrefs? */
+ if (unref->len > 0)
+ {
+ DEBUG ("releasing %u handles", unref->len);
+
+ tp_cli_connection_call_release_handles (self, -1,
+ handle_type, unref, post_unref, unref, array_free_TRUE, NULL);
+ }
+
+ if (g_hash_table_size (refcounts) == 0)
+ {
+ g_hash_table_destroy (refcounts);
+ bucket->refcounts[handle_type] = NULL;
+
+ for (i = 1; i < NUM_TP_HANDLE_TYPES; i++)
+ {
+ if (bucket->refcounts[handle_type] != NULL)
+ return;
+ }
+
+ /* if still here, then there are no refs left on this connection */
+ g_hash_table_remove (table, object_path);
+
+ if (g_hash_table_size (table) == 0)
+ {
+ /* this calls the destructor, g_hash_table_destroy */
+ dbus_connection_set_data (dbus_g_connection_get_connection (
+ g_connection), connection_handle_refs_slot, NULL, NULL);
+ }
+ }
+}
+
+
+typedef struct {
+ TpHandleType handle_type;
+ GArray *handles;
+ gpointer user_data;
+ GDestroyNotify destroy;
+ TpConnectionHoldHandlesCb callback;
+} HoldHandlesContext;
+
+
+static void
+hold_handles_context_free (gpointer p)
+{
+ HoldHandlesContext *context = p;
+
+ if (context->destroy != NULL)
+ context->destroy (context->user_data);
+
+ g_array_free (context->handles, TRUE);
+
+ g_slice_free (HoldHandlesContext, context);
+}
+
+/**
+ * TpConnectionHoldHandlesCb:
+ * @connection: the connection
+ * @handle_type: the handle type that was passed to
+ * tp_connection_hold_handles()
+ * @n_handles: the number of handles that were passed to
+ * tp_connection_hold_handles() on success, or 0 on failure
+ * @handles: the same @n_handles handles that were passed to
+ * tp_connection_hold_handles() on success, or %NULL on failure
+ * @error: %NULL on success, or an error on failure
+ * @user_data: the same arbitrary pointer that was passed to
+ * tp_connection_hold_handles()
+ * @weak_object: the same object that was passed to
+ * tp_connection_hold_handles()
+ *
+ * Signature of the callback called when tp_connection_hold_handles() succeeds
+ * or fails.
+ *
+ * On success, the caller has one reference to each handle in @handles, which
+ * may be released later with tp_connection_unref_handles(). If not
+ * released, the handles will remain valid until @connection becomes invalid
+ * (signalled by TpProxy::invalidated).
+ *
+ * For convenience, the handle type and handles requested by the caller are
+ * passed through to this callback on success, so the caller does not have to
+ * include them in @user_data.
+ */
+
+static void
+connection_held_handles (TpConnection *self,
+ const GError *error,
+ gpointer user_data,
+ GObject *weak_object)
+{
+ HoldHandlesContext *context = user_data;
+
+ g_object_ref (self);
+
+ if (error == NULL)
+ {
+ DEBUG ("%u handles of type %u", context->handles->len,
+ context->handle_type);
+ /* On the Telepathy side, we have held these handles (at least once).
+ * On the GLib side, record that we have one reference */
+ _tp_connection_ref_handles (self, context->handle_type,
+ context->handles);
+
+ context->callback (self, context->handle_type, context->handles->len,
+ (const TpHandle *) context->handles->data, NULL,
+ context->user_data, weak_object);
+ }
+ else
+ {
+ DEBUG ("%u handles of type %u failed: %s %u: %s",
+ context->handles->len, context->handle_type,
+ g_quark_to_string (error->domain), error->code, error->message);
+ context->callback (self, context->handle_type, 0, NULL, error,
+ context->user_data, weak_object);
+ }
+
+ g_object_unref (self);
+}
+
+
+/**
+ * tp_connection_hold_handles:
+ * @self: a connection
+ * @timeout_ms: the timeout in milliseconds, or -1 to use the default
+ * @handle_type: the handle type
+ * @n_handles: the number of handles in @handles
+ * @handles: an array of handles
+ * @callback: called on success or failure (unless @weak_object has become
+ * unreferenced)
+ * @user_data: arbitrary user-supplied data
+ * @destroy: called to destroy @user_data after calling @callback, or when
+ * @weak_object becomes unreferenced (whichever occurs sooner)
+ * @weak_object: if not %NULL, an object to be weakly referenced: if it is
+ * destroyed, @callback will not be called
+ *
+ * Hold (ensure a reference to) the given handles, if they are valid.
+ *
+ * If they are valid, the callback will later be called with the given
+ * handles; if not all of them are valid, the callback will be called with
+ * an error.
+ */
+void
+tp_connection_hold_handles (TpConnection *self,
+ gint timeout_ms,
+ TpHandleType handle_type,
+ guint n_handles,
+ const TpHandle *handles,
+ TpConnectionHoldHandlesCb callback,
+ gpointer user_data,
+ GDestroyNotify destroy,
+ GObject *weak_object)
+{
+ HoldHandlesContext *context;
+
+ context = g_slice_new0 (HoldHandlesContext);
+ context->handle_type = handle_type;
+ context->user_data = user_data;
+ context->destroy = destroy;
+ context->handles = g_array_sized_new (FALSE, FALSE, sizeof (guint),
+ n_handles);
+ g_array_append_vals (context->handles, handles, n_handles);
+ context->callback = callback;
+
+ tp_cli_connection_call_hold_handles (self, timeout_ms, handle_type,
+ context->handles, connection_held_handles,
+ context, hold_handles_context_free, weak_object);
+}
+
+
+typedef struct {
+ TpHandleType handle_type;
+ guint n_ids;
+ gchar **ids;
+ gpointer user_data;
+ GDestroyNotify destroy;
+ TpConnectionRequestHandlesCb callback;
+} RequestHandlesContext;
+
+
+static void
+request_handles_context_free (gpointer p)
+{
+ RequestHandlesContext *context = p;
+
+ g_strfreev (context->ids);
+
+ if (context->destroy != NULL)
+ context->destroy (context->user_data);
+
+ g_slice_free (RequestHandlesContext, context);
+}
+
+
+/**
+ * TpConnectionRequestHandlesCb:
+ * @connection: the connection
+ * @handle_type: the handle type that was passed to
+ * tp_connection_request_handles()
+ * @n_handles: the number of IDs that were passed to
+ * tp_connection_request_handles() on success, or 0 on failure
+ * @handles: the @n_handles handles corresponding to @ids, in the same order,
+ * or %NULL on failure
+ * @ids: the same @n_handles IDs that were passed to
+ * tp_connection_request_handles() on success, or %NULL on failure
+ * @error: %NULL on success, or an error on failure
+ * @user_data: the same arbitrary pointer that was passed to
+ * tp_connection_request_handles()
+ * @weak_object: the same object that was passed to
+ * tp_connection_request_handles()
+ *
+ * Signature of the callback called when tp_connection_request_handles()
+ * succeeds or fails.
+ *
+ * On success, the caller has one reference to each handle in @handles, which
+ * may be released later with tp_connection_unref_handles(). If not
+ * released, the handles will remain valid until @connection becomes invalid
+ * (signalled by TpProxy::invalidated).
+ *
+ * For convenience, the handle type and IDs requested by the caller are
+ * passed through to this callback, so the caller does not have to include
+ * them in @user_data.
+ */
+
+
+static void
+connection_requested_handles (TpConnection *self,
+ const GArray *handles,
+ const GError *error,
+ gpointer user_data,
+ GObject *weak_object)
+{
+ RequestHandlesContext *context = user_data;
+
+ g_object_ref (self);
+
+ if (error == NULL)
+ {
+ if (G_UNLIKELY (g_strv_length (context->ids) != handles->len))
+ {
+ const gchar *cm = tp_proxy_get_bus_name ((TpProxy *) self);
+ GError *e = g_error_new (TP_DBUS_ERRORS, TP_DBUS_ERROR_INCONSISTENT,
+ "Connection manager %s is broken: we asked for %u "
+ "handles but RequestHandles returned %u",
+ cm, g_strv_length (context->ids), handles->len);
+
+ /* This CM is bad and wrong. We can't trust it to get anything
+ * right, so we'd probably better leak the handles, hence this
+ * early-return comes before recording that we have a ref to them. */
+ g_warning ("%s", e->message);
+
+ context->callback (self, context->handle_type, 0, NULL, NULL,
+ error, context->user_data, weak_object);
+ return;
+ }
+
+ DEBUG ("%u handles of type %u", handles->len,
+ context->handle_type);
+ /* On the Telepathy side, we have held these handles (at least once).
+ * On the GLib side, record that we have one reference */
+ _tp_connection_ref_handles (self, context->handle_type,
+ handles);
+
+ context->callback (self, context->handle_type, handles->len,
+ (const TpHandle *) handles->data,
+ (const gchar * const *) context->ids,
+ NULL, context->user_data, weak_object);
+ }
+ else
+ {
+ DEBUG ("%u handles of type %u failed: %s %u: %s",
+ g_strv_length (context->ids), context->handle_type,
+ g_quark_to_string (error->domain), error->code, error->message);
+ context->callback (self, context->handle_type, 0, NULL, NULL, error,
+ context->user_data, weak_object);
+ }
+
+ g_object_unref (self);
+}
+
+
+/**
+ * tp_connection_request_handles:
+ * @self: a connection
+ * @timeout_ms: the timeout in milliseconds, or -1 to use the default
+ * @handle_type: the handle type
+ * @ids: an array of string identifiers for which handles are required,
+ * terminated by %NULL
+ * @callback: called on success or failure (unless @weak_object has become
+ * unreferenced)
+ * @user_data: arbitrary user-supplied data
+ * @destroy: called to destroy @user_data after calling @callback, or when
+ * @weak_object becomes unreferenced (whichever occurs sooner)
+ * @weak_object: if not %NULL, an object to be weakly referenced: if it is
+ * destroyed, @callback will not be called
+ *
+ * Request the handles corresponding to the given identifiers, and if they
+ * are valid, hold (ensure a reference to) the corresponding handles.
+ *
+ * If they are valid, the callback will later be called with the given
+ * handles; if not all of them are valid, the callback will be called with
+ * an error.
+ */
+void
+tp_connection_request_handles (TpConnection *self,
+ gint timeout_ms,
+ TpHandleType handle_type,
+ const gchar * const *ids,
+ TpConnectionRequestHandlesCb callback,
+ gpointer user_data,
+ GDestroyNotify destroy,
+ GObject *weak_object)
+{
+ RequestHandlesContext *context;
+
+ context = g_slice_new0 (RequestHandlesContext);
+ context->handle_type = handle_type;
+
+ /* g_strv_length is not NULL-safe - it's simpler to ensure that
+ * context->ids is not NULL */
+ if (ids == NULL)
+ context->ids = g_new0 (gchar *, 1);
+ else
+ context->ids = g_strdupv ((GStrv) ids);
+
+ context->user_data = user_data;
+ context->destroy = destroy;
+ context->callback = callback;
+
+ tp_cli_connection_call_request_handles (self, timeout_ms, handle_type,
+ (const gchar **) context->ids, connection_requested_handles,
+ context, request_handles_context_free, weak_object);
+}
diff --git a/telepathy-glib/connection-internal.h b/telepathy-glib/connection-internal.h
index 10d2b49..11f17cd 100644
--- a/telepathy-glib/connection-internal.h
+++ b/telepathy-glib/connection-internal.h
@@ -29,6 +29,8 @@ G_BEGIN_DECLS
const GArray *_tp_connection_get_contact_attribute_interfaces (
TpConnection *self);
+void _tp_connection_clean_up_handle_refs (TpConnection *self);
+
G_END_DECLS
#endif
diff --git a/telepathy-glib/connection.c b/telepathy-glib/connection.c
index 538f624..4f45f1c 100644
--- a/telepathy-glib/connection.c
+++ b/telepathy-glib/connection.c
@@ -460,6 +460,9 @@ tp_connection_constructor (GType type,
tp_cli_connection_call_get_status (self, -1,
tp_connection_got_status_cb, NULL, NULL, NULL);
+ g_signal_connect (self, "invalidated",
+ G_CALLBACK (_tp_connection_clean_up_handle_refs), NULL);
+
DEBUG ("Returning %p", self);
return (GObject *) self;
}
diff --git a/telepathy-glib/connection.h b/telepathy-glib/connection.h
index 8dc2bcf..557b464 100644
--- a/telepathy-glib/connection.h
+++ b/telepathy-glib/connection.h
@@ -23,6 +23,7 @@
#define __TP_CONNECTION_H__
#include <telepathy-glib/enums.h>
+#include <telepathy-glib/handle.h>
#include <telepathy-glib/proxy.h>
G_BEGIN_DECLS
@@ -102,6 +103,30 @@ void tp_connection_init_known_interfaces (void);
gint tp_connection_presence_type_cmp_availability (TpConnectionPresenceType p1,
TpConnectionPresenceType p2);
+/* connection-handles.c */
+
+typedef void (*TpConnectionHoldHandlesCb) (TpConnection *connection,
+ TpHandleType handle_type, guint n_handles, const TpHandle *handles,
+ const GError *error, gpointer user_data, GObject *weak_object);
+
+void tp_connection_hold_handles (TpConnection *self, gint timeout_ms,
+ TpHandleType handle_type, guint n_handles, const TpHandle *handles,
+ TpConnectionHoldHandlesCb callback,
+ gpointer user_data, GDestroyNotify destroy, GObject *weak_object);
+
+typedef void (*TpConnectionRequestHandlesCb) (TpConnection *connection,
+ TpHandleType handle_type,
+ guint n_handles, const TpHandle *handles, const gchar * const *ids,
+ const GError *error, gpointer user_data, GObject *weak_object);
+
+void tp_connection_request_handles (TpConnection *self, gint timeout_ms,
+ TpHandleType handle_type, const gchar * const *ids,
+ TpConnectionRequestHandlesCb callback,
+ gpointer user_data, GDestroyNotify destroy, GObject *weak_object);
+
+void tp_connection_unref_handles (TpConnection *self,
+ TpHandleType handle_type, guint n_handles, const TpHandle *handles);
+
G_END_DECLS
#include <telepathy-glib/_gen/tp-cli-connection.h>
--
1.5.6.5
More information about the Telepathy-commits
mailing list