diff options
author | Simon McVittie <simon.mcvittie@collabora.co.uk> | 2012-07-30 15:41:00 +0100 |
---|---|---|
committer | Simon McVittie <simon.mcvittie@collabora.co.uk> | 2012-07-30 15:41:00 +0100 |
commit | ddd21ba082fd5a90e93052463d00927853a8ecea (patch) | |
tree | ff8a133eefd41fc1ed5792029091c6c0e294701d | |
parent | ee34e40100294702f9bcde300056f659de4e3ed8 (diff) | |
parent | 694be3e53cb8523f86afb73f3ecc0f089610375a (diff) | |
download | telepathy-mission-control-ddd21ba082fd5a90e93052463d00927853a8ecea.tar.gz |
Merge branch 'one-channel-should-be-enough-for-anyone'
Reviewed-by: Jonny Lamb <jonny.lamb@collabora.co.uk>
Bug: https://bugs.freedesktop.org/show_bug.cgi?id=52305
-rw-r--r-- | src/client-registry.c | 33 | ||||
-rw-r--r-- | src/client-registry.h | 2 | ||||
-rw-r--r-- | src/mcd-connection.c | 87 | ||||
-rw-r--r-- | src/mcd-connection.h | 9 | ||||
-rw-r--r-- | src/mcd-dispatch-operation-priv.h | 11 | ||||
-rw-r--r-- | src/mcd-dispatch-operation.c | 401 | ||||
-rw-r--r-- | src/mcd-dispatcher-priv.h | 6 | ||||
-rw-r--r-- | src/mcd-dispatcher.c | 167 | ||||
-rw-r--r-- | src/plugin-dispatch-operation.c | 22 | ||||
-rw-r--r-- | tests/twisted/dispatcher/exploding-bundles.py | 156 |
10 files changed, 418 insertions, 476 deletions
diff --git a/src/client-registry.c b/src/client-registry.c index 3e01a66d..acc65e78 100644 --- a/src/client-registry.c +++ b/src/client-registry.c @@ -604,11 +604,10 @@ GList * _mcd_client_registry_list_possible_handlers (McdClientRegistry *self, const gchar *preferred_handler, GHashTable *request_props, - const GList *channels, + TpChannel *channel, const gchar *must_have_unique_name) { GList *handlers = NULL; - const GList *iter; GList *handlers_iter; GHashTableIter client_iter; gpointer client_p; @@ -618,7 +617,8 @@ _mcd_client_registry_list_possible_handlers (McdClientRegistry *self, while (g_hash_table_iter_next (&client_iter, NULL, &client_p)) { McdClientProxy *client = MCD_CLIENT_PROXY (client_p); - gsize total_quality = 0; + GHashTable *properties; + gsize quality; if (must_have_unique_name != NULL && tp_strdiff (must_have_unique_name, @@ -636,48 +636,33 @@ _mcd_client_registry_list_possible_handlers (McdClientRegistry *self, continue; } - if (channels == NULL) + if (channel == NULL) { - /* We don't know any channels' properties (the next loop will not + /* We don't know the channel's properties (the next part will not * execute), so we must work out the quality of match from the * channel request. We can assume that the request will return one * channel, with the requested properties, plus Requested == TRUE. */ g_assert (request_props != NULL); - total_quality = _mcd_client_match_filters (request_props, + quality = _mcd_client_match_filters (request_props, _mcd_client_proxy_get_handler_filters (client), TRUE); } - - for (iter = channels; iter != NULL; iter = iter->next) + else { - TpChannel *channel = iter->data; - GHashTable *properties; - guint quality; - g_assert (TP_IS_CHANNEL (channel)); properties = tp_channel_borrow_immutable_properties (channel); quality = _mcd_client_match_filters (properties, _mcd_client_proxy_get_handler_filters (client), FALSE); - - if (quality == 0) - { - total_quality = 0; - break; - } - else - { - total_quality += quality; - } } - if (total_quality > 0) + if (quality > 0) { PossibleHandler *ph = g_slice_new0 (PossibleHandler); ph->client = client; ph->bypass = _mcd_client_proxy_get_bypass_approval (client); - ph->quality = total_quality; + ph->quality = quality; handlers = g_list_prepend (handlers, ph); } diff --git a/src/client-registry.h b/src/client-registry.h index 88f19569..ec2e167c 100644 --- a/src/client-registry.h +++ b/src/client-registry.h @@ -82,7 +82,7 @@ G_GNUC_INTERNAL void _mcd_client_registry_init_hash_iter ( G_GNUC_INTERNAL GList *_mcd_client_registry_list_possible_handlers ( McdClientRegistry *self, const gchar *preferred_handler, - GHashTable *request_props, const GList *channels, + GHashTable *request_props, TpChannel *channel, const gchar *must_have_unique_name); G_END_DECLS diff --git a/src/mcd-connection.c b/src/mcd-connection.c index 166f9de4..f2518491 100644 --- a/src/mcd-connection.c +++ b/src/mcd-connection.c @@ -573,9 +573,8 @@ on_new_channel (TpConnection *proxy, const gchar *chan_obj_path, * AddDispatchOperation or HandleChannels. * * We assume that channels without suppress_handler are incoming. */ - _mcd_dispatcher_take_channels (priv->dispatcher, - g_list_prepend (NULL, channel), - suppress_handler, suppress_handler); + _mcd_dispatcher_add_channel (priv->dispatcher, channel, + suppress_handler, suppress_handler); } } @@ -1171,6 +1170,10 @@ connect_cb (TpConnection *tp_conn, const GError *error, } } +static gboolean +_mcd_connection_request_channel (McdConnection *connection, + McdChannel *channel); + static void request_unrequested_channels (McdConnection *connection) { @@ -1216,16 +1219,16 @@ mcd_connection_find_channel_by_path (McdConnection *connection, return NULL; } +static gboolean mcd_connection_need_dispatch (McdConnection *connection, + const gchar *object_path, + GHashTable *props); + static void on_new_channels (TpConnection *proxy, const GPtrArray *channels, gpointer user_data, GObject *weak_object) { McdConnection *connection = MCD_CONNECTION (weak_object); McdConnectionPrivate *priv = user_data; - McdChannel *channel; - GList *channel_list = NULL; - gboolean requested = FALSE; - gboolean only_observe = FALSE; guint i; if (DEBUGGING) @@ -1256,9 +1259,6 @@ on_new_channels (TpConnection *proxy, const GPtrArray *channels, * FALSE: they'll also be in Channels in the GetAll(Requests) result */ if (!priv->dispatched_initial_channels) return; - only_observe = ! MCD_CONNECTION_GET_CLASS (connection)->need_dispatch ( - connection, channels); - sp_timestamp ("NewChannels received"); for (i = 0; i < channels->len; i++) { @@ -1266,11 +1266,17 @@ on_new_channels (TpConnection *proxy, const GPtrArray *channels, const gchar *object_path; GHashTable *props; GValue *value; + gboolean requested = FALSE; + gboolean only_observe = FALSE; + McdChannel *channel; va = g_ptr_array_index (channels, i); object_path = g_value_get_boxed (va->values); props = g_value_get_boxed (va->values + 1); + only_observe = !mcd_connection_need_dispatch (connection, object_path, + props); + /* Don't do anything for requested channels */ value = g_hash_table_lookup (props, TP_IFACE_CHANNEL ".Requested"); if (value && g_value_get_boolean (value)) @@ -1289,17 +1295,15 @@ on_new_channels (TpConnection *proxy, const GPtrArray *channels, MCD_MISSION (channel)); } - channel_list = g_list_prepend (channel_list, channel); - } + if (!requested) + { + /* we always dispatch unrequested (incoming) channels */ + only_observe = FALSE; + } - if (!requested) - { - /* we always dispatch unrequested (incoming) channels */ - only_observe = FALSE; + _mcd_dispatcher_add_channel (priv->dispatcher, channel, requested, + only_observe); } - - _mcd_dispatcher_take_channels (priv->dispatcher, channel_list, requested, - only_observe); } static void @@ -2125,18 +2129,19 @@ _mcd_connection_get_property (GObject * obj, guint prop_id, /* * mcd_connection_need_dispatch: * @connection: the #McdConnection. - * @channels: array of #McdChannel elements. + * @object_path: the object path of the new channel (only for debugging) + * @props: the properties of the new channel * * This functions must be called in response to a NewChannels signals, and is * responsible for deciding whether MC must handle the channels or not. */ static gboolean mcd_connection_need_dispatch (McdConnection *connection, - const GPtrArray *channels) + const gchar *object_path, + GHashTable *props) { McdAccount *account = mcd_connection_get_account (connection); - gboolean any_requested = FALSE, requested_by_us = FALSE; - guint i; + gboolean requested = FALSE, requested_by_us = FALSE; if (_mcd_account_needs_dispatch (account)) { @@ -2149,31 +2154,18 @@ mcd_connection_need_dispatch (McdConnection *connection, * have no McdChannel object associated: these are the channels directly * requested to the CM by some other application, and we must ignore them */ - for (i = 0; i < channels->len; i++) - { - GValueArray *va; - const gchar *object_path; - GHashTable *props; - gboolean requested; - va = g_ptr_array_index (channels, i); - object_path = g_value_get_boxed (va->values); - props = g_value_get_boxed (va->values + 1); - - requested = tp_asv_get_boolean (props, TP_IFACE_CHANNEL ".Requested", - NULL); - if (requested) - { - any_requested = TRUE; - - if (mcd_connection_find_channel_by_path (connection, object_path)) - requested_by_us = TRUE; - } + requested = tp_asv_get_boolean (props, TP_IFACE_CHANNEL ".Requested", + NULL); + if (requested) + { + if (mcd_connection_find_channel_by_path (connection, object_path)) + requested_by_us = TRUE; } /* handle only bundles which were not requested or that were requested * through MC */ - return !any_requested || requested_by_us; + return !requested || requested_by_us; } gboolean @@ -2205,7 +2197,8 @@ _mcd_connection_target_handle_is_urgent (McdConnection *self, guint handle) } static gboolean -_mcd_connection_request_channel (McdConnection *connection, McdChannel *channel) +_mcd_connection_request_channel (McdConnection *connection, + McdChannel *channel) { McdConnectionPrivate *priv = MCD_CONNECTION_PRIV (connection); gboolean ret; @@ -2253,9 +2246,6 @@ mcd_connection_class_init (McdConnectionClass * klass) object_class->set_property = _mcd_connection_set_property; object_class->get_property = _mcd_connection_get_property; - klass->need_dispatch = mcd_connection_need_dispatch; - klass->request_channel = _mcd_connection_request_channel; - _mcd_ext_register_dbus_glib_marshallers (); tp_connection_init_known_interfaces (); @@ -2479,8 +2469,7 @@ mcd_connection_request_channel (McdConnection *connection, mcd_operation_take_mission (MCD_OPERATION (connection), MCD_MISSION (channel)); - return MCD_CONNECTION_GET_CLASS (connection)->request_channel (connection, - channel); + return _mcd_connection_request_channel (connection, channel); } void diff --git a/src/mcd-connection.h b/src/mcd-connection.h index 6ea414bc..8f450b92 100644 --- a/src/mcd-connection.h +++ b/src/mcd-connection.h @@ -55,15 +55,6 @@ struct _McdConnection struct _McdConnectionClass { McdOperationClass parent_class; - gboolean (*need_dispatch) (McdConnection *connection, - const GPtrArray *channels); - void (*_mc_reserved2) (void); - gboolean (*request_channel) (McdConnection *connection, - McdChannel *channel); - void (*_mc_reserved3) (void); - void (*_mc_reserved4) (void); - void (*_mc_reserved5) (void); - void (*_mc_reserved6) (void); }; #include "mcd-dispatcher.h" diff --git a/src/mcd-dispatch-operation-priv.h b/src/mcd-dispatch-operation-priv.h index 933c12a4..ce168fc3 100644 --- a/src/mcd-dispatch-operation-priv.h +++ b/src/mcd-dispatch-operation-priv.h @@ -69,15 +69,18 @@ G_GNUC_INTERNAL void _mcd_dispatch_operation_approve #define MCD_DISPATCH_OPERATION_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), MCD_TYPE_DISPATCH_OPERATION, McdDispatchOperationClass)) G_GNUC_INTERNAL McdDispatchOperation *_mcd_dispatch_operation_new ( - McdClientRegistry *client_registry, McdHandlerMap *handler_map, - gboolean needs_approval, gboolean observe_only, GList *channels, + McdClientRegistry *client_registry, + McdHandlerMap *handler_map, + gboolean needs_approval, + gboolean observe_only, + McdChannel *channel, const gchar * const *possible_handlers); G_GNUC_INTERNAL gboolean _mcd_dispatch_operation_has_channel ( McdDispatchOperation *self, McdChannel *channel); -G_GNUC_INTERNAL const GList *_mcd_dispatch_operation_peek_channels ( +G_GNUC_INTERNAL McdChannel *_mcd_dispatch_operation_peek_channel ( McdDispatchOperation *self); -G_GNUC_INTERNAL GList *_mcd_dispatch_operation_dup_channels ( +G_GNUC_INTERNAL McdChannel *_mcd_dispatch_operation_dup_channel ( McdDispatchOperation *self); G_GNUC_INTERNAL gboolean _mcd_dispatch_operation_is_finished ( diff --git a/src/mcd-dispatch-operation.c b/src/mcd-dispatch-operation.c index 6a4a2877..ac036f0c 100644 --- a/src/mcd-dispatch-operation.c +++ b/src/mcd-dispatch-operation.c @@ -196,14 +196,14 @@ struct _McdDispatchOperationPrivate McdAccount *account; McdConnection *connection; - /* Owned McdChannels we're dispatching */ - GList *channels; - /* Owned McdChannels for which we can't emit ChannelLost yet, in - * reverse chronological order */ - GList *lost_channels; - - /* If TRUE, either the channels being dispatched were requested, or they - * were pre-approved by being returned as a response to another request, + /* Owned McdChannel we're dispatching */ + McdChannel *channel; + /* If non-NULL, we have lost the McdChannel but can't emit + * ChannelLost yet */ + McdChannel *lost_channel; + + /* If TRUE, either the channel being dispatched was requested, or it + * was pre-approved by being returned as a response to another request, * or a client approved processing with arbitrary handlers */ gboolean approved; @@ -246,7 +246,7 @@ struct _McdDispatchOperationPrivate /* If TRUE, we're dispatching a channel request and it was cancelled */ gboolean cancelled; - /* if TRUE, these channels were requested "behind our back", so stop + /* if TRUE, this channel was requested "behind our back", so stop * after observers */ gboolean observe_only; @@ -345,7 +345,7 @@ _mcd_dispatch_operation_dec_ado_pending (McdDispatchOperation *self) if (self->priv->ado_pending == 0 && !self->priv->accepted_by_an_approver) { - DEBUG ("No approver accepted the channels; considering them to be " + DEBUG ("No approver accepted the channel; considering it to be " "approved"); g_queue_push_tail (self->priv->approvals, approval_new (APPROVAL_TYPE_NO_APPROVERS)); @@ -429,7 +429,7 @@ _mcd_dispatch_operation_check_client_locks (McdDispatchOperation *self) if (!self->priv->tried_handlers_before_approval && !_mcd_dispatch_operation_handlers_can_bypass_approval (self) && self->priv->delay_approver_observers_pending == 0 - && self->priv->channels != NULL && + && self->priv->channel != NULL && ! _mcd_plugin_dispatch_operation_will_terminate ( self->priv->plugin_api)) { @@ -463,7 +463,7 @@ _mcd_dispatch_operation_check_client_locks (McdDispatchOperation *self) return; } - /* if a handler has claimed or accepted the channels, we have nothing to + /* if a handler has claimed or accepted the channel, we have nothing to * do */ if (self->priv->result != NULL) { @@ -481,24 +481,21 @@ _mcd_dispatch_operation_check_client_locks (McdDispatchOperation *self) if (_mcd_dispatch_operation_is_internal (self)) { - guint i = 0; - GList *list = self->priv->channels; - DEBUG ("Invoking internal handlers for requests"); - for (; list != NULL; list = g_list_next (list), i++) + if (self->priv->channel != NULL) { - McdChannel *channel = list->data; + McdChannel *channel = self->priv->channel; McdRequest *request = _mcd_channel_get_request (channel); - if (request == NULL) - continue; - - DEBUG ("Internal handler for request channel #%u", i); - _mcd_handler_map_set_channel_handled_internally ( - self->priv->handler_map, - mcd_channel_get_tp_channel (channel), - _mcd_dispatch_operation_get_account_path (self)); - _mcd_request_handle_internally (request, channel, TRUE); + if (request != NULL) + { + DEBUG ("Internal handler for request channel"); + _mcd_handler_map_set_channel_handled_internally ( + self->priv->handler_map, + mcd_channel_get_tp_channel (channel), + _mcd_dispatch_operation_get_account_path (self)); + _mcd_request_handle_internally (request, channel, TRUE); + } } /* The rest of this function deals with externally handled requests: * @@ -524,7 +521,6 @@ _mcd_dispatch_operation_check_client_locks (McdDispatchOperation *self) /* if we've been claimed, respond, then do not call HandleChannels */ if (approval != NULL && approval->type == APPROVAL_TYPE_CLAIM) { - const GList *list; /* this needs to be copied because we don't use it til after we've * freed approval->context */ gchar *caller = g_strdup (dbus_g_method_get_sender ( @@ -534,9 +530,9 @@ _mcd_dispatch_operation_check_client_locks (McdDispatchOperation *self) * failure */ g_queue_pop_head (self->priv->approvals); - for (list = self->priv->channels; list != NULL; list = list->next) + if (self->priv->channel != NULL) { - McdChannel *channel = MCD_CHANNEL (list->data); + McdChannel *channel = self->priv->channel; mcd_dispatch_operation_set_channel_handled_by (self, channel, caller, NULL); @@ -615,7 +611,7 @@ _mcd_dispatch_operation_check_client_locks (McdDispatchOperation *self) enum { PROP_0, - PROP_CHANNELS, + PROP_CHANNEL, PROP_CLIENT_REGISTRY, PROP_HANDLER_MAP, PROP_POSSIBLE_HANDLERS, @@ -724,8 +720,16 @@ get_channels (TpSvcDBusProperties *iface, const gchar *name, GValue *value) DEBUG ("called for %s", self->priv->unique_name); g_value_init (value, TP_ARRAY_TYPE_CHANNEL_DETAILS_LIST); + + if (self->priv->channel == NULL) + { + g_value_take_boxed (value, g_ptr_array_sized_new (0)); + return; + } + g_value_take_boxed (value, - _mcd_tp_channel_details_build_from_list (self->priv->channels)); + _mcd_tp_channel_details_build_from_tp_chan ( + mcd_channel_get_tp_channel (self->priv->channel))); } static void @@ -868,7 +872,8 @@ _mcd_dispatch_operation_finish (McdDispatchOperation *operation, else { /* Handling finished for some other reason: perhaps the - * channel was claimed, or perhaps we ran out of channels. + * channel was claimed, or perhaps it closed or we were + * told to forget about it. */ DEBUG ("HandleWith -> error: %s %d: %s", g_quark_to_string (priv->result->domain), @@ -1073,7 +1078,7 @@ mcd_dispatch_operation_constructor (GType type, guint n_params, g_return_val_if_fail (operation != NULL, NULL); priv = operation->priv; - if (!priv->client_registry || !priv->handler_map) + if (!priv->client_registry || !priv->handler_map || !priv->channel) goto error; if (priv->needs_approval && priv->observe_only) @@ -1087,14 +1092,12 @@ mcd_dispatch_operation_constructor (GType type, guint n_params, DEBUG ("%s/%p: needs_approval=%c", priv->unique_name, object, priv->needs_approval ? 'T' : 'F'); - if (DEBUGGING) + if (priv->channel != NULL) { - GList *list; - - for (list = priv->channels; list != NULL; list = list->next) - { - DEBUG ("Channel: %s", mcd_channel_get_object_path (list->data)); - } + DEBUG ("Channel: %s", + mcd_channel_get_object_path (priv->channel)); + g_assert (mcd_channel_get_account (priv->channel) == + priv->account); } /* If approval is not needed, we don't appear on D-Bus (and approvers @@ -1149,7 +1152,7 @@ mcd_dispatch_operation_channel_aborted_cb (McdChannel *channel, _mcd_dispatch_operation_lose_channel (self, channel); - if (_mcd_dispatch_operation_peek_channels (self) == NULL) + if (self->priv->channel == NULL) { DEBUG ("Nothing left in this context"); } @@ -1163,7 +1166,6 @@ mcd_dispatch_operation_set_property (GObject *obj, guint prop_id, { McdDispatchOperation *operation = MCD_DISPATCH_OPERATION (obj); McdDispatchOperationPrivate *priv = operation->priv; - GList *list; switch (prop_id) { @@ -1177,21 +1179,19 @@ mcd_dispatch_operation_set_property (GObject *obj, guint prop_id, priv->handler_map = MCD_HANDLER_MAP (g_value_dup_object (val)); break; - case PROP_CHANNELS: + case PROP_CHANNEL: /* because this is construct-only, we can assert that: */ - g_assert (priv->channels == NULL); + g_assert (priv->channel == NULL); g_assert (g_queue_is_empty (priv->approvals)); - priv->channels = g_list_copy (g_value_get_pointer (val)); + priv->channel = g_value_dup_object (val); - if (G_LIKELY (priv->channels)) + if (G_LIKELY (priv->channel)) { - /* get the connection and account from the first channel */ - McdChannel *channel = MCD_CHANNEL (priv->channels->data); const gchar *preferred_handler; priv->connection = (McdConnection *) - mcd_mission_get_parent (MCD_MISSION (channel)); + mcd_mission_get_parent (MCD_MISSION (priv->channel)); if (G_LIKELY (priv->connection)) { @@ -1203,10 +1203,10 @@ mcd_dispatch_operation_set_property (GObject *obj, guint prop_id, g_warning ("Channel has no Connection?!"); } - /* if the first channel is actually a channel request, get the + /* if the channel is actually a channel request, get the * preferred handler from it */ preferred_handler = - _mcd_channel_get_request_preferred_handler (channel); + _mcd_channel_get_request_preferred_handler (priv->channel); if (preferred_handler != NULL && g_str_has_prefix (preferred_handler, TP_CLIENT_BUS_NAME_BASE) && @@ -1220,7 +1220,7 @@ mcd_dispatch_operation_set_property (GObject *obj, guint prop_id, approval_new_requested (preferred_handler)); } - priv->account = mcd_channel_get_account (channel); + priv->account = mcd_channel_get_account (priv->channel); if (G_LIKELY (priv->account != NULL)) { @@ -1233,16 +1233,10 @@ mcd_dispatch_operation_set_property (GObject *obj, guint prop_id, "Account?!"); } - /* reference the channels and connect to their signals */ - for (list = priv->channels; list != NULL; list = list->next) - { - g_object_ref (list->data); - - g_signal_connect_after (list->data, "abort", - G_CALLBACK (mcd_dispatch_operation_channel_aborted_cb), - operation); - - } + /* connect to its signals */ + g_signal_connect_after (priv->channel, "abort", + G_CALLBACK (mcd_dispatch_operation_channel_aborted_cb), + operation); } break; @@ -1317,32 +1311,18 @@ static void mcd_dispatch_operation_dispose (GObject *object) { McdDispatchOperationPrivate *priv = MCD_DISPATCH_OPERATION_PRIV (object); - GList *list; tp_clear_object (&priv->plugin_api); tp_clear_object (&priv->successful_handler); - if (priv->channels != NULL) - { - for (list = priv->channels; list != NULL; list = list->next) - { - g_signal_handlers_disconnect_by_func (list->data, - mcd_dispatch_operation_channel_aborted_cb, object); - g_object_unref (list->data); - } - - tp_clear_pointer (&priv->channels, g_list_free); - } - - if (priv->lost_channels != NULL) + if (priv->channel != NULL) { - for (list = priv->lost_channels; list != NULL; list = list->next) - g_object_unref (list->data); - - tp_clear_pointer (&priv->lost_channels, g_list_free); + g_signal_handlers_disconnect_by_func (priv->channel, + mcd_dispatch_operation_channel_aborted_cb, object); } - tp_clear_object (&priv->connection); + tp_clear_object (&priv->channel); + tp_clear_object (&priv->lost_channel); tp_clear_object (&priv->account); tp_clear_object (&priv->handler_map); tp_clear_object (&priv->client_registry); @@ -1383,9 +1363,11 @@ _mcd_dispatch_operation_class_init (McdDispatchOperationClass * klass) G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); - g_object_class_install_property (object_class, PROP_CHANNELS, - g_param_spec_pointer ("channels", "channels", "channels", - G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY)); + g_object_class_install_property (object_class, PROP_CHANNEL, + g_param_spec_object ("channel", "Channel", "the channel to dispatch", + MCD_TYPE_CHANNEL, + G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); g_object_class_install_property (object_class, PROP_POSSIBLE_HANDLERS, g_param_spec_boxed ("possible-handlers", "Possible handlers", @@ -1429,31 +1411,31 @@ _mcd_dispatch_operation_init (McdDispatchOperation *operation) * _mcd_dispatch_operation_new: * @client_registry: the client registry. * @handler_map: the handler map - * @channels: a #GList of #McdChannel elements to dispatch. - * @possible_handlers: the bus names of possible handlers for these channels. + * @channel: the channel to dispatch + * @possible_handlers: the bus names of possible handlers for this channel * - * Creates a #McdDispatchOperation. The #GList @channels will be no longer - * valid after this function has been called. + * Creates a #McdDispatchOperation. */ McdDispatchOperation * _mcd_dispatch_operation_new (McdClientRegistry *client_registry, McdHandlerMap *handler_map, gboolean needs_approval, gboolean observe_only, - GList *channels, + McdChannel *channel, const gchar * const *possible_handlers) { gpointer *obj; - /* If we're only observing, then the channels were requested "behind MC's + /* If we're only observing, then the channel was requested "behind MC's * back", so they can't need approval (i.e. observe_only implies * !needs_approval) */ g_return_val_if_fail (!observe_only || !needs_approval, NULL); + g_return_val_if_fail (MCD_IS_CHANNEL (channel), NULL); obj = g_object_new (MCD_TYPE_DISPATCH_OPERATION, "client-registry", client_registry, "handler-map", handler_map, - "channels", channels, + "channel", channel, "possible-handlers", possible_handlers, "needs-approval", needs_approval, "observe-only", observe_only, @@ -1620,16 +1602,18 @@ static void _mcd_dispatch_operation_lose_channel (McdDispatchOperation *self, McdChannel *channel) { - GList *li = g_list_find (self->priv->channels, channel); const gchar *object_path; const GError *error = NULL; - if (li == NULL) + if (G_UNLIKELY (channel != self->priv->channel)) { + g_warning ("%s: apparently lost %p but my channel is %p", + G_STRFUNC, channel, self->priv->channel); return; } - self->priv->channels = g_list_delete_link (self->priv->channels, li); + /* steal the reference */ + self->priv->channel = NULL; object_path = mcd_channel_get_object_path (channel); error = mcd_channel_get_error (channel); @@ -1650,9 +1634,8 @@ _mcd_dispatch_operation_lose_channel (McdDispatchOperation *self, "%" G_GSIZE_FORMAT " approvers", self->priv->unique_name, self, object_path, self->priv->observers_pending, self->priv->ado_pending); - self->priv->lost_channels = - g_list_prepend (self->priv->lost_channels, - g_object_ref (channel)); + g_assert (self->priv->lost_channel == NULL); + self->priv->lost_channel = g_object_ref (channel); } else { @@ -1667,15 +1650,12 @@ _mcd_dispatch_operation_lose_channel (McdDispatchOperation *self, g_free (error_name); } - /* We previously had a ref in the linked list - drop it */ + /* We previously stole this ref from self->priv->channel - drop it */ g_object_unref (channel); - if (self->priv->channels == NULL) - { - /* no channels left, so the CDO finishes (if it hasn't already) */ - _mcd_dispatch_operation_finish (self, error->domain, error->code, - "%s", error->message); - } + /* no channels left, so the CDO finishes (if it hasn't already) */ + _mcd_dispatch_operation_finish (self, error->domain, error->code, + "%s", error->message); } static void @@ -1683,28 +1663,26 @@ _mcd_dispatch_operation_check_finished (McdDispatchOperation *self) { if (mcd_dispatch_operation_may_signal_finished (self)) { - GList *lost_channels; + McdChannel *lost_channel = self->priv->lost_channel; - /* get the lost channels into chronological order, and steal them from - * the object*/ - lost_channels = g_list_reverse (self->priv->lost_channels); - self->priv->lost_channels = NULL; + /* steal it */ + self->priv->lost_channel = NULL; - while (lost_channels != NULL) + if (lost_channel != NULL) { - McdChannel *channel = lost_channels->data; - const gchar *object_path = mcd_channel_get_object_path (channel); + const gchar *object_path = mcd_channel_get_object_path ( + lost_channel); if (object_path == NULL) { /* This shouldn't happen, but McdChannel is twisty enough * that I can't be sure */ g_critical ("McdChannel has already lost its TpChannel: %p", - channel); + lost_channel); } else { - const GError *error = mcd_channel_get_error (channel); + const GError *error = mcd_channel_get_error (lost_channel); gchar *error_name = _mcd_build_error_string (error); DEBUG ("%s/%p losing channel %s: %s: %s", @@ -1715,8 +1693,7 @@ _mcd_dispatch_operation_check_finished (McdDispatchOperation *self) g_free (error_name); } - g_object_unref (channel); - lost_channels = g_list_delete_link (lost_channels, lost_channels); + g_object_unref (lost_channel); } if (self->priv->result != NULL) @@ -1884,25 +1861,28 @@ _mcd_dispatch_operation_has_channel (McdDispatchOperation *self, McdChannel *channel) { g_return_val_if_fail (MCD_IS_DISPATCH_OPERATION (self), FALSE); - return (g_list_find (self->priv->channels, channel) != NULL); + + return (self->priv->channel != NULL && + self->priv->channel == channel); } -const GList * -_mcd_dispatch_operation_peek_channels (McdDispatchOperation *self) +McdChannel * +_mcd_dispatch_operation_peek_channel (McdDispatchOperation *self) { g_return_val_if_fail (MCD_IS_DISPATCH_OPERATION (self), NULL); - return self->priv->channels; + + return self->priv->channel; } -GList * -_mcd_dispatch_operation_dup_channels (McdDispatchOperation *self) +McdChannel * +_mcd_dispatch_operation_dup_channel (McdDispatchOperation *self) { - GList *copy; - g_return_val_if_fail (MCD_IS_DISPATCH_OPERATION (self), NULL); - copy = g_list_copy (self->priv->channels); - g_list_foreach (copy, (GFunc) g_object_ref, NULL); - return copy; + + if (self->priv->channel != NULL) + return g_object_ref (self->priv->channel); + + return NULL; } static void @@ -1922,11 +1902,10 @@ _mcd_dispatch_operation_handle_channels_cb (TpClient *client, } else { - const GList *list; - - for (list = self->priv->channels; list != NULL; list = list->next) + /* FIXME: can channel ever be NULL here? */ + if (self->priv->channel != NULL) { - McdChannel *channel = list->data; + McdChannel *channel = MCD_CHANNEL (self->priv->channel); const gchar *unique_name; unique_name = _mcd_client_proxy_get_unique_name (MCD_CLIENT_PROXY (client)); @@ -1955,11 +1934,12 @@ _mcd_dispatch_operation_handle_channels_cb (TpClient *client, g_warning ("Closing channel %s as a result", mcd_channel_get_object_path (channel)); _mcd_channel_undispatchable (channel); - continue; } - - mcd_dispatch_operation_set_channel_handled_by (self, channel, - unique_name, tp_proxy_get_bus_name (client)); + else + { + mcd_dispatch_operation_set_channel_handled_by (self, channel, + unique_name, tp_proxy_get_bus_name (client)); + } } /* emit Finished, if we haven't already; but first make a note of the @@ -1998,36 +1978,25 @@ observe_channels_cb (TpClient *proxy, const GError *error, * request-properties for Observer_Info or Handler_Info */ static void -collect_satisfied_requests (const GList *channels, +collect_satisfied_requests (McdChannel *channel, GPtrArray **paths_out, GHashTable **props_out) { - const GList *c; - GHashTable *set; GHashTableIter it; gpointer path, value; GPtrArray *satisfied_requests; GHashTable *request_properties; + GHashTable *reqs; - set = g_hash_table_new_full (g_str_hash, g_str_equal, - g_free, g_object_unref); - - for (c = channels; c != NULL; c = c->next) - { - GHashTable *reqs = _mcd_channel_get_satisfied_requests (c->data, - NULL); - tp_g_hash_table_update (set, reqs, - (GBoxedCopyFunc) g_strdup, (GBoxedCopyFunc) g_object_ref); - g_hash_table_unref (reqs); - } + reqs = _mcd_channel_get_satisfied_requests (channel, NULL); - satisfied_requests = g_ptr_array_sized_new (g_hash_table_size (set)); + satisfied_requests = g_ptr_array_sized_new (g_hash_table_size (reqs)); g_ptr_array_set_free_func (satisfied_requests, g_free); request_properties = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) g_hash_table_unref); - g_hash_table_iter_init (&it, set); + g_hash_table_iter_init (&it, reqs); while (g_hash_table_iter_next (&it, &path, &value)) { @@ -2039,7 +2008,7 @@ collect_satisfied_requests (const GList *channels, g_hash_table_insert (request_properties, g_strdup (path), props); } - g_hash_table_unref (set); + g_hash_table_unref (reqs); if (paths_out != NULL) *paths_out = satisfied_requests; @@ -2055,7 +2024,6 @@ collect_satisfied_requests (const GList *channels, static void _mcd_dispatch_operation_run_observers (McdDispatchOperation *self) { - const GList *cl; const gchar *dispatch_operation_path = "/"; GHashTable *observer_info; GHashTableIter iter; @@ -2068,7 +2036,7 @@ _mcd_dispatch_operation_run_observers (McdDispatchOperation *self) while (g_hash_table_iter_next (&iter, NULL, &client_p)) { McdClientProxy *client = MCD_CLIENT_PROXY (client_p); - GList *observed = NULL; + gboolean observed = FALSE; const gchar *account_path, *connection_path; GPtrArray *channels_array, *satisfied_requests; GHashTable *request_properties; @@ -2077,9 +2045,9 @@ _mcd_dispatch_operation_run_observers (McdDispatchOperation *self) TP_IFACE_QUARK_CLIENT_OBSERVER)) continue; - for (cl = self->priv->channels; cl != NULL; cl = cl->next) + if (self->priv->channel != NULL) { - McdChannel *channel = MCD_CHANNEL (cl->data); + McdChannel *channel = MCD_CHANNEL (self->priv->channel); GHashTable *properties; properties = _mcd_channel_get_immutable_properties (channel); @@ -2088,8 +2056,10 @@ _mcd_dispatch_operation_run_observers (McdDispatchOperation *self) if (_mcd_client_match_filters (properties, _mcd_client_proxy_get_observer_filters (client), FALSE)) - observed = g_list_prepend (observed, channel); + observed = TRUE; } + + /* in particular this happens if there is no channel at all */ if (!observed) continue; /* build up the parameters and invoke the observer */ @@ -2099,9 +2069,10 @@ _mcd_dispatch_operation_run_observers (McdDispatchOperation *self) /* TODO: there's room for optimization here: reuse the channels_array, * if the observed list is the same */ - channels_array = _mcd_tp_channel_details_build_from_list (observed); + channels_array = _mcd_tp_channel_details_build_from_tp_chan ( + mcd_channel_get_tp_channel (self->priv->channel)); - collect_satisfied_requests (observed, &satisfied_requests, + collect_satisfied_requests (self->priv->channel, &satisfied_requests, &request_properties); /* transfer ownership into observer_info */ @@ -2129,8 +2100,6 @@ _mcd_dispatch_operation_run_observers (McdDispatchOperation *self) g_ptr_array_unref (satisfied_requests); _mcd_tp_channel_details_free (channels_array); - - g_list_free (observed); } g_hash_table_unref (observer_info); @@ -2173,7 +2142,6 @@ add_dispatch_operation_cb (TpClient *proxy, static void _mcd_dispatch_operation_run_approvers (McdDispatchOperation *self) { - const GList *cl; GHashTableIter iter; gpointer client_p; @@ -2195,9 +2163,9 @@ _mcd_dispatch_operation_run_approvers (McdDispatchOperation *self) TP_IFACE_QUARK_CLIENT_APPROVER)) continue; - for (cl = self->priv->channels; cl != NULL; cl = cl->next) + if (self->priv->channel != NULL) { - McdChannel *channel = MCD_CHANNEL (cl->data); + McdChannel *channel = MCD_CHANNEL (self->priv->channel); GHashTable *channel_properties; channel_properties = _mcd_channel_get_immutable_properties (channel); @@ -2208,15 +2176,17 @@ _mcd_dispatch_operation_run_approvers (McdDispatchOperation *self) FALSE)) { matched = TRUE; - break; } } + + /* in particular, after this point, self->priv->channel can't + * be NULL */ if (!matched) continue; dispatch_operation = _mcd_dispatch_operation_get_path (self); properties = _mcd_dispatch_operation_get_properties (self); - channel_details = - _mcd_tp_channel_details_build_from_list (self->priv->channels); + channel_details = _mcd_tp_channel_details_build_from_tp_chan ( + mcd_channel_get_tp_channel (self->priv->channel)); DEBUG ("Calling AddDispatchOperation on approver %s for CDO %s @ %p", tp_proxy_get_bus_name (client), dispatch_operation, self); @@ -2262,7 +2232,7 @@ _mcd_dispatch_operation_run_clients (McdDispatchOperation *self) g_object_ref (self); DEBUG ("%s %p", self->priv->unique_name, self); - if (self->priv->channels != NULL) + if (self->priv->channel != NULL) { const GList *mini_plugins; @@ -2308,6 +2278,7 @@ _mcd_dispatch_operation_run_clients (McdDispatchOperation *self) static void mcd_dispatch_operation_handle_channels (McdDispatchOperation *self) { + GList *channels = NULL; GHashTable *handler_info; GHashTable *request_properties; @@ -2330,19 +2301,31 @@ mcd_dispatch_operation_handle_channels (McdDispatchOperation *self) return; } + /* FIXME: it shouldn't be possible to get here without a channel */ + if (self->priv->channel != NULL) + { + collect_satisfied_requests (self->priv->channel, NULL, + &request_properties); + channels = g_list_prepend (NULL, self->priv->channel); + } + else + { + request_properties = g_hash_table_new_full (g_str_hash, + g_str_equal, g_free, (GDestroyNotify) g_hash_table_unref); + } + handler_info = tp_asv_new (NULL, NULL); - collect_satisfied_requests (self->priv->channels, NULL, - &request_properties); tp_asv_take_boxed (handler_info, "request-properties", TP_HASH_TYPE_OBJECT_IMMUTABLE_PROPERTIES_MAP, request_properties); request_properties = NULL; _mcd_client_proxy_handle_channels (self->priv->trying_handler, - -1, self->priv->channels, self->priv->handle_with_time, + -1, channels, self->priv->handle_with_time, handler_info, _mcd_dispatch_operation_handle_channels_cb, g_object_ref (self), g_object_unref, NULL); g_hash_table_unref (handler_info); + g_list_free (channels); } static void @@ -2385,9 +2368,7 @@ mcd_dispatch_operation_try_handler (McdDispatchOperation *self, self->priv->handler_suitable_pending = 0; - DEBUG ("%s: channel ACL verification [%u channels]", - self->priv->unique_name, - g_list_length (self->priv->channels)); + DEBUG ("%s: channel ACL verification", self->priv->unique_name); for (p = mcp_list_objects (); p != NULL; p = g_list_next (p)) { @@ -2495,8 +2476,6 @@ static void _mcd_dispatch_operation_close_as_undispatchable (McdDispatchOperation *self, const GError *error) { - GList *channels, *list; - /* All of the usable handlers vanished while we were thinking about it * (this can only happen if non-activatable handlers exit after we * include them in the list of possible handlers, but before we . @@ -2507,20 +2486,17 @@ _mcd_dispatch_operation_close_as_undispatchable (McdDispatchOperation *self, _mcd_dispatch_operation_finish (self, error->domain, error->code, "%s", error->message); - channels = _mcd_dispatch_operation_dup_channels (self); - - for (list = channels; list != NULL; list = list->next) + if (self->priv->channel != NULL) { - McdChannel *channel = MCD_CHANNEL (list->data); + McdChannel *channel = MCD_CHANNEL (self->priv->channel); GError e = { TP_ERROR, TP_ERROR_NOT_AVAILABLE, "Handler no longer available" }; + g_object_ref (channel); mcd_channel_take_error (channel, g_error_copy (&e)); _mcd_channel_undispatchable (channel); g_object_unref (channel); } - - g_list_free (channels); } void @@ -2549,20 +2525,18 @@ _mcd_dispatch_operation_end_plugin_delay (McdDispatchOperation *self) void _mcd_dispatch_operation_forget_channels (McdDispatchOperation *self) { - /* make a temporary copy, which is destroyed during the loop - otherwise - * we'll be trying to iterate over the list at the same time - * that mcd_mission_abort results in modifying it, which would be bad */ - GList *list = _mcd_dispatch_operation_dup_channels (self); - - while (list != NULL) + if (self->priv->channel != NULL) { - mcd_mission_abort (list->data); - g_object_unref (list->data); - list = g_list_delete_link (list, list); + /* Take a temporary copy, because self->priv->channels is going + * to be modified as a result of mcd_mission_abort() */ + McdChannel *channel = g_object_ref (self->priv->channel); + + mcd_mission_abort (MCD_MISSION (channel)); + g_object_unref (channel); } - /* There should now be none left (they all aborted) */ - g_return_if_fail (self->priv->channels == NULL); + /* There should now be no channel left (it was aborted) */ + g_return_if_fail (self->priv->channel == NULL); } void @@ -2570,20 +2544,19 @@ _mcd_dispatch_operation_leave_channels (McdDispatchOperation *self, TpChannelGroupChangeReason reason, const gchar *message) { - GList *list; - if (message == NULL) { message = ""; } - list = _mcd_dispatch_operation_dup_channels (self); - - while (list != NULL) + if (self->priv->channel != NULL) { - _mcd_channel_depart (list->data, reason, message); - g_object_unref (list->data); - list = g_list_delete_link (list, list); + /* Take a temporary copy, because self->priv->channels could + * be modified as a result */ + McdChannel *channel = g_object_ref (self->priv->channel); + + _mcd_channel_depart (channel, reason, message); + g_object_unref (channel); } _mcd_dispatch_operation_forget_channels (self); @@ -2592,13 +2565,14 @@ _mcd_dispatch_operation_leave_channels (McdDispatchOperation *self, void _mcd_dispatch_operation_close_channels (McdDispatchOperation *self) { - GList *list = _mcd_dispatch_operation_dup_channels (self); - - while (list != NULL) + if (self->priv->channel != NULL) { - _mcd_channel_close (list->data); - g_object_unref (list->data); - list = g_list_delete_link (list, list); + /* Take a temporary copy, because self->priv->channels could + * be modified as a result */ + McdChannel *channel = g_object_ref (self->priv->channel); + + _mcd_channel_close (channel); + g_object_unref (channel); } _mcd_dispatch_operation_forget_channels (self); @@ -2607,13 +2581,14 @@ _mcd_dispatch_operation_close_channels (McdDispatchOperation *self) void _mcd_dispatch_operation_destroy_channels (McdDispatchOperation *self) { - GList *list = _mcd_dispatch_operation_dup_channels (self); - - while (list != NULL) + if (self->priv->channel != NULL) { - _mcd_channel_undispatchable (list->data); - g_object_unref (list->data); - list = g_list_delete_link (list, list); + /* Take a temporary copy, because self->priv->channels could + * be modified as a result */ + McdChannel *channel = g_object_ref (self->priv->channel); + + _mcd_channel_undispatchable (channel); + g_object_unref (channel); } _mcd_dispatch_operation_forget_channels (self); diff --git a/src/mcd-dispatcher-priv.h b/src/mcd-dispatcher-priv.h index 82c7ad42..639d751a 100644 --- a/src/mcd-dispatcher-priv.h +++ b/src/mcd-dispatcher-priv.h @@ -34,8 +34,10 @@ G_BEGIN_DECLS /* not exported */ -G_GNUC_INTERNAL void _mcd_dispatcher_take_channels ( - McdDispatcher *dispatcher, GList *channels, gboolean requested, +G_GNUC_INTERNAL void _mcd_dispatcher_add_channel ( + McdDispatcher *dispatcher, + McdChannel *channel, + gboolean requested, gboolean only_observe); G_GNUC_INTERNAL void _mcd_dispatcher_add_channel_request (McdDispatcher *dispatcher, diff --git a/src/mcd-dispatcher.c b/src/mcd-dispatcher.c index 19d11d6b..c7f3c7a3 100644 --- a/src/mcd-dispatcher.c +++ b/src/mcd-dispatcher.c @@ -178,14 +178,14 @@ mcd_dispatcher_dup_internal_handlers (void) static GStrv mcd_dispatcher_dup_possible_handlers (McdDispatcher *self, McdRequest *request, - const GList *channels, + TpChannel *channel, const gchar *must_have_unique_name) { GList *handlers = _mcd_client_registry_list_possible_handlers ( self->priv->clients, request != NULL ? _mcd_request_get_preferred_handler (request) : NULL, request != NULL ? _mcd_request_get_properties (request) : NULL, - channels, must_have_unique_name); + channel, must_have_unique_name); guint n_handlers = g_list_length (handlers); guint i; GStrv ret; @@ -238,7 +238,7 @@ on_operation_finished (McdDispatchOperation *operation, static void _mcd_dispatcher_enter_state_machine (McdDispatcher *dispatcher, - GList *channels, + McdChannel *channel, const gchar * const *possible_handlers, gboolean requested, gboolean only_observe) @@ -248,11 +248,10 @@ _mcd_dispatcher_enter_state_machine (McdDispatcher *dispatcher, McdAccount *account; g_return_if_fail (MCD_IS_DISPATCHER (dispatcher)); - g_return_if_fail (channels != NULL); - g_return_if_fail (MCD_IS_CHANNEL (channels->data)); + g_return_if_fail (MCD_IS_CHANNEL (channel)); g_return_if_fail (requested || !only_observe); - account = mcd_channel_get_account (channels->data); + account = mcd_channel_get_account (channel); if (G_UNLIKELY (!account)) { g_warning ("%s called with no account", G_STRFUNC); @@ -261,19 +260,13 @@ _mcd_dispatcher_enter_state_machine (McdDispatcher *dispatcher, priv = dispatcher->priv; - DEBUG ("new dispatch operation for %s channel %p (%s): %s", + DEBUG ("new dispatch operation for %s channel %p: %s", requested ? "requested" : "unrequested", - channels->data, - channels->next == NULL ? "only" : "and more", - mcd_channel_get_object_path (channels->data)); - - /* FIXME: what should we do when the channels are a mixture of Requested - * and unRequested? At the moment we act as though they're all Requested; - * perhaps we should act as though they're all unRequested, or split up the - * bundle? */ + channel, + mcd_channel_get_object_path (channel)); operation = _mcd_dispatch_operation_new (priv->clients, - priv->handler_map, !requested, only_observe, channels, + priv->handler_map, !requested, only_observe, channel, (const gchar * const *) possible_handlers); if (!requested) @@ -297,28 +290,21 @@ _mcd_dispatcher_enter_state_machine (McdDispatcher *dispatcher, { GError error = { TP_ERROR, TP_ERROR_CANCELLED, "Channel request cancelled" }; - GList *list; + McdChannel *cancelled; - /* make a temporary copy, which is destroyed during the loop - - * otherwise we'll be trying to iterate over the list at the same time - * that mcd_mission_abort results in modifying it, which would be - * bad */ - list = _mcd_dispatch_operation_dup_channels (operation); + cancelled = _mcd_dispatch_operation_dup_channel (operation); - while (list != NULL) + if (cancelled != NULL) { - McdChannel *channel = MCD_CHANNEL (list->data); - - if (mcd_channel_get_error (channel) == NULL) - mcd_channel_take_error (channel, g_error_copy (&error)); + if (mcd_channel_get_error (cancelled) == NULL) + mcd_channel_take_error (cancelled, g_error_copy (&error)); - _mcd_channel_undispatchable (channel); + _mcd_channel_undispatchable (cancelled); - g_object_unref (channel); - list = g_list_delete_link (list, list); + g_object_unref (cancelled); } } - else if (_mcd_dispatch_operation_peek_channels (operation) == NULL) + else if (_mcd_dispatch_operation_peek_channel (operation) == NULL) { DEBUG ("No channels left"); } @@ -546,7 +532,6 @@ _mcd_dispatcher_lookup_handler (McdDispatcher *self, if (handler == NULL) { GList *possible_handlers; - GList *channels; /* Failing that, maybe the Handler it was dispatched to was temporary; * try to pick another Handler that can deal with it, on the same @@ -554,12 +539,11 @@ _mcd_dispatcher_lookup_handler (McdDispatcher *self, * It can also happen in the case an Observer/Approver Claimed the * channel; in that case we did not get its handler well known name. */ - channels = g_list_prepend (NULL, channel); possible_handlers = _mcd_client_registry_list_possible_handlers ( self->priv->clients, request != NULL ? _mcd_request_get_preferred_handler (request) : NULL, request != NULL ? _mcd_request_get_properties (request) : NULL, - channels, unique_name); + channel, unique_name); if (possible_handlers != NULL) { @@ -576,7 +560,6 @@ _mcd_dispatcher_lookup_handler (McdDispatcher *self, unique_name, object_path); } - g_list_free (channels); g_list_free (possible_handlers); } @@ -632,11 +615,11 @@ mcd_dispatcher_client_needs_recovery_cb (McdClientProxy *client, if (_mcd_dispatch_operation_has_invoked_observers (op)) { - for (channels = _mcd_dispatch_operation_peek_channels (op); - channels != NULL; - channels = channels->next) + McdChannel *mcd_channel = + _mcd_dispatch_operation_peek_channel (op); + + if (mcd_channel != NULL) { - McdChannel *mcd_channel = channels->data; GHashTable *properties = _mcd_channel_get_immutable_properties (mcd_channel); @@ -924,36 +907,31 @@ mcd_dispatcher_new (TpDBusDaemon *dbus_daemon, McdMaster *master) } /* - * _mcd_dispatcher_take_channels: + * _mcd_dispatcher_add_channel: * @dispatcher: the #McdDispatcher. - * @channels: a #GList of #McdChannel elements, each of which must own a - * #TpChannel + * @channel: (transfer none): a #McdChannel which must own a #TpChannel * @requested: whether the channels were requested by MC. * - * Dispatch @channels. The #GList @channels will be no longer valid after this - * function has been called. + * Add @channel to the dispatching state machine. */ void -_mcd_dispatcher_take_channels (McdDispatcher *dispatcher, GList *channels, - gboolean requested, gboolean only_observe) +_mcd_dispatcher_add_channel (McdDispatcher *dispatcher, + McdChannel *channel, + gboolean requested, + gboolean only_observe) { - GList *list; - GList *tp_channels = NULL; + TpChannel *tp_channel = NULL; GStrv possible_handlers; McdRequest *request = NULL; gboolean internal_request = FALSE; - if (channels == NULL) - { - DEBUG ("trivial case - no channels"); - return; - } + g_return_if_fail (MCD_IS_DISPATCHER (dispatcher)); + g_return_if_fail (MCD_IS_CHANNEL (channel)); - DEBUG ("%s channel %p (%s): %s", + DEBUG ("%s channel %p: %s", requested ? "requested" : "unrequested", - channels->data, - channels->next == NULL ? "only" : "and more", - mcd_channel_get_object_path (channels->data)); + channel, + mcd_channel_get_object_path (channel)); if (only_observe) { @@ -961,29 +939,17 @@ _mcd_dispatcher_take_channels (McdDispatcher *dispatcher, GList *channels, /* these channels were requested "behind our back", so only call * ObserveChannels on them */ - _mcd_dispatcher_enter_state_machine (dispatcher, channels, NULL, + _mcd_dispatcher_enter_state_machine (dispatcher, channel, NULL, TRUE, TRUE); - g_list_free (channels); return; } - /* These channels must have the TpChannel part of McdChannel's double life. - * They might also have the McdRequest part. */ - for (list = channels; list != NULL; list = list->next) - { - TpChannel *tp_channel = mcd_channel_get_tp_channel (list->data); - - g_assert (tp_channel != NULL); - tp_channels = g_list_prepend (tp_channels, g_object_ref (tp_channel)); - - /* We take the channel request from the first McdChannel that (has|is) - * one.*/ - if (request == NULL) - { - request = _mcd_channel_get_request (list->data); - } - } + /* The channel must have the TpChannel part of McdChannel's double life. + * It might also have the McdRequest part. */ + tp_channel = mcd_channel_get_tp_channel (channel); + g_assert (tp_channel != NULL); + request = _mcd_channel_get_request (channel); internal_request = _mcd_request_is_internal (request); /* See if there are any handlers that can take all these channels */ @@ -992,49 +958,24 @@ _mcd_dispatcher_take_channels (McdDispatcher *dispatcher, GList *channels, else possible_handlers = mcd_dispatcher_dup_possible_handlers (dispatcher, request, - tp_channels, + tp_channel, NULL); - g_list_foreach (tp_channels, (GFunc) g_object_unref, NULL); - g_list_free (tp_channels); - if (possible_handlers == NULL) { - if (channels->next == NULL) - { - DEBUG ("One channel, which cannot be handled - making a CDO " - "anyway, to get Observers run"); - } - else - { - DEBUG ("Two or more channels, which cannot all be handled - " - "will split up the batch and try again"); - - while (channels != NULL) - { - list = channels; - channels = g_list_remove_link (channels, list); - _mcd_dispatcher_take_channels (dispatcher, list, requested, - FALSE); - } - - return; - } + DEBUG ("Channel cannot be handled - making a CDO " + "anyway, to get Observers run"); } else { - DEBUG ("%s handler(s) found, dispatching %u channels", - internal_request ? "internal" : "possible", - g_list_length (channels)); + DEBUG ("%s handler(s) found, dispatching channel", + internal_request ? "internal" : "possible"); } - for (list = channels; list != NULL; list = list->next) - _mcd_channel_set_status (MCD_CHANNEL (list->data), - MCD_CHANNEL_STATUS_DISPATCHING); + _mcd_channel_set_status (channel, MCD_CHANNEL_STATUS_DISPATCHING); - _mcd_dispatcher_enter_state_machine (dispatcher, channels, + _mcd_dispatcher_enter_state_machine (dispatcher, channel, (const gchar * const *) possible_handlers, requested, FALSE); - g_list_free (channels); g_strfreev (possible_handlers); } @@ -1245,10 +1186,7 @@ _mcd_dispatcher_recover_channel (McdDispatcher *dispatcher, DEBUG ("%s is unhandled, redispatching", path); requested = mcd_channel_is_requested (channel); - _mcd_dispatcher_take_channels (dispatcher, - g_list_prepend (NULL, channel), - requested, - FALSE); + _mcd_dispatcher_add_channel (dispatcher, channel, requested, FALSE); } } @@ -2126,16 +2064,11 @@ add_possible_handlers (McdDispatcher *self, const gchar *sender, const gchar *preferred_handler) { - GList *channels = NULL; GStrv possible_handlers; guint i; - channels = g_list_prepend (channels, tp_channel); - possible_handlers = mcd_dispatcher_dup_possible_handlers (self, - NULL, channels, NULL); - - g_list_free (channels); + NULL, tp_channel, NULL); for (i = 0; possible_handlers[i] != NULL; i++) { diff --git a/src/plugin-dispatch-operation.c b/src/plugin-dispatch-operation.c index 934e6dde..0281dbfc 100644 --- a/src/plugin-dispatch-operation.c +++ b/src/plugin-dispatch-operation.c @@ -178,9 +178,11 @@ plugin_do_get_n_channels (McpDispatchOperation *obj) McdPluginDispatchOperation *self = MCD_PLUGIN_DISPATCH_OPERATION (obj); g_return_val_if_fail (self != NULL, 0); - /* FIXME: O(n) */ - return g_list_length ((GList *) _mcd_dispatch_operation_peek_channels ( - self->real_cdo)); + + if (_mcd_dispatch_operation_peek_channel (self->real_cdo) != NULL) + return 1; + + return 0; } static const gchar * @@ -191,11 +193,10 @@ plugin_do_get_nth_channel_path (McpDispatchOperation *obj, McdChannel *channel; g_return_val_if_fail (self != NULL, NULL); - /* FIXME: O(n) */ - channel = g_list_nth_data ((GList *) _mcd_dispatch_operation_peek_channels ( - self->real_cdo), n); - if (channel == NULL) + channel = _mcd_dispatch_operation_peek_channel (self->real_cdo); + + if (channel == NULL || n != 0) return NULL; return mcd_channel_get_object_path (channel); @@ -210,11 +211,10 @@ plugin_do_ref_nth_channel_properties (McpDispatchOperation *obj, GHashTable *ret; g_return_val_if_fail (self != NULL, NULL); - /* FIXME: O(n) */ - channel = g_list_nth_data ((GList *) _mcd_dispatch_operation_peek_channels ( - self->real_cdo), n); - if (channel == NULL) + channel = _mcd_dispatch_operation_peek_channel (self->real_cdo); + + if (channel == NULL || n != 0) return NULL; ret = _mcd_channel_get_immutable_properties (channel); diff --git a/tests/twisted/dispatcher/exploding-bundles.py b/tests/twisted/dispatcher/exploding-bundles.py index a62a7009..27d3be3a 100644 --- a/tests/twisted/dispatcher/exploding-bundles.py +++ b/tests/twisted/dispatcher/exploding-bundles.py @@ -107,34 +107,30 @@ def test(q, bus, mc): conn.NewChannels([text_chan, media_chan]) - # A channel dispatch operation is created + # A channel dispatch operation is created for the Text channel first. e = q.expect('dbus-signal', path=cs.CD_PATH, interface=cs.CD_IFACE_OP_LIST, signal='NewDispatchOperation') - cdo_path = e.args[0] - cdo_properties = e.args[1] + text_cdo_path = e.args[0] + text_cdo_properties = e.args[1] - assert cdo_properties[cs.CDO + '.Account'] == account.object_path - assert cdo_properties[cs.CDO + '.Connection'] == conn.object_path + assert text_cdo_properties[cs.CDO + '.Account'] == account.object_path + assert text_cdo_properties[cs.CDO + '.Connection'] == conn.object_path - handlers = cdo_properties[cs.CDO + '.PossibleHandlers'][:] - # only Empathy can handle the whole batch - assert handlers == [cs.tp_name_prefix + '.Client.org.gnome.Empathy'], \ - handlers + handlers = text_cdo_properties[cs.CDO + '.PossibleHandlers'][:] + assert (sorted(handlers) == + [cs.tp_name_prefix + '.Client.org.gnome.Empathy', + cs.tp_name_prefix + '.Client.org.kde.Kopete']), handlers - assert cs.CD_IFACE_OP_LIST in cd_props.Get(cs.CD, 'Interfaces') - assert cd_props.Get(cs.CD_IFACE_OP_LIST, 'DispatchOperations') ==\ - [(cdo_path, cdo_properties)] - - cdo = bus.get_object(cs.CD, cdo_path) - cdo_iface = dbus.Interface(cdo, cs.CDO) + text_cdo = bus.get_object(cs.CD, text_cdo_path) + text_cdo_iface = dbus.Interface(text_cdo, cs.CDO) - # Both Observers are told about the new channels + # Both Observers are told about the new Text channel - e, k = q.expect_many( + e_observe_text, k_observe_text = q.expect_many( EventPattern('dbus-method-call', path=empathy.object_path, interface=cs.OBSERVER, method='ObserveChannels', @@ -144,62 +140,130 @@ def test(q, bus, mc): interface=cs.OBSERVER, method='ObserveChannels', handled=False), ) - assert e.args[0] == account.object_path, e.args - assert e.args[1] == conn.object_path, e.args - assert e.args[3] == cdo_path, e.args - assert e.args[4] == [], e.args # no requests satisfied - channels = e.args[2] - assert len(channels) == 2, channels + assert e_observe_text.args[0] == account.object_path, e_observe_text.args + assert e_observe_text.args[1] == conn.object_path, e_observe_text.args + assert e_observe_text.args[3] == text_cdo_path, e_observe_text.args + assert e_observe_text.args[4] == [], e_observe_text.args + channels = e_observe_text.args[2] + assert len(channels) == 1, channels assert (text_chan.object_path, text_channel_properties) in channels + + assert k_observe_text.args[0] == e_observe_text.args[0], k_observe_text.args + assert k_observe_text.args[1] == e_observe_text.args[1], k_observe_text.args + assert (k_observe_text.args[2] == + [(text_chan.object_path, text_channel_properties)]) + + # Now a separate CDO is created for the media channel. + + e = q.expect('dbus-signal', + path=cs.CD_PATH, + interface=cs.CD_IFACE_OP_LIST, + signal='NewDispatchOperation') + + media_cdo_path = e.args[0] + media_cdo_properties = e.args[1] + + assert media_cdo_properties[cs.CDO + '.Account'] == account.object_path + assert media_cdo_properties[cs.CDO + '.Connection'] == conn.object_path + + handlers = media_cdo_properties[cs.CDO + '.PossibleHandlers'][:] + # only Empathy can handle it + assert (sorted(handlers) == + [cs.tp_name_prefix + '.Client.org.gnome.Empathy']), handlers + + assert cs.CD_IFACE_OP_LIST in cd_props.Get(cs.CD, 'Interfaces') + assert (sorted(cd_props.Get(cs.CD_IFACE_OP_LIST, 'DispatchOperations')) == + [(text_cdo_path, text_cdo_properties), + (media_cdo_path, media_cdo_properties)]) + + media_cdo = bus.get_object(cs.CD, media_cdo_path) + media_cdo_iface = dbus.Interface(media_cdo, cs.CDO) + + # Only Empathy is told about the new media channel + + e_observe_media = q.expect('dbus-method-call', + path=empathy.object_path, + interface=cs.OBSERVER, method='ObserveChannels', + handled=False) + assert e_observe_media.args[0] == account.object_path, e_observe_media.args + assert e_observe_media.args[1] == conn.object_path, e_observe_media.args + assert e_observe_media.args[3] == media_cdo_path, e_observe_media.args + assert e_observe_media.args[4] == [], e_observe_media.args + channels = e_observe_media.args[2] + assert len(channels) == 1, channels assert (media_chan.object_path, media_channel_properties) in channels - # fd.o #21089: telepathy-spec doesn't say whether Kopete observes the whole - # batch or just the text channel. In current MC, it only observes the text. - assert k.args[0] == e.args[0], k.args - assert k.args[1] == e.args[1], e.args - assert k.args[2] == [(text_chan.object_path, text_channel_properties)] + # All Observers reply. - # Both Observers indicate that they are ready to proceed - q.dbus_return(k.message, signature='') - q.dbus_return(e.message, signature='') + q.dbus_return(e_observe_text.message, signature='') + q.dbus_return(k_observe_text.message, signature='') + q.dbus_return(e_observe_media.message, signature='') # The Approvers are next - # fd.o #21090: telepathy-spec doesn't say whether Kopete is asked to - # approve this CDO. In current MC, it is. - e, k = q.expect_many( + e_approve_text, k_approve_text, e_approve_media = q.expect_many( EventPattern('dbus-method-call', path=empathy.object_path, interface=cs.APPROVER, method='AddDispatchOperation', + predicate=lambda e: e.args[1] == text_cdo_path, handled=False), EventPattern('dbus-method-call', path=kopete.object_path, interface=cs.APPROVER, method='AddDispatchOperation', handled=False), + EventPattern('dbus-method-call', + path=empathy.object_path, + interface=cs.APPROVER, method='AddDispatchOperation', + predicate=lambda e: e.args[1] == media_cdo_path, + handled=False) ) - assert len(e.args[0]) == 2 - assert (text_chan.object_path, text_channel_properties) in e.args[0] - assert (media_chan.object_path, media_channel_properties) in e.args[0] - assert e.args[1:] == [cdo_path, cdo_properties] - assert k.args == e.args + assert len(e_approve_text.args[0]) == 1 + assert ((text_chan.object_path, text_channel_properties) in + e_approve_text.args[0]) + assert e_approve_text.args[1:] == [text_cdo_path, text_cdo_properties] + assert k_approve_text.args == e_approve_text.args - q.dbus_return(e.message, signature='') - q.dbus_return(k.message, signature='') + assert len(e_approve_media.args[0]) == 1 + assert ((media_chan.object_path, media_channel_properties) in + e_approve_media.args[0]) + assert e_approve_media.args[1:] == [media_cdo_path, media_cdo_properties] + + q.dbus_return(e_approve_text.message, signature='') + q.dbus_return(k_approve_text.message, signature='') + q.dbus_return(e_approve_media.message, signature='') # Both Approvers now have a flashing icon or something, trying to get the - # user's attention + # user's attention. The user clicks on Empathy + call_async(q, text_cdo_iface, 'HandleWith', + cs.tp_name_prefix + '.Client.org.gnome.Empathy') + + # Empathy is asked to handle the channel + e = q.expect('dbus-method-call', + path=empathy.object_path, + interface=cs.HANDLER, method='HandleChannels', + handled=False) + + # Empathy accepts the channel + q.dbus_return(e.message, signature='') + + q.expect_many( + EventPattern('dbus-return', method='HandleWith'), + EventPattern('dbus-signal', interface=cs.CDO, signal='Finished'), + EventPattern('dbus-signal', interface=cs.CD_IFACE_OP_LIST, + signal='DispatchOperationFinished'), + ) - # The user doesn't care which one will handle the channels - because + # The user doesn't care which client will handle the channel - because # Empathy is the only possibility, it will be chosen (this is also a # regression test for the ability to leave the handler unspecified). - call_async(q, cdo_iface, 'HandleWith', '') + call_async(q, media_cdo_iface, 'HandleWith', '') - # Empathy is asked to handle the channels + # Empathy is asked to handle the channel e = q.expect('dbus-method-call', path=empathy.object_path, interface=cs.HANDLER, method='HandleChannels', handled=False) - # Empathy accepts the channels + # Empathy accepts the channel q.dbus_return(e.message, signature='') q.expect_many( |