[Bug 34931] Add a "meta porter"

bugzilla-daemon at freedesktop.org bugzilla-daemon at freedesktop.org
Tue Mar 15 21:52:23 CET 2011


https://bugs.freedesktop.org/show_bug.cgi?id=34931

--- Comment #5 from Will Thompson <will.thompson at collabora.co.uk> 2011-03-15 13:52:23 PDT ---
This is a review of wocky-meta-porter.[ch] in its (gargantuan) entirety. I
still haven't reviewed the loopback stream.

 ~~~

This file is *enormous*. I wonder whether parts of it could be split out into
separate files to help keep it manageable.

  if (p->porter != NULL)
    {
      g_object_weak_unref (G_OBJECT (p->porter),
          porter_disposed, data);

      g_object_unref (p->porter);
    }

If PorterData owns a ref to its porter field, why does it also weak-ref it?
It's pretty exciting how this file contains a function called porter_disposed()
and another called porter_disposed_cb().

  if (!wocky_porter_close_finish (porter, result, &error))
    {
      DEBUG ("Failed to close porter: %s", error->message);
      g_clear_error (&error);
    }
  else
    {
      DEBUG ("Closed porter to '%s'", data->jid);
    }

If it's worth including the jid on one branch, it's worth including it on the
other. I also do wonder what we should do if closing a porter fails (for a
reason other than, say, ‘already closed’) — force close it?

  if (data->porter != NULL)
    g_object_unref (data->porter);
  data->porter = NULL;

‘g_clear_object (&data->porter)’.

  /* weak reffed WockyPorter* => handler ID */
  GHashTable *porters;

  DEBUG ("porter closed by remote, remove it from our records");

but it doesn't actually seem to be removed from the dictionary of porters.
“It'll be removed because it's weak-reffed” isn't a good idea — you don't want
to rely on that kind of side-effect. I think the hash table key should be a
reffed porter.

(Reading this comment again, I think I may be confused between the various
dictionaries of porters …)

  /* maybe start the timeout */
  if (data->refcount == 0)
    {
      DEBUG ("Started porter timeout...");
      data->timeout_id = g_timeout_add_seconds (5, porter_timeout_cb, data);
    }

This code is duplicated; add maybe_start_timeout().

  typedef struct
  {
    WockyMetaPorter *self;
    GInetAddress *addr;
  } NewConnectionData;

If you were feeling keen you could fish 'addr' out of the WockyXmppConnection
returned by wocky_ll_connector_finish() and then you wouldn't need a context
struct.

loopback_recv_open_cb and loopback_sent_open_cb both leak on the error path.

I think that any existing loopback porter should be torn down if set_jid() is
called with an existing JID — either that or it should g_return_if_fail().

  /* Convenience function to call @callback with a porter and do all the
   * handling the creating a porter if necessary. Also, omg two user datas! */
  static void
  open_porter_if_necessary (WockyMetaPorter *self,
      WockyLLContact *contact,
      GCancellable *cancellable,
      OpenPorterIfNecessaryFunc callback,
      gpointer user_data1,
      gpointer user_data2)

Every caller of this function passes a GSimpleAsyncResult as the first
user_data. Maybe its signature should reflect that.

  PorterData *porter = g_hash_table_lookup (priv->porters, contact);

  if (porter != NULL && porter->porter != NULL)

This perhaps should suggest that the name of the local variable is wrong. ;-)

  data = g_slice_new0 (OpenPorterData);
  data->self = self;
  data->contact = g_object_ref (contact);
  data->callback = callback;
  data->cancellable = cancellable;
  data->user_data1 = user_data1;
  data->user_data2 = user_data2;

If user_data1 was a GSimpleAsyncResult, you wouldn't need to hang onto self in
data: you could get_self_object() on the GSAR.

  /* stamp on from if there is none */
  if (wocky_node_get_attribute (wocky_stanza_get_top_node (stanza),
          "from") == NULL)

This is secret code for ‘wocky_stanza_get_from (stanza) == NULL’.

static void
send (WockyMetaPorter *self,
    WockyPorter *porter,
    GCancellable *cancellable,
    const GError *error,
    gpointer user_data1,
    gpointer user_data2)

send is a terrible name for this function. How about:

  meta_porter_send_cb()
  meta_porter_send_got_porter_cb()
  wocky_meta_porter_send_async()

for this chain of functions?

  if (error != NULL)
    {
      DEBUG ("Failed to listen: %s", error->message);
      g_clear_error (&error);
    }

  DEBUG ("listening on port %u", port);

  g_socket_service_start (G_SOCKET_SERVICE (priv->listener));

  priv->port = port;

Are you missing a 'return' there?

In porter_handler_cb:

  from = wocky_node_get_attribute (wocky_stanza_get_top_node (stanza), "from");

  contact = wocky_contact_factory_ensure_ll_contact (
      priv->contact_factory, from);

Also secret code; and, is it guaranteed to be non-NULL?

  handler = g_slice_new0 (StanzaHandler);
  handler->self = self;
  handler->contact = g_object_ref (from);
  handler->porters = g_hash_table_new (g_direct_hash, g_direct_equal);

Pass NULL, NULL to g_hash_table_new(). You should also have a function that
creates a StanzaHandler struct and stuffs it into the handlers hash table,
rather than duplicating that code.

  /* there were no porters to close anyway */
  if (num == 0)
    {
      g_simple_async_result_complete (simple);
      g_object_unref (simple);
    }

This leaks the ClosePorterData structure. Instead, have a
close_porters_maybe_complete() function called from here and from
porter_close_cb().

    g_simple_async_result_set_op_res_gpointer (simple, stanza, NULL);

is a no-op.

  void
  wocky_meta_porter_open_async (WockyMetaPorter *self,
      WockyContact *contact,
      ^^^^^^^^^^^^
      GCancellable *cancellable,
      GAsyncReadyCallback callback,
      gpointer user_data)
  {
    GSimpleAsyncResult *simple;

    g_return_if_fail (WOCKY_IS_META_PORTER (self));
    g_return_if_fail (WOCKY_IS_LL_CONTACT (contact));
                               ^^

If the function requires a WockyLLContact then its signature should say so.
Ditto functions like wocky_meta_porter_borrow_connection().

  /**
   * wocky_meta_porter_open_finish:
   * @porter: a #WockyMetaPorter
   * @result: the #GAsyncResult
   * @error: an optional #GError location to store an error message
   *
   * Finishes an asynchronous request to open a connection to @contact

This function has no contact argument:                        ^^^^^^^^

  /**
   * wocky_meta_porter_borrow_connection:
   * @porter: a #WockyMetaPorter
   * @contact: the #WockyContact
   *
   * Borrow the #GSocketConnection of the porter to @contact, if one
   * exists otherwise %NULL will be returned. Note that the connection
   * returned is not reffed and should not be kept as it still is owned

                                              ^^^^

Well, the caller can keep it if they want, by reffing it, but they should be
aware that it might be disconnected spontaneously unless they hold() the
corresponding contact.

   * and operated on by the underlying #WockyXmppConnection object.
   *
   * Returns: the #GSocketConnection or %NULL if no connection is open
   */

I'd be inclined to make this return a ref, too, to be honest. I'm not really
all that hung up on it, but code that unrefs a pointer and then returns that
same pointer looks suspect. :)

(It also be nice to have accessors for C2SPorter:connection and
XmppConnetion:base-stream so that you don't have to use g_object_get() in the
first place and then this would look less suspect.)

-- 
Configure bugmail: https://bugs.freedesktop.org/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
You are the QA contact for the bug.
You are the assignee for the bug.


More information about the telepathy-bugs mailing list