[Telepathy-commits] [telepathy-salut/master] gibber_tcp_transport_connect is now async

Guillaume Desmottes guillaume.desmottes at collabora.co.uk
Fri Mar 6 08:06:28 PST 2009


---
 lib/gibber/gibber-tcp-transport.c |  202 ++++++++++++++++++++++++++++---------
 1 files changed, 153 insertions(+), 49 deletions(-)

diff --git a/lib/gibber/gibber-tcp-transport.c b/lib/gibber/gibber-tcp-transport.c
index 7c3c531..32f2379 100644
--- a/lib/gibber/gibber-tcp-transport.c
+++ b/lib/gibber/gibber-tcp-transport.c
@@ -22,7 +22,10 @@
 #include <stdio.h>
 #include <stdlib.h>
 
+#include <errno.h>
+#include <fcntl.h>
 #include <string.h>
+#include <unistd.h>
 
 #include "gibber-tcp-transport.h"
 
@@ -39,6 +42,11 @@ typedef struct _GibberTCPTransportPrivate GibberTCPTransportPrivate;
 
 struct _GibberTCPTransportPrivate
 {
+  GIOChannel *channel;
+  struct addrinfo *ans;
+  struct addrinfo *tmpaddr;
+  guint watch_in;
+
   gboolean dispose_has_run;
 };
 
@@ -71,6 +79,42 @@ gibber_tcp_transport_class_init (
   object_class->finalize = gibber_tcp_transport_finalize;
 }
 
+static void
+clean_connect_attempt (GibberTCPTransport *self)
+{
+  GibberTCPTransportPrivate *priv = GIBBER_TCP_TRANSPORT_GET_PRIVATE (
+      self);
+
+  if (priv->watch_in != 0)
+    {
+      g_source_remove (priv->watch_in);
+      priv->watch_in = 0;
+    }
+
+  if (priv->channel != NULL)
+    {
+      g_io_channel_unref (priv->channel);
+      priv->channel = NULL;
+    }
+}
+
+static void
+clean_all_connect_attempts (GibberTCPTransport *self)
+{
+  GibberTCPTransportPrivate *priv = GIBBER_TCP_TRANSPORT_GET_PRIVATE (
+      self);
+
+  clean_connect_attempt (self);
+
+  if (priv->ans != NULL)
+    {
+      freeaddrinfo (priv->ans);
+      priv->ans = NULL;
+    }
+
+  priv->tmpaddr = NULL;
+}
+
 void
 gibber_tcp_transport_dispose (GObject *object)
 {
@@ -82,6 +126,7 @@ gibber_tcp_transport_dispose (GObject *object)
 
   priv->dispose_has_run = TRUE;
 
+  clean_all_connect_attempts (self);
   /* release any references held by the object here */
 
   if (G_OBJECT_CLASS (gibber_tcp_transport_parent_class)->dispose)
@@ -107,75 +152,134 @@ gibber_tcp_transport_new ()
   return g_object_new (GIBBER_TYPE_TCP_TRANSPORT, NULL);
 }
 
-void
-gibber_tcp_transport_connect (GibberTCPTransport *tcp_transport,
-    const gchar *host, const gchar *port)
+static void new_connect_attempt (GibberTCPTransport *self);
+
+static gboolean
+try_to_connect (GibberTCPTransport *self)
 {
-  int fd = -1, ret = -1;
-  struct addrinfo req, *ans = NULL, *tmpaddr;
-  char name[NI_MAXHOST], portname[NI_MAXSERV];
+  GibberTCPTransportPrivate *priv = GIBBER_TCP_TRANSPORT_GET_PRIVATE (
+      self);
+  gint fd;
+  int ret;
 
-  gibber_transport_set_state (GIBBER_TRANSPORT (tcp_transport),
-                             GIBBER_TRANSPORT_CONNECTING);
+  g_assert (priv->channel != NULL);
 
-  memset (&req, 0, sizeof (req));
-  req.ai_flags = 0;
-  req.ai_family = AF_UNSPEC;
-  req.ai_socktype = SOCK_STREAM;
-  req.ai_protocol = IPPROTO_TCP;
+  fd = g_io_channel_unix_get_fd (priv->channel);
+  ret = connect (fd, priv->tmpaddr->ai_addr, priv->tmpaddr->ai_addrlen);
 
-  ret = getaddrinfo (host, port, &req, &ans);
-  if (ret != 0)
+  if (ret == 0)
     {
-      DEBUG("getaddrinfo failed: %s", gai_strerror (ret));
-      goto failed;
+      DEBUG ("connect succeeded");
+
+      clean_all_connect_attempts (self);
+      gibber_fd_transport_set_fd (GIBBER_FD_TRANSPORT (self), fd);
+      return FALSE;
     }
 
-  tmpaddr = ans;
-  while (tmpaddr != NULL)
+  if (errno == EALREADY || errno == EINPROGRESS)
     {
-      getnameinfo (tmpaddr->ai_addr, tmpaddr->ai_addrlen,
-          name, sizeof (name), portname, sizeof (portname),
-          NI_NUMERICHOST | NI_NUMERICSERV);
-
-      DEBUG ( "Trying %s port %s...", name, portname);
-
-      fd = socket (tmpaddr->ai_family, tmpaddr->ai_socktype,
-          tmpaddr->ai_protocol);
-
-      if (fd < 0)
-        {
-          DEBUG("socket failed: %s", strerror (errno));
-        }
-      else if ((ret = connect (fd, tmpaddr->ai_addr, tmpaddr->ai_addrlen)) < 0)
-        {
-          DEBUG( "connect failed: %s", strerror (errno));
-        }
-      else
-        {
-          break;
-        }
-
-      tmpaddr = tmpaddr->ai_next;
+      /* We have to wait longer */
+      return TRUE;
     }
 
-  if (ret != 0 || fd < 0)
+  clean_connect_attempt (self);
+  priv->tmpaddr = priv->tmpaddr->ai_next;
+  new_connect_attempt (self);
+  return FALSE;
+}
+
+static gboolean
+_channel_io (GIOChannel *source,
+             GIOCondition condition,
+             gpointer data)
+{
+  GibberTCPTransport *self = GIBBER_TCP_TRANSPORT (data);
+
+  return try_to_connect (self);
+}
+
+static void
+new_connect_attempt (GibberTCPTransport *self)
+{
+  GibberTCPTransportPrivate *priv = GIBBER_TCP_TRANSPORT_GET_PRIVATE (
+      self);
+  int fd;
+  char name[NI_MAXHOST], portname[NI_MAXSERV];
+
+  if (priv->tmpaddr == NULL)
     {
+      /* no more candidate to try */
+      DEBUG ("connection failed");
       goto failed;
     }
 
-  DEBUG ("succeeded");
+  getnameinfo (priv->tmpaddr->ai_addr, priv->tmpaddr->ai_addrlen,
+      name, sizeof (name), portname, sizeof (portname),
+      NI_NUMERICHOST | NI_NUMERICSERV);
+
+  DEBUG ("Trying %s port %s...", name, portname);
 
-  gibber_fd_transport_set_fd (GIBBER_FD_TRANSPORT (tcp_transport), fd);
+  fd = socket (priv->tmpaddr->ai_family, priv->tmpaddr->ai_socktype,
+      priv->tmpaddr->ai_protocol);
 
-  freeaddrinfo (ans);
+  if (fd < 0)
+    {
+      DEBUG("socket failed: %s", strerror (errno));
+      goto failed;
+    }
+
+  fcntl (fd, F_SETFL, O_NONBLOCK);
+  priv->channel = g_io_channel_unix_new (fd);
+  g_io_channel_set_close_on_unref (priv->channel, FALSE);
+  g_io_channel_set_encoding (priv->channel, NULL, NULL);
+  g_io_channel_set_buffered (priv->channel, FALSE);
+
+  priv->watch_in = g_io_add_watch (priv->channel, G_IO_IN | G_IO_PRI | G_IO_OUT,
+      _channel_io, self);
+
+  try_to_connect (self);
   return;
 
 failed:
-  if (ans != NULL)
-    freeaddrinfo (ans);
+  clean_all_connect_attempts (self);
 
-  gibber_transport_set_state (GIBBER_TRANSPORT (tcp_transport),
+  gibber_transport_set_state (GIBBER_TRANSPORT (self),
       GIBBER_TRANSPORT_DISCONNECTED);
+}
+
+void
+gibber_tcp_transport_connect (GibberTCPTransport *tcp_transport,
+    const gchar *host, const gchar *port)
+{
+  GibberTCPTransportPrivate *priv = GIBBER_TCP_TRANSPORT_GET_PRIVATE (
+      tcp_transport);
+  int ret = -1;
+  struct addrinfo req;
+
+  gibber_transport_set_state (GIBBER_TRANSPORT (tcp_transport),
+                             GIBBER_TRANSPORT_CONNECTING);
+
+  memset (&req, 0, sizeof (req));
+  req.ai_flags = 0;
+  req.ai_family = AF_UNSPEC;
+  req.ai_socktype = SOCK_STREAM;
+  req.ai_protocol = IPPROTO_TCP;
+
+  g_assert (priv->ans == NULL);
+  g_assert (priv->tmpaddr == NULL);
+  g_assert (priv->channel == NULL);
+
+  ret = getaddrinfo (host, port, &req, &priv->ans);
+  if (ret != 0)
+    {
+      DEBUG("getaddrinfo failed: %s", gai_strerror (ret));
+
+      gibber_transport_set_state (GIBBER_TRANSPORT (tcp_transport),
+          GIBBER_TRANSPORT_DISCONNECTED);
+      return;
+    }
+
+  priv->tmpaddr = priv->ans;
 
+  new_connect_attempt (tcp_transport);
 }
-- 
1.5.6.5



More information about the telepathy-commits mailing list