Merge pull request #6589 from dklempner/return_unused_port

Add a grpc_recycle_unused_port to return pick_unused_port ports to the portserver
diff --git a/test/core/end2end/dualstack_socket_test.c b/test/core/end2end/dualstack_socket_test.c
index 81f76ea..202fb3b 100644
--- a/test/core/end2end/dualstack_socket_test.c
+++ b/test/core/end2end/dualstack_socket_test.c
@@ -88,9 +88,11 @@
   int was_cancelled = 2;
   grpc_call_details call_details;
   char *peer;
+  int picked_port = 0;
 
   if (port == 0) {
     port = grpc_pick_unused_port_or_die();
+    picked_port = 1;
   }
 
   gpr_join_host_port(&server_hostport, server_host, port);
@@ -263,6 +265,9 @@
 
   grpc_call_details_destroy(&call_details);
   gpr_free(details);
+  if (picked_port) {
+    grpc_recycle_unused_port(port);
+  }
 }
 
 int external_dns_works(const char *host) {
diff --git a/test/core/util/port.h b/test/core/util/port.h
index 93788bc..faeabba 100644
--- a/test/core/util/port.h
+++ b/test/core/util/port.h
@@ -40,10 +40,16 @@
 
 /* pick a port number that is currently unused by either tcp or udp. return
    0 on failure. */
-int grpc_pick_unused_port();
+int grpc_pick_unused_port(void);
 /* pick a port number that is currently unused by either tcp or udp. abort
    on failure. */
-int grpc_pick_unused_port_or_die();
+int grpc_pick_unused_port_or_die(void);
+
+/* Return a port which was previously returned by grpc_pick_unused_port().
+ * Implementations of grpc_pick_unused_port() backed by a portserver may limit
+ * the total number of ports available; this lets a binary return its allocated
+ * ports back to the server if it is going to allocate a large number. */
+void grpc_recycle_unused_port(int port);
 
 #ifdef __cplusplus
 }
diff --git a/test/core/util/port_posix.c b/test/core/util/port_posix.c
index eabd62f..f139601 100644
--- a/test/core/util/port_posix.c
+++ b/test/core/util/port_posix.c
@@ -68,6 +68,30 @@
   return 0;
 }
 
+static int free_chosen_port(int port) {
+  size_t i;
+  int found = 0;
+  size_t found_at = 0;
+  char *env = gpr_getenv("GRPC_TEST_PORT_SERVER");
+  /* Find the port and erase it from the list, then tell the server it can be
+     freed. */
+  for (i = 0; i < num_chosen_ports; i++) {
+    if (chosen_ports[i] == port) {
+      GPR_ASSERT(found == 0);
+      found = 1;
+      found_at = i;
+    }
+  }
+  if (found) {
+    chosen_ports[found_at] = chosen_ports[num_chosen_ports - 1];
+    num_chosen_ports--;
+    if (env) {
+      grpc_free_port_using_server(env, port);
+    }
+  }
+  return found;
+}
+
 static void free_chosen_ports(void) {
   char *env = gpr_getenv("GRPC_TEST_PORT_SERVER");
   if (env != NULL) {
@@ -210,4 +234,6 @@
   return port;
 }
 
+void grpc_recycle_unused_port(int port) { GPR_ASSERT(free_chosen_port(port)); }
+
 #endif /* GPR_POSIX_SOCKET && GRPC_TEST_PICK_PORT */
diff --git a/test/core/util/port_windows.c b/test/core/util/port_windows.c
index 154d607..9023719 100644
--- a/test/core/util/port_windows.c
+++ b/test/core/util/port_windows.c
@@ -71,6 +71,30 @@
   return 0;
 }
 
+static int free_chosen_port(int port) {
+  size_t i;
+  int found = 0;
+  size_t found_at = 0;
+  char *env = gpr_getenv("GRPC_TEST_PORT_SERVER");
+  if (env != NULL) {
+    /* Find the port and erase it from the list, then tell the server it can be
+       freed. */
+    for (i = 0; i < num_chosen_ports; i++) {
+      if (chosen_ports[i] == port) {
+        GPR_ASSERT(found == 0);
+        found = 1;
+        found_at = i;
+      }
+    }
+    if (found) {
+      chosen_ports[found_at] = chosen_ports[num_chosen_ports - 1];
+      grpc_free_port_using_server(env, port);
+      num_chosen_ports--;
+    }
+  }
+  return found;
+}
+
 static void free_chosen_ports(void) {
   char *env = gpr_getenv("GRPC_TEST_PORT_SERVER");
   if (env != NULL) {
@@ -216,4 +240,6 @@
   return port;
 }
 
+void grpc_recycle_unused_port(int port) { GPR_ASSERT(free_chosen_port(port)); }
+
 #endif /* GPR_WINSOCK_SOCKET && GRPC_TEST_PICK_PORT */
diff --git a/test/cpp/end2end/async_end2end_test.cc b/test/cpp/end2end/async_end2end_test.cc
index e0649bd..45f5eb1 100644
--- a/test/cpp/end2end/async_end2end_test.cc
+++ b/test/cpp/end2end/async_end2end_test.cc
@@ -245,8 +245,8 @@
   void SetUp() GRPC_OVERRIDE {
     poll_overrider_.reset(new PollingOverrider(!GetParam().disable_blocking));
 
-    int port = grpc_pick_unused_port_or_die();
-    server_address_ << "localhost:" << port;
+    port_ = grpc_pick_unused_port_or_die();
+    server_address_ << "localhost:" << port_;
 
     // Setup server
     ServerBuilder builder;
@@ -274,6 +274,7 @@
       ;
     poll_overrider_.reset();
     gpr_tls_set(&g_is_async_end2end_test, 0);
+    grpc_recycle_unused_port(port_);
   }
 
   void ResetStub() {
@@ -325,6 +326,7 @@
   std::unique_ptr<Server> server_;
   grpc::testing::EchoTestService::AsyncService service_;
   std::ostringstream server_address_;
+  int port_;
 
   std::unique_ptr<PollingOverrider> poll_overrider_;
 };