Bug 27874

Summary: TpBaseClient Approver support
Product: Telepathy Reporter: Simon McVittie <smcv>
Component: tp-glibAssignee: Guillaume Desmottes <guillaume.desmottes>
Status: RESOLVED FIXED QA Contact: Telepathy bugs list <telepathy-bugs>
Severity: enhancement    
Priority: medium CC: danielle
Version: unspecifiedKeywords: patch
Hardware: Other   
OS: All   
URL: http://git.collabora.co.uk/?p=user/cassidy/telepathy-glib;a=shortlog;h=refs/heads/approver
Whiteboard: review+
i915 platform: i915 features:
Bug Depends on: 25236, 27833    
Bug Blocks: 27875    

Description Simon McVittie 2010-04-28 08:49:11 UTC
+++ This bug was initially created as a clone of Bug #27872 +++

Cloned from Bug #25236. There was skeletal support for being an Approver, but it was deleted before merging; it should be reinstated, and filled in.

Tentatively assigning to Guillaume, who implemented the Observer equivalent.

This also requires a decent API for TpChannelDispatchOperation, for which I'll file a bug in a moment.
Comment 1 Guillaume Desmottes 2010-05-04 02:32:40 UTC
I'm not sure about the best API for this.
The signature of my current callback for the AddDispatchOperation implementation is:

typedef void (*TpBaseClientClassAddDispatchOperationImpl) (
    TpBaseClient *client,
    GList *channels,
    TpChannelDispatchOperation *dispatch_operation,
    TpAddDispatchOperationContext *context);

All the channels have TP_CHANNEL_FEATURE_CORE prepared and the CDO has TP_CHANNEL_DISPATCH_OPERATION_FEATURE_CORE prepared.

The channels list is a bit redundant as we can get them using tp_channel_dispatch_operation_borrow_channels().
On the other hand, TP_CHANNEL_DISPATCH_OPERATION_FEATURE_CORE doesn't prepare the channels objects so the channels returned by tp_channel_dispatch_operation_borrow_channels() are not guaranteed to be prepared.

Should we remove the GList from this callback? In that case should we add a TP_CHANNEL_DISPATCH_OPERATION_FEATURE_CHANNELS_CORE_PREPARED feature and ensure that the CDO has this feature prepared?
If we do that, the list of channels passed to AddDispatchOperation() would be ignored as CDO will fetch them from D-Bus.
Comment 2 Guillaume Desmottes 2010-05-05 06:03:33 UTC
Let's say we let the channels arg in the callback. How should we handle channels who failed to prepare (because they've been invalidated in most/all the cases)?
a) Pass them to the callback? Then the list of channels could be different from the one returned by tp_channel_dispatch_operation_borrow_channels()
b) Ignore them and don't pass them to the callback.

Should we also pass an invalidated TpChannelDispatchOperation object to the callback or ignore it and return from the D-Bus call right away?
Comment 4 Simon McVittie 2010-05-05 06:13:36 UTC
I think the distinction is: the Channels passed to AddDispatchOperation are an *initial* set of channels, while the CDO's Channels property is the *current* set of channels.

I'd be inclined to pass them to the callback; also, the Account and Connection.

Another thing to consider, which I missed during TpBaseClient code review (sorry): uniqueness guarantees. At the moment, you always make a new TpAccount and TpConnection in _tp_base_client_observe_channels, and presumably in the Approver. I think it should be more like this:

* each TpBaseClient has a reference to a TpAccountManager
* if the TpBaseClient's dbus_daemon is the same as returned by tp_dbus_daemon_dup(), then the account manager is guaranteed to be the one from tp_account_manager_dup(); otherwise, it's given by tp_account_manager_new()
* the TpAccount in ObserveChannels is made using tp_account_manager_ensure_account() on the AM
* the TpConnection is made using tp_account_ensure_connection() on the Account
* the channels are attached to that TpConnection

This means that any TpContacts will all come from the same TpConnection, which is a considerable win.
Comment 5 Guillaume Desmottes 2010-05-06 01:37:52 UTC
API is now:

/**
 * TpBaseClientClassAddDispatchOperationImpl:
 * @client: a #TpBaseClient instance
 * @account: a #TpAccount having %TP_ACCOUNT_FEATURE_CORE prepared if possible
 * @connection: a #TpConnection having %TP_CONNECTION_FEATURE_CORE prepared
 * if possible
 * @channels: (element-type TelepathyGLib.Channel): a #GList of #TpChannel,
 *  all having %TP_CHANNEL_FEATURE_CORE prepared if possible
 * @dispatch_operation: a #TpChannelDispatchOperation having
 * %TP_CHANNEL_DISPATCH_OPERATION_FEATURE_CORE prepared if possible
 * @context: a #TpObserveChannelsContext representing the context of this
 *  D-Bus call
 *
 * Signature of the implementation of the AddDispatchOperation method.
 *
 * This function must call either tp_add_dispatch_operation_context_accept(),
 * tp_add_dispatch_operation_context_delay() or
 * tp_add_dispatch_operation_context_fail() on @context before it returns.
 *
 * Since: 0.11.UNRELEASED
 */
Comment 6 Guillaume Desmottes 2010-05-06 03:59:34 UTC
(In reply to comment #1)
> On the other hand, TP_CHANNEL_DISPATCH_OPERATION_FEATURE_CORE doesn't prepare
> the channels objects so the channels returned by
> tp_channel_dispatch_operation_borrow_channels() are not guaranteed to be
> prepared.

I thinks that's annoying as, in practice that's the channels list that will be used by the approver.
It's silly that those TpChannel are not the same ones as the one passed in the callback.
Suggestion?
Comment 7 Simon McVittie 2010-05-06 10:15:42 UTC
(I'm reviewing this as part of Bug #27833.)
Comment 8 Guillaume Desmottes 2010-05-07 02:36:46 UTC
Replying here as that's the right bug.

> > + * This function must call either tp_add_dispatch_operation_context_accept(),
> > + * tp_add_dispatch_operation_context_delay() or
> > + * tp_add_dispatch_operation_context_fail() on @context before it returns.
> 
> I think it's also worth putting in a reference to the HandleWith and Claim
> wrappers.

Done.

> > +      if (g_error_matches (error, TP_DBUS_ERRORS, TP_DBUS_ERROR_OBJECT_REMOVED))
> > +        {
> > +          /* There is no point calling the AddDispatchOperation
> > +           * implementation; just terminate the D-Bus call right away */
> > +          tp_add_dispatch_operation_context_accept (ctx);
> > +        }
> 
> This isn't meant to happen - ChannelLost and Finished are meant to be delayed
> until all approvers have returned from AddDispatchOperation (per the spec), and
> we're an approver that hasn't returned. So, I don't think it's worth a special
> case.

Cool. Removed.

> >     test-base-client: test invalidating one channel while calling AddDispatchOperation
> 
> This looks as if it contains a secret bugfix? :-)

Yeah, but I probably squash the fix while rebasing. :)
Comment 9 Guillaume Desmottes 2010-05-07 02:50:27 UTC
(In reply to comment #9)
> As with the Observer context, it would be good to use the shared
> TpAccountManager, TpAccount, TpConnection chain if possible.
> 
> It might be good to have the CDO and the Approver context receive an account
> and a connection as optional construct-time properties, which would change the
> processing to something like:
> 
> set_property:
>     remember the immutable properties
>     *don't* make Account etc. proxies
> 
> constructed:
>     if no Account, Connection, etc. were supplied at construct time,
>     look in the immutable properties and try to set them
> 
> Alternatively, the CDO and context could take the TpAccountManager as a
> construct-time property, and start the chain there.

Why not just do the same trick in the CDO using _tp_dbus_daemon_is_the_shared_one and create the AM accordingly? That way we wouldn't have to add new API.

Note that this doesn't solve the "issue" of having different TpChannel objects in the context and in CDO.
Comment 10 Simon McVittie 2010-05-07 03:57:00 UTC
(In reply to comment #9)
> Why not just do the same trick in the CDO using
> _tp_dbus_daemon_is_the_shared_one and create the AM accordingly? That way we
> wouldn't have to add new API.

You unavoidably end up with a different TpAccount and TpConnection in all non-shared-bus cases, which seems less than ideal.

> Note that this doesn't solve the "issue" of having different TpChannel objects
> in the context and in CDO.

Yes, that's a fair point; perhaps the approver context should be able to stuff TpChannel objects into the CDO, too (maybe with library-internal API).

For Observers that are acting as non-interactive approvers, it would also be useful for the observer context to be able to pre-populate the CDO with the right TpAccount, TpConnection and initial TpChannel objects; the CDO still wouldn't be pre-prepared, but when asked to prepare, it would use its existing objects.
Comment 11 Simon McVittie 2010-05-07 05:04:30 UTC
Trivia seen when reading through the combined diff (you can consider the fixes for these to have been pre-reviewed):

> +++ b/telepathy-glib/add-dispatch-operation-context-internal.h

Copyright date should be 2010 like the others, or 2009-2010 if it's derived from code from 2009

> +  void (*chain_up) (GObject *) =
> +    ((GObjectClass *) \
> +      tp_add_dispatch_operation_context_parent_class)->constructed;

You don't need that backslash, line-joining is generally only relevant in macro definitions

> + * Implementation can then use tp_channel_dispatch_operation_handle_with_async()
> + * or tp_channel_dispatch_operation_claim_async() to approve or disapprove the
> + * channels.

"The implementation can then use tp_channel_dispatch_operation_handle_with_async() to approve handling of the channels, or tp_channel_dispatch_operation_claim_async() to take responsibility for handling or closing them", perhaps?

> and may only be called on objects that implement
> + * @add_dispatch_operation.

"and may only be called on objects whose class has called tp_base_client_implement_add_dispatch_operation()" perhaps?

> +  if (cls->priv->add_dispatch_operation_impl == NULL)
> +    {
> +      DEBUG ("class %s does not implement AddDispatchOperation",
> +          G_OBJECT_TYPE_NAME (self));

CRITICAL()? This isn't meant to be reachable, AFAICS.
Comment 12 Guillaume Desmottes 2010-05-07 05:31:19 UTC
(In reply to comment #11)
> Trivia seen when reading through the combined diff (you can consider the fixes
> for these to have been pre-reviewed):
> 
> > +++ b/telepathy-glib/add-dispatch-operation-context-internal.h
> 
> Copyright date should be 2010 like the others, or 2009-2010 if it's derived
> from code from 2009

Fixed.

> > +  void (*chain_up) (GObject *) =
> > +    ((GObjectClass *) \
> > +      tp_add_dispatch_operation_context_parent_class)->constructed;
> 
> You don't need that backslash, line-joining is generally only relevant in macro
> definitions

removed.

> > + * Implementation can then use tp_channel_dispatch_operation_handle_with_async()
> > + * or tp_channel_dispatch_operation_claim_async() to approve or disapprove the
> > + * channels.
> 
> "The implementation can then use
> tp_channel_dispatch_operation_handle_with_async() to approve handling of the
> channels, or tp_channel_dispatch_operation_claim_async() to take responsibility
> for handling or closing them", perhaps?

changed.

> > and may only be called on objects that implement
> > + * @add_dispatch_operation.
> 
> "and may only be called on objects whose class has called
> tp_base_client_implement_add_dispatch_operation()" perhaps?

done. I changed the Observer API as well to stay coherent.

> > +  if (cls->priv->add_dispatch_operation_impl == NULL)
> > +    {
> > +      DEBUG ("class %s does not implement AddDispatchOperation",
> > +          G_OBJECT_TYPE_NAME (self));
> 
> CRITICAL()? This isn't meant to be reachable, AFAICS.

Wouldn't that make the client remotely crashable by calling a D-Bus method it doesn't implement?
Comment 13 Simon McVittie 2010-05-07 05:47:10 UTC
(In reply to comment #12)
> > > +  if (cls->priv->add_dispatch_operation_impl == NULL)
> > > +    {
> > > +      DEBUG ("class %s does not implement AddDispatchOperation",
> > > +          G_OBJECT_TYPE_NAME (self));
> > 
> > CRITICAL()? This isn't meant to be reachable, AFAICS.
> 
> Wouldn't that make the client remotely crashable by calling a D-Bus method it
> doesn't implement?

AFAICS, you already raised NotImplemented if the "is an approver" flag wasn't set, and trying to set that flag without having an AddDispatchOperation implementation would already have caused a critical?
Comment 14 Guillaume Desmottes 2010-05-07 06:24:45 UTC
(In reply to comment #13)
> (In reply to comment #12)
> > > > +  if (cls->priv->add_dispatch_operation_impl == NULL)
> > > > +    {
> > > > +      DEBUG ("class %s does not implement AddDispatchOperation",
> > > > +          G_OBJECT_TYPE_NAME (self));
> > > 
> > > CRITICAL()? This isn't meant to be reachable, AFAICS.
> > 
> > Wouldn't that make the client remotely crashable by calling a D-Bus method it
> > doesn't implement?
> 
> AFAICS, you already raised NotImplemented if the "is an approver" flag wasn't
> set, and trying to set that flag without having an AddDispatchOperation
> implementation would already have caused a critical?

You're right. Done for the Observer and Approver.
Comment 15 Guillaume Desmottes 2010-05-10 02:41:57 UTC
http://git.collabora.co.uk/?p=user/cassidy/telepathy-glib;a=shortlog;h=refs/heads/approver
contains the previous branches and fix the "recycling proxies" issue.
Comment 16 Simon McVittie 2010-05-10 02:54:40 UTC
> +        "dbus-connection", ((TpProxy *) bus_daemon)->dbus_connection,

This is redundant - just pass in the bus daemon and TpProxy will DTRT. Add a check to the regression test that the dbus-connection is the same as the bus daemon's dbus-connection, if you're worried.

Strictly speaking, _tp_channel_dispatch_operation_new_with_objects doesn't need the bus_daemon argument - it could get it from the TpAccount - but that's probably unnecessarily subtle.

Other than that, this looks good.
Comment 17 Guillaume Desmottes 2010-05-10 05:04:29 UTC
done
Comment 18 Simon McVittie 2010-05-10 05:07:11 UTC
Looks good, ship it
Comment 19 Guillaume Desmottes 2010-05-10 05:13:02 UTC
Merged! Will be in 0.11.5

Use of freedesktop.org services, including Bugzilla, is subject to our Code of Conduct. How we collect and use information is described in our Privacy Policy.