Add an API to return an unused port to the portserver
diff --git a/test/core/util/port.h b/test/core/util/port.h
index 93788bc..4b70fdc 100644
--- a/test/core/util/port.h
+++ b/test/core/util/port.h
@@ -45,6 +45,12 @@
    on failure. */
 int grpc_pick_unused_port_or_die();
 
+/* 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();
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/test/core/util/port_posix.c b/test/core/util/port_posix.c
index eabd62f..32b1849 100644
--- a/test/core/util/port_posix.c
+++ b/test/core/util/port_posix.c
@@ -68,6 +68,31 @@
   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--;
+      chosen_ports = gpr_realloc(chosen_ports, sizeof(int) * num_chosen_ports);
+    }
+  }
+  return found;
+}
+
 static void free_chosen_ports(void) {
   char *env = gpr_getenv("GRPC_TEST_PORT_SERVER");
   if (env != NULL) {
@@ -210,4 +235,8 @@
   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..29f3404 100644
--- a/test/core/util/port_windows.c
+++ b/test/core/util/port_windows.c
@@ -71,6 +71,31 @@
   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--;
+      chosen_ports = gpr_realloc(chosen_ports, sizeof(int) * num_chosen_ports);
+    }
+  }
+  return found;
+}
+
 static void free_chosen_ports(void) {
   char *env = gpr_getenv("GRPC_TEST_PORT_SERVER");
   if (env != NULL) {
@@ -216,4 +241,8 @@
   return port;
 }
 
+void grpc_recycle_unused_port(int port) {
+  GPR_ASSERT(free_chosen_port(port));
+}
+
 #endif /* GPR_WINSOCK_SOCKET && GRPC_TEST_PICK_PORT */