MEMCACHED_BEHAVIOR_RETRY_TIMEOUT added for timeout
[awesomized/libmemcached] / lib / memcached_connect.c
index f7167f451b8fbf8af1ca8a7808163b9acbf21243..4bfc710fa23e9c1543de27c46da52bbfa37d6116 100644 (file)
@@ -38,6 +38,78 @@ static memcached_return set_hostinfo(memcached_server_st *server)
   return MEMCACHED_SUCCESS;
 }
 
+static memcached_return set_socket_options(memcached_server_st *ptr)
+{
+  if (ptr->type == MEMCACHED_CONNECTION_UDP)
+    return MEMCACHED_SUCCESS;
+
+  if (ptr->root->flags & MEM_NO_BLOCK)
+  {
+    int error;
+    struct linger linger;
+    struct timeval waittime;
+
+    waittime.tv_sec= 10;
+    waittime.tv_usec= 0;
+
+    linger.l_onoff= 1; 
+    linger.l_linger= MEMCACHED_DEFAULT_TIMEOUT; 
+    error= setsockopt(ptr->fd, SOL_SOCKET, SO_LINGER, 
+                      &linger, (socklen_t)sizeof(struct linger));
+    WATCHPOINT_ASSERT(error == 0);
+
+    error= setsockopt(ptr->fd, SOL_SOCKET, SO_SNDTIMEO, 
+                      &waittime, (socklen_t)sizeof(struct timeval));
+    WATCHPOINT_ASSERT(error == 0);
+
+    error= setsockopt(ptr->fd, SOL_SOCKET, SO_RCVTIMEO, 
+                      &waittime, (socklen_t)sizeof(struct timeval));
+    WATCHPOINT_ASSERT(error == 0);
+  }
+
+  if (ptr->root->flags & MEM_TCP_NODELAY)
+  {
+    int flag= 1;
+    int error;
+
+    error= setsockopt(ptr->fd, IPPROTO_TCP, TCP_NODELAY, 
+                      &flag, (socklen_t)sizeof(int));
+    WATCHPOINT_ASSERT(error == 0);
+  }
+
+  if (ptr->root->send_size)
+  {
+    int error;
+
+    error= setsockopt(ptr->fd, SOL_SOCKET, SO_SNDBUF, 
+                      &ptr->root->send_size, (socklen_t)sizeof(int));
+    WATCHPOINT_ASSERT(error == 0);
+  }
+
+  if (ptr->root->recv_size)
+  {
+    int error;
+
+    error= setsockopt(ptr->fd, SOL_SOCKET, SO_SNDBUF, 
+                      &ptr->root->recv_size, (socklen_t)sizeof(int));
+    WATCHPOINT_ASSERT(error == 0);
+  }
+
+  /* For the moment, not getting a nonblocking mode will not be fatal */
+  if (ptr->root->flags & MEM_NO_BLOCK)
+  {
+    int flags;
+
+    flags= fcntl(ptr->fd, F_GETFL, 0);
+    if (flags != -1)
+    {
+      (void)fcntl(ptr->fd, F_SETFL, flags | O_NONBLOCK);
+    }
+  }
+
+  return MEMCACHED_SUCCESS;
+}
+
 static memcached_return unix_socket_connect(memcached_server_st *ptr)
 {
   struct sockaddr_un servAddr;
@@ -63,9 +135,49 @@ test_connect:
                 sizeof(servAddr)) < 0)
     {
       switch (errno) {
+      case EINPROGRESS:
+        {
+        struct timeval tm = { ptr->root->connect_timeout, 0 };
+        socklen_t len= sizeof(int);
+        fd_set wset;
+        int error=0, value;
+
+        FD_ZERO(&wset);
+        FD_SET(ptr->fd, &wset);
+
+        select(ptr->fd+1, NULL, &wset, NULL, &tm);
+        if (FD_ISSET(ptr->fd, &wset) != 0)
+        {
+          if (getsockopt(ptr->fd, SOL_SOCKET, SO_ERROR, &value, &len) == 0)
+          {
+            if (value)
+            {
+              error= 1;
+            }
+          }
+          else
+          {
+            error= 1;
+          }
+        }
+        else
+        {
+          error= 1;
+        }
+
+        if (error)
+        {
+          ptr->cached_errno= errno;
+          WATCHPOINT_ERRNO(ptr->cached_errno);
+          close(ptr->fd);
+          ptr->fd= -1;
+          return MEMCACHED_ERRNO;
+        }
+
+        break;
+        }
         /* We are spinning waiting on connect */
       case EALREADY:
-      case EINPROGRESS:
       case EINTR:
         goto test_connect;
       case EISCONN: /* We were spinning waiting on connect */
@@ -99,114 +211,63 @@ static memcached_return network_connect(memcached_server_st *ptr)
         return rc;
       ptr->sockaddr_inited= MEMCACHED_ALLOCATED;
     }
-    use= ptr->address_info;
 
+    use= ptr->address_info;
     /* Create the socket */
-    if ((ptr->fd= socket(use->ai_family, 
-                         use->ai_socktype, 
-                         use->ai_protocol)) < 0)
-    {
-      ptr->cached_errno= errno;
-      WATCHPOINT_ERRNO(errno);
-      return MEMCACHED_CONNECTION_SOCKET_CREATE_FAILURE;
-    }
-
-    if (ptr->type == MEMCACHED_CONNECTION_UDP)
-      return MEMCACHED_SUCCESS;
-
-    if (ptr->root->flags & MEM_NO_BLOCK)
-    {
-      int error;
-      struct linger linger;
-      struct timeval waittime;
-
-      waittime.tv_sec= 10;
-      waittime.tv_usec= 0;
-
-      linger.l_onoff= 1; 
-      linger.l_linger= MEMCACHED_DEFAULT_TIMEOUT; 
-      error= setsockopt(ptr->fd, SOL_SOCKET, SO_LINGER, 
-                        &linger, (socklen_t)sizeof(struct linger));
-      WATCHPOINT_ASSERT(error == 0);
-
-      error= setsockopt(ptr->fd, SOL_SOCKET, SO_SNDTIMEO, 
-                        &waittime, (socklen_t)sizeof(struct timeval));
-      WATCHPOINT_ASSERT(error == 0);
-
-      error= setsockopt(ptr->fd, SOL_SOCKET, SO_RCVTIMEO, 
-                        &waittime, (socklen_t)sizeof(struct timeval));
-      WATCHPOINT_ASSERT(error == 0);
-    }
-
-    if (ptr->root->flags & MEM_TCP_NODELAY)
+    while (use != NULL)
     {
-      int flag= 1;
-      int error;
-
-      error= setsockopt(ptr->fd, IPPROTO_TCP, TCP_NODELAY, 
-                        &flag, (socklen_t)sizeof(int));
-      WATCHPOINT_ASSERT(error == 0);
-    }
-
-    if (ptr->root->send_size)
-    {
-      int error;
-
-      error= setsockopt(ptr->fd, SOL_SOCKET, SO_SNDBUF, 
-                        &ptr->root->send_size, (socklen_t)sizeof(int));
-      WATCHPOINT_ASSERT(error == 0);
-    }
-
-    if (ptr->root->recv_size)
-    {
-      int error;
-
-      error= setsockopt(ptr->fd, SOL_SOCKET, SO_SNDBUF, 
-                        &ptr->root->recv_size, (socklen_t)sizeof(int));
-      WATCHPOINT_ASSERT(error == 0);
-    }
-
-    /* For the moment, not getting a nonblocking mode will not be fatal */
-    if (ptr->root->flags & MEM_NO_BLOCK)
-    {
-      int flags;
-
-      flags= fcntl(ptr->fd, F_GETFL, 0);
-      if (flags != -1)
+      if ((ptr->fd= socket(use->ai_family, 
+                           use->ai_socktype, 
+                           use->ai_protocol)) < 0)
       {
-        (void)fcntl(ptr->fd, F_SETFL, flags | O_NONBLOCK);
+        ptr->cached_errno= errno;
+        WATCHPOINT_ERRNO(errno);
+        return MEMCACHED_CONNECTION_SOCKET_CREATE_FAILURE;
       }
-    }
 
+      (void)set_socket_options(ptr);
 
-    /* connect to server */
+      /* connect to server */
 test_connect:
-    if (connect(ptr->fd, 
-                use->ai_addr, 
-                use->ai_addrlen) < 0)
-    {
-      switch (errno) {
-        /* We are spinning waiting on connect */
-      case EALREADY:
-      case EINPROGRESS:
-      case EINTR:
-        goto test_connect;
-      case EISCONN: /* We were spinning waiting on connect */
-        break;
-      default:
-        ptr->cached_errno= errno;
-        WATCHPOINT_ASSERT(errno == ECONNREFUSED);
-        WATCHPOINT_ERRNO(ptr->cached_errno);
-        close(ptr->fd);
-        ptr->fd= -1;
-        return MEMCACHED_ERRNO;
+      if (connect(ptr->fd, 
+                  use->ai_addr, 
+                  use->ai_addrlen) < 0)
+      {
+        switch (errno) {
+          /* We are spinning waiting on connect */
+        case EALREADY:
+        case EINPROGRESS:
+        case EINTR:
+          goto test_connect;
+        case EISCONN: /* We were spinning waiting on connect */
+          break;
+        default:
+          ptr->cached_errno= errno;
+          WATCHPOINT_ERRNO(ptr->cached_errno);
+          close(ptr->fd);
+          ptr->fd= -1;
+          if (ptr->root->retry_timeout)
+          {
+            struct timeval next_time;
+
+            gettimeofday(&next_time, NULL);
+            ptr->next_retry= next_time.tv_sec + ptr->root->retry_timeout;
+          }
+        }
       }
+      else
+      {
+        WATCHPOINT_ASSERT(ptr->cursor_active == 0);
+        return MEMCACHED_SUCCESS;
+      }
+      use = use->ai_next;
     }
-
-    WATCHPOINT_ASSERT(ptr->cursor_active == 0);
   }
 
-  return MEMCACHED_SUCCESS;
+  if (ptr->fd == -1)
+    return MEMCACHED_ERRNO; /* The last error should be from connect() */
+
+  return MEMCACHED_SUCCESS; /* The last error should be from connect() */
 }
 
 
@@ -215,6 +276,14 @@ memcached_return memcached_connect(memcached_server_st *ptr)
   memcached_return rc= MEMCACHED_NO_SERVERS;
   LIBMEMCACHED_MEMCACHED_CONNECT_START();
 
+  if (ptr->root->retry_timeout)
+  {
+    struct timeval next_time;
+
+    gettimeofday(&next_time, NULL);
+    if (next_time.tv_sec < ptr->next_retry)
+      return MEMCACHED_TIMEOUT;
+  }
   /* We need to clean up the multi startup piece */
   switch (ptr->type)
   {