THRIFT: configurable retry settings for bind

Summary: SO_REUSEADDR doesn't always work, and some of my servers have
expensive init routines that I really want to run before it becomes available
to services, so I want thrift to retry the bind several times at varied
intervals before giving up

Reviewed By: slee

Test Plan: running a service in GDB with no problems binding, with another
service bound to the port for all the retries, another service bound to the
port for only some of the retries


git-svn-id: https://svn.apache.org/repos/asf/incubator/thrift/trunk@665109 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/lib/cpp/src/transport/TServerSocket.cpp b/lib/cpp/src/transport/TServerSocket.cpp
index 3b11e9d..e942a67 100644
--- a/lib/cpp/src/transport/TServerSocket.cpp
+++ b/lib/cpp/src/transport/TServerSocket.cpp
@@ -25,6 +25,8 @@
   acceptBacklog_(1024),
   sendTimeout_(0),
   recvTimeout_(0),
+  retryLimit_(0),
+  retryDelay_(0),
   intSock1_(-1),
   intSock2_(-1) {}
 
@@ -34,6 +36,8 @@
   acceptBacklog_(1024),
   sendTimeout_(sendTimeout),
   recvTimeout_(recvTimeout),
+  retryLimit_(0),
+  retryDelay_(0),
   intSock1_(-1),
   intSock2_(-1) {}
 
@@ -49,6 +53,14 @@
   recvTimeout_ = recvTimeout;
 }
 
+void TServerSocket::setRetryLimit(int retryLimit) {
+  retryLimit_ = retryLimit;
+}
+
+void TServerSocket::setRetryDelay(int retryDelay) {
+  retryDelay_ = retryDelay;
+}
+
 void TServerSocket::listen() {
   int sv[2];
   if (-1 == socketpair(AF_LOCAL, SOCK_STREAM, 0, sv)) {
@@ -113,13 +125,26 @@
     throw TTransportException(TTransportException::NOT_OPEN, "fcntl() failed");
   }
 
-  // Bind to a port
+  // prepare the port information
   struct sockaddr_in addr;
   memset(&addr, 0, sizeof(addr));
   addr.sin_family = AF_INET;
   addr.sin_port = htons(port_);
   addr.sin_addr.s_addr = INADDR_ANY;
-  if (-1 == bind(serverSocket_, (struct sockaddr *)&addr, sizeof(addr))) {
+  
+  // we may want to try to bind more than once, since SO_REUSEADDR doesn't 
+  // always seem to work. The client can configure the retry variables.
+  int retries = 0;
+  do {
+    if (0 == bind(serverSocket_, (struct sockaddr *)&addr, sizeof(addr))) {
+      break;
+    }
+
+    // use short circuit evaluation here to only sleep if we need to
+  } while ((retries++ < retryLimit_) && (sleep(retryDelay_) == 0));
+
+  // throw an error if we failed to bind properly
+  if (retries > retryLimit_) {
     char errbuf[1024];
     sprintf(errbuf, "TServerSocket::listen() BIND %d", port_);
     perror(errbuf);
@@ -203,7 +228,7 @@
   if (recvTimeout_ > 0) {
     client->setRecvTimeout(recvTimeout_);
   }
-  
+
   return client;
 }
 
diff --git a/lib/cpp/src/transport/TServerSocket.h b/lib/cpp/src/transport/TServerSocket.h
index edd2510..8d76fef 100644
--- a/lib/cpp/src/transport/TServerSocket.h
+++ b/lib/cpp/src/transport/TServerSocket.h
@@ -30,6 +30,9 @@
   void setSendTimeout(int sendTimeout);
   void setRecvTimeout(int recvTimeout);
 
+  void setRetryLimit(int retryLimit);
+  void setRetryDelay(int retryDelay);
+
   void listen();
   void close();
 
@@ -44,6 +47,8 @@
   int acceptBacklog_;
   int sendTimeout_;
   int recvTimeout_;
+  int retryLimit_;
+  int retryDelay_;
 
   int intSock1_;
   int intSock2_;