[Telepathy-commits] [telepathy-mission-control/master] Account cache in McAccountManager and call_when_ready_with_accounts
mardy
mardy at 64d1ce6a-1406-0410-9ac8-afed03b27183
Mon Nov 17 00:05:13 PST 2008
Implement an cache (as GHashTable) of McAccounts into McAccountManager.
Add mc_account_manager_get_account() to get an account from the cache,
instantiating a new object in case it was not there.
Add mc_account_manager_call_when_ready_with_accounts(), which calls a callback
when the account manager and all the known accounts are ready.
git-svn-id: https://projects.maemo.org/svn/chavo/trunk/framework/open/telepathy-mission-control@20535 64d1ce6a-1406-0410-9ac8-afed03b27183
---
libmcclient/mc-account-manager.c | 267 ++++++++++++++++++++++++++++++++++++++
libmcclient/mc-account-manager.h | 11 ++
2 files changed, 278 insertions(+), 0 deletions(-)
diff --git a/libmcclient/mc-account-manager.c b/libmcclient/mc-account-manager.c
index 669d835..fe9777d 100644
--- a/libmcclient/mc-account-manager.c
+++ b/libmcclient/mc-account-manager.c
@@ -70,10 +70,22 @@ struct _McAccountManager {
struct _McAccountManagerPrivate {
McAccountManagerProps *props;
+ GPtrArray *account_ifaces;
+ GHashTable *accounts;
};
G_DEFINE_TYPE (McAccountManager, mc_account_manager, TP_TYPE_PROXY);
+typedef struct {
+ McAccountManagerWhenReadyObjectCb callback;
+ gpointer user_data;
+ GDestroyNotify destroy;
+ GError *error;
+ McAccountManager *manager;
+ gint ref_count;
+ gint cb_remaining;
+} ReadyWithAccountsData;
+
enum
{
ACCOUNT_CREATED,
@@ -93,6 +105,100 @@ static McIfaceDescription iface_description = {
static void
+ready_with_accounts_data_free (gpointer ptr)
+{
+ ReadyWithAccountsData *cb_data = ptr;
+
+ cb_data->ref_count--;
+ g_assert (cb_data->ref_count >= 0);
+ if (cb_data->ref_count == 0)
+ {
+ if (cb_data->destroy)
+ cb_data->destroy (cb_data->user_data);
+ if (cb_data->error)
+ g_error_free (cb_data->error);
+ g_slice_free (ReadyWithAccountsData, cb_data);
+ }
+}
+
+static void
+account_ready_cb (TpProxy *proxy, const GError *error,
+ gpointer user_data, GObject *weak_object)
+{
+ ReadyWithAccountsData *cb_data = user_data;
+
+ if (error)
+ {
+ if (cb_data->error == NULL)
+ cb_data->error = g_error_copy (error);
+ }
+ cb_data->cb_remaining--;
+ if (cb_data->cb_remaining == 0)
+ {
+ if (cb_data->callback)
+ cb_data->callback (cb_data->manager, error, cb_data->user_data,
+ weak_object);
+ }
+}
+
+static void
+get_accounts_ready (McAccountManager *manager, gchar **accounts,
+ ReadyWithAccountsData *cb_data, GObject *weak_object)
+{
+ McAccountManagerPrivate *priv = manager->priv;
+ GQuark *ifaces;
+ guint i, n_ifaces;
+
+ ifaces = (GQuark *)priv->account_ifaces->pdata;
+ n_ifaces = priv->account_ifaces->len;
+
+ for (i = 0; accounts[i] != NULL; i++)
+ {
+ McAccount *account;
+
+ account = mc_account_manager_get_account (manager, accounts[i]);
+ cb_data->ref_count++;
+ cb_data->cb_remaining++;
+ _mc_iface_call_when_all_readyv (TP_PROXY (account), MC_TYPE_ACCOUNT,
+ account_ready_cb, cb_data,
+ ready_with_accounts_data_free,
+ weak_object,
+ n_ifaces, ifaces);
+ }
+}
+
+static void
+manager_ready_cb (McAccountManager *manager, const GError *error,
+ gpointer user_data, GObject *weak_object)
+{
+ McAccountManagerPrivate *priv = manager->priv;
+ ReadyWithAccountsData *cb_data = user_data;
+
+ if (error)
+ {
+ if (cb_data->callback)
+ cb_data->callback (manager, error, cb_data->user_data,
+ weak_object);
+ return;
+ }
+
+ /* now we have the account names; create all accounts and get them ready */
+ get_accounts_ready (manager, priv->props->valid_accounts,
+ cb_data, weak_object);
+ get_accounts_ready (manager, priv->props->invalid_accounts,
+ cb_data, weak_object);
+ cb_data->cb_remaining--;
+ if (cb_data->cb_remaining == 0)
+ {
+ /* either we have no accounts, or they were all ready; in any case, we
+ * can invoke the callback now */
+ if (cb_data->callback)
+ cb_data->callback (manager, error, cb_data->user_data,
+ weak_object);
+ }
+}
+
+static void
mc_account_manager_init (McAccountManager *self)
{
self->priv = G_TYPE_INSTANCE_GET_PRIVATE(self, MC_TYPE_ACCOUNT_MANAGER,
@@ -113,6 +219,20 @@ manager_props_free (McAccountManagerProps *props)
}
static void
+dispose (GObject *object)
+{
+ McAccountManagerPrivate *priv = MC_ACCOUNT_MANAGER (object)->priv;
+
+ if (priv->accounts)
+ {
+ g_hash_table_destroy (priv->accounts);
+ priv->accounts = NULL;
+ }
+
+ G_OBJECT_CLASS (mc_account_manager_parent_class)->dispose (object);
+}
+
+static void
finalize (GObject *object)
{
McAccountManagerPrivate *priv = MC_ACCOUNT_MANAGER (object)->priv;
@@ -120,6 +240,9 @@ finalize (GObject *object)
if (priv->props)
manager_props_free (priv->props);
+ if (priv->account_ifaces)
+ g_ptr_array_free (priv->account_ifaces, TRUE);
+
G_OBJECT_CLASS (mc_account_manager_parent_class)->finalize (object);
}
@@ -132,6 +255,7 @@ mc_account_manager_class_init (McAccountManagerClass *klass)
g_type_class_add_private (object_class, sizeof (McAccountManagerPrivate));
object_class->finalize = finalize;
+ object_class->dispose = dispose;
/* the API is stateless, so we can keep the same proxy across restarts */
proxy_class->must_have_unique_name = FALSE;
@@ -467,3 +591,146 @@ mc_account_manager_call_when_iface_ready (McAccountManager *manager,
user_data, destroy, weak_object);
}
+/**
+ * mc_account_manager_call_when_ready_with_accounts:
+ * @manager: the #McAccountManager.
+ * @callback: called when the account manager and all the accounts are ready,
+ * or some error occurs.
+ * @user_data: user data to be passed to @callback.
+ * @destroy: called with the user_data as argument, after the call has
+ * succeeded, failed or been cancelled.
+ * @weak_object: If not %NULL, a #GObject which will be weakly referenced; if
+ * it is destroyed, this call will automatically be cancelled. Must be %NULL if
+ * @callback is %NULL
+ * @Varargs: a list of #GQuark types representing the account interfaces to
+ * process, followed by %0.
+ *
+ * This is a convenience function that waits for the account manager to be
+ * ready, after which it requests the desired interfaces out of all accounts
+ * and returns only once they are all ready.
+ * After this function has been called, all new accounts that should appear
+ * will have their desired interfaces retrieved, and the "account-ready" signal
+ * will be emitted.
+ */
+void
+mc_account_manager_call_when_ready_with_accounts (McAccountManager *manager,
+ McAccountManagerWhenReadyObjectCb callback,
+ gpointer user_data,
+ GDestroyNotify destroy,
+ GObject *weak_object, ...)
+{
+ McAccountManagerPrivate *priv;
+ GQuark iface;
+ guint len, i;
+ ReadyWithAccountsData *cb_data;
+ va_list ifaces;
+
+ g_return_if_fail (MC_IS_ACCOUNT_MANAGER (manager));
+ priv = manager->priv;
+
+ /* Add the requested interfaces to the account_ifaces array */
+ va_start (ifaces, weak_object);
+
+ if (!priv->account_ifaces)
+ priv->account_ifaces = g_ptr_array_sized_new (8);
+
+ len = priv->account_ifaces->len;
+ for (iface = va_arg (ifaces, GQuark); iface != 0;
+ iface = va_arg (ifaces, GQuark))
+ {
+ for (i = 0; i < len; i++)
+ {
+ gpointer ptr = g_ptr_array_index (priv->account_ifaces, i);
+
+ if ((GQuark)GPOINTER_TO_UINT (ptr) == iface)
+ break;
+ }
+ if (i < len) /* found */
+ continue;
+
+ g_ptr_array_add (priv->account_ifaces, GUINT_TO_POINTER(iface));
+ }
+ va_end (ifaces);
+
+ /* Get the account manager ready; in the callback, we'll make the accounts
+ * ready. */
+ cb_data = g_slice_new0 (ReadyWithAccountsData);
+ cb_data->callback = callback;
+ cb_data->user_data = user_data;
+ cb_data->destroy = destroy;
+ cb_data->manager = manager;
+ cb_data->ref_count = 1;
+ cb_data->cb_remaining = 1;
+
+ mc_account_manager_call_when_iface_ready (manager,
+ MC_IFACE_QUARK_ACCOUNT_MANAGER,
+ manager_ready_cb,
+ cb_data,
+ ready_with_accounts_data_free,
+ weak_object);
+}
+
+/**
+ * mc_account_manager_get_account:
+ * @manager: the #McAccountManager.
+ * @account_name: the name of a #McAccount, or an object path.
+ *
+ * Get the #McAccount for the account whose name is @account_name. It looks up
+ * the accounts from an internal cache, and if the #McAccount is not found
+ * there it is created, provided that the @account_name refers to a proper
+ * account.
+ *
+ * Returns: a #McAccount, not referenced. %NULL if @account_name does not match
+ * any account.
+ */
+McAccount *
+mc_account_manager_get_account (McAccountManager *manager,
+ const gchar *account_name)
+{
+ McAccountManagerPrivate *priv;
+ McAccount *account;
+ const gchar *object_path, *name;
+
+ g_return_val_if_fail (MC_IS_ACCOUNT_MANAGER (manager), NULL);
+ g_return_val_if_fail (account_name != NULL, NULL);
+
+ priv = manager->priv;
+ if (G_UNLIKELY (!priv->accounts))
+ {
+ priv->accounts = g_hash_table_new_full (g_str_hash, g_str_equal,
+ NULL, g_object_unref);
+ }
+ g_return_val_if_fail (priv->accounts != NULL, NULL);
+
+ /* @account_name can be an account name or an object path; now we need the
+ * name */
+ if (strncmp (account_name, MC_ACCOUNT_DBUS_OBJECT_BASE,
+ MC_ACCOUNT_DBUS_OBJECT_BASE_LEN) == 0)
+ {
+ object_path = account_name;
+ name = account_name + MC_ACCOUNT_DBUS_OBJECT_BASE_LEN;
+ }
+ else
+ {
+ object_path = NULL;
+ name = account_name;
+ }
+
+ account = g_hash_table_lookup (priv->accounts, name);
+ if (!account)
+ {
+ if (!object_path)
+ object_path = g_strconcat (MC_ACCOUNT_DBUS_OBJECT_BASE,
+ account_name, NULL);
+ account = mc_account_new (TP_PROXY(manager)->dbus_daemon,
+ object_path);
+ if (G_LIKELY (account))
+ {
+ g_hash_table_insert (priv->accounts, account->name, account);
+ }
+ if (object_path != account_name)
+ g_free ((gchar *)object_path);
+ }
+ return account;
+}
+
diff --git a/libmcclient/mc-account-manager.h b/libmcclient/mc-account-manager.h
index 3da0fc8..1b02316 100644
--- a/libmcclient/mc-account-manager.h
+++ b/libmcclient/mc-account-manager.h
@@ -31,6 +31,8 @@ typedef struct _McAccountManager McAccountManager;
typedef struct _McAccountManagerClass McAccountManagerClass;
typedef struct _McAccountManagerPrivate McAccountManagerPrivate;
+#include <libmcclient/mc-account.h>
+
GType mc_account_manager_get_type (void);
#define MC_TYPE_ACCOUNT_MANAGER \
@@ -75,6 +77,15 @@ void mc_account_manager_call_when_iface_ready (McAccountManager *manager,
gpointer user_data,
GDestroyNotify destroy,
GObject *weak_object);
+void mc_account_manager_call_when_ready_with_accounts (
+ McAccountManager *manager,
+ McAccountManagerWhenReadyObjectCb callback,
+ gpointer user_data,
+ GDestroyNotify destroy,
+ GObject *weak_object, ...);
+
+McAccount *mc_account_manager_get_account (McAccountManager *manager,
+ const gchar *account_name);
G_END_DECLS
--
1.5.6.5
More information about the Telepathy-commits
mailing list