From cba864bfbe7b7d3afb3dbe43289a20872489d172 Mon Sep 17 00:00:00 2001 From: Craig Tiller Date: Fri, 17 Feb 2017 10:27:56 -0800 Subject: Eliminate port server env var, force its use always, consolidate code --- test/core/util/BUILD | 3 +- test/core/util/port.c | 119 +++++++++++++++++ test/core/util/port_posix.c | 256 ------------------------------------ test/core/util/port_server_client.c | 10 +- test/core/util/port_server_client.h | 7 +- test/core/util/port_uv.c | 117 ---------------- test/core/util/port_windows.c | 247 ---------------------------------- 7 files changed, 130 insertions(+), 629 deletions(-) create mode 100644 test/core/util/port.c delete mode 100644 test/core/util/port_posix.c delete mode 100644 test/core/util/port_uv.c delete mode 100644 test/core/util/port_windows.c (limited to 'test/core/util') diff --git a/test/core/util/BUILD b/test/core/util/BUILD index 5c90f1db44..e6d0d247db 100644 --- a/test/core/util/BUILD +++ b/test/core/util/BUILD @@ -51,9 +51,8 @@ cc_library( "mock_endpoint.c", "parse_hexstring.c", "passthru_endpoint.c", - "port_posix.c", + "port.c", "port_server_client.c", - "port_windows.c", "reconnect_server.c", "slice_splitter.c", "test_tcp_server.c", diff --git a/test/core/util/port.c b/test/core/util/port.c new file mode 100644 index 0000000000..da1ed4e052 --- /dev/null +++ b/test/core/util/port.c @@ -0,0 +1,119 @@ +/* + * + * Copyright 2015, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include "src/core/lib/iomgr/port.h" +#include "test/core/util/test_config.h" +#if defined(GRPC_TEST_PICK_PORT) + +#include "test/core/util/port.h" + +#include +#include +#include + +#include +#include +#include +#include + +#include "src/core/lib/http/httpcli.h" +#include "src/core/lib/iomgr/resolve_address.h" +#include "src/core/lib/iomgr/sockaddr_utils.h" +#include "test/core/util/port_server_client.h" + +static int *chosen_ports = NULL; +static size_t num_chosen_ports = 0; + +static int free_chosen_port(int port) { + size_t i; + int found = 0; + size_t found_at = 0; + /* 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--; + grpc_free_port_using_server(port); + } + return found; +} + +static void free_chosen_ports(void) { + size_t i; + for (i = 0; i < num_chosen_ports; i++) { + grpc_free_port_using_server(chosen_ports[i]); + } + gpr_free(chosen_ports); +} + +static void chose_port(int port) { + if (chosen_ports == NULL) { + atexit(free_chosen_ports); + } + num_chosen_ports++; + chosen_ports = gpr_realloc(chosen_ports, sizeof(int) * num_chosen_ports); + chosen_ports[num_chosen_ports - 1] = port; +} + +int grpc_pick_unused_port(void) { + int port = grpc_pick_port_using_server(); + if (port != 0) { + chose_port(port); + } + + return port; +} + +int grpc_pick_unused_port_or_die(void) { + int port = grpc_pick_unused_port(); + if (port == 0) { + fprintf(stderr, + "gRPC tests require a helper port server to allocate ports used \n" + "during the test.\n\n" + "This server is not currently running.\n\n" + "To start it, run tools/run_tests/start_port_server.py\n\n"); + exit(1); + } + return port; +} + +void grpc_recycle_unused_port(int port) { GPR_ASSERT(free_chosen_port(port)); } + +#endif /* GRPC_TEST_PICK_PORT */ diff --git a/test/core/util/port_posix.c b/test/core/util/port_posix.c deleted file mode 100644 index 4a42e4c702..0000000000 --- a/test/core/util/port_posix.c +++ /dev/null @@ -1,256 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -#include "src/core/lib/iomgr/port.h" -#include "test/core/util/test_config.h" -#if defined(GRPC_POSIX_SOCKET) && defined(GRPC_TEST_PICK_PORT) - -#include "test/core/util/port.h" - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include "src/core/lib/http/httpcli.h" -#include "src/core/lib/iomgr/resolve_address.h" -#include "src/core/lib/iomgr/sockaddr_utils.h" -#include "src/core/lib/support/env.h" -#include "test/core/util/port_server_client.h" - -#define NUM_RANDOM_PORTS_TO_PICK 100 - -static int *chosen_ports = NULL; -static size_t num_chosen_ports = 0; - -static int has_port_been_chosen(int port) { - size_t i; - for (i = 0; i < num_chosen_ports; i++) { - if (chosen_ports[i] == port) { - return 1; - } - } - 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); - } - } - gpr_free(env); - return found; -} - -static void free_chosen_ports(void) { - char *env = gpr_getenv("GRPC_TEST_PORT_SERVER"); - if (env != NULL) { - size_t i; - for (i = 0; i < num_chosen_ports; i++) { - grpc_free_port_using_server(env, chosen_ports[i]); - } - gpr_free(env); - } - - gpr_free(chosen_ports); -} - -static void chose_port(int port) { - if (chosen_ports == NULL) { - atexit(free_chosen_ports); - } - num_chosen_ports++; - chosen_ports = gpr_realloc(chosen_ports, sizeof(int) * num_chosen_ports); - chosen_ports[num_chosen_ports - 1] = port; -} - -static bool is_port_available(int *port, bool is_tcp) { - GPR_ASSERT(*port >= 0); - GPR_ASSERT(*port <= 65535); - - /* For a port to be considered available, the kernel must support - at least one of (IPv6, IPv4), and the port must be available - on each supported family. */ - bool got_socket = false; - for (int is_ipv6 = 1; is_ipv6 >= 0; is_ipv6--) { - const int fd = - socket(is_ipv6 ? AF_INET6 : AF_INET, is_tcp ? SOCK_STREAM : SOCK_DGRAM, - is_tcp ? IPPROTO_TCP : 0); - if (fd >= 0) { - got_socket = true; - } else { - continue; - } - - /* Reuseaddr lets us start up a server immediately after it exits */ - const int one = 1; - if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)) < 0) { - gpr_log(GPR_ERROR, "setsockopt() failed: %s", strerror(errno)); - close(fd); - return false; - } - - /* Try binding to port */ - grpc_resolved_address addr; - if (is_ipv6) { - grpc_sockaddr_make_wildcard6(*port, &addr); /* [::]:port */ - } else { - grpc_sockaddr_make_wildcard4(*port, &addr); /* 0.0.0.0:port */ - } - if (bind(fd, (struct sockaddr *)addr.addr, (socklen_t)addr.len) < 0) { - gpr_log(GPR_DEBUG, "bind(port=%d) failed: %s", *port, strerror(errno)); - close(fd); - return false; - } - - /* Get the bound port number */ - if (getsockname(fd, (struct sockaddr *)addr.addr, (socklen_t *)&addr.len) < - 0) { - gpr_log(GPR_ERROR, "getsockname() failed: %s", strerror(errno)); - close(fd); - return false; - } - GPR_ASSERT(addr.len <= sizeof(addr.addr)); - const int actual_port = grpc_sockaddr_get_port(&addr); - GPR_ASSERT(actual_port > 0); - if (*port == 0) { - *port = actual_port; - } else { - GPR_ASSERT(*port == actual_port); - } - - close(fd); - } - if (!got_socket) { - gpr_log(GPR_ERROR, "socket() failed: %s", strerror(errno)); - return false; - } - return true; -} - -int grpc_pick_unused_port(void) { - /* We repeatedly pick a port and then see whether or not it is - available for use both as a TCP socket and a UDP socket. First, we - pick a random large port number. For subsequent - iterations, we bind to an anonymous port and let the OS pick the - port number. The random port picking reduces the probability of - races with other processes on kernels that want to reuse the same - port numbers over and over. */ - - /* In alternating iterations we trial UDP ports before TCP ports UDP - ports -- it could be the case that this machine has been using up - UDP ports and they are scarcer. */ - - /* Type of port to first pick in next iteration */ - bool is_tcp = true; - int trial = 0; - - char *env = gpr_getenv("GRPC_TEST_PORT_SERVER"); - if (env) { - int port = grpc_pick_port_using_server(env); - gpr_free(env); - if (port != 0) { - chose_port(port); - } - return port; - } - - for (;;) { - int port; - trial++; - if (trial == 1) { - port = getpid() % (65536 - 30000) + 30000; - } else if (trial <= NUM_RANDOM_PORTS_TO_PICK) { - port = rand() % (65536 - 30000) + 30000; - } else { - port = 0; - } - - if (has_port_been_chosen(port)) { - continue; - } - - if (!is_port_available(&port, is_tcp)) { - continue; - } - - GPR_ASSERT(port > 0); - /* Check that the port # is free for the other type of socket also */ - if (!is_port_available(&port, !is_tcp)) { - /* In the next iteration trial to bind to the other type first - because perhaps it is more rare. */ - is_tcp = !is_tcp; - continue; - } - - chose_port(port); - return port; - } - - /* The port iterator reached the end without finding a suitable port. */ - return 0; -} - -int grpc_pick_unused_port_or_die(void) { - int port = grpc_pick_unused_port(); - GPR_ASSERT(port > 0); - return port; -} - -void grpc_recycle_unused_port(int port) { GPR_ASSERT(free_chosen_port(port)); } - -#endif /* GRPC_POSIX_SOCKET && GRPC_TEST_PICK_PORT */ diff --git a/test/core/util/port_server_client.c b/test/core/util/port_server_client.c index 7b733ab9c7..455b4e0a7d 100644 --- a/test/core/util/port_server_client.c +++ b/test/core/util/port_server_client.c @@ -74,7 +74,7 @@ static void freed_port_from_server(grpc_exec_ctx *exec_ctx, void *arg, gpr_mu_unlock(pr->mu); } -void grpc_free_port_using_server(char *server, int port) { +void grpc_free_port_using_server(int port) { grpc_httpcli_context context; grpc_httpcli_request req; grpc_httpcli_response rsp; @@ -95,7 +95,7 @@ void grpc_free_port_using_server(char *server, int port) { shutdown_closure = grpc_closure_create(destroy_pops_and_shutdown, &pr.pops, grpc_schedule_on_exec_ctx); - req.host = server; + req.host = GRPC_PORT_SERVER_ADDRESS; gpr_asprintf(&path, "/drop/%d", port); req.http.path = path; @@ -198,7 +198,7 @@ static void got_port_from_server(grpc_exec_ctx *exec_ctx, void *arg, gpr_mu_unlock(pr->mu); } -int grpc_pick_port_using_server(char *server) { +int grpc_pick_port_using_server(void) { grpc_httpcli_context context; grpc_httpcli_request req; portreq pr; @@ -215,10 +215,10 @@ int grpc_pick_port_using_server(char *server) { shutdown_closure = grpc_closure_create(destroy_pops_and_shutdown, &pr.pops, grpc_schedule_on_exec_ctx); pr.port = -1; - pr.server = server; + pr.server = GRPC_PORT_SERVER_ADDRESS; pr.ctx = &context; - req.host = server; + req.host = GRPC_PORT_SERVER_ADDRESS; req.http.path = "/get"; grpc_httpcli_context_init(&context); diff --git a/test/core/util/port_server_client.h b/test/core/util/port_server_client.h index 437006495c..70471ecb8a 100644 --- a/test/core/util/port_server_client.h +++ b/test/core/util/port_server_client.h @@ -36,7 +36,10 @@ // C interface to port_server.py -int grpc_pick_port_using_server(char *server); -void grpc_free_port_using_server(char *server, int port); +// must be synchronized with tools/run_tests/python_utils/start_port_server.py +#define GRPC_PORT_SERVER_ADDRESS "localhost:32766" + +int grpc_pick_port_using_server(void); +void grpc_free_port_using_server(int port); #endif // GRPC_TEST_CORE_UTIL_PORT_SERVER_CLIENT_H diff --git a/test/core/util/port_uv.c b/test/core/util/port_uv.c deleted file mode 100644 index 0c9c0d87d6..0000000000 --- a/test/core/util/port_uv.c +++ /dev/null @@ -1,117 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -#include "src/core/lib/iomgr/port.h" -#include "test/core/util/test_config.h" -#if defined(GRPC_UV) && defined(GRPC_TEST_PICK_PORT) - -#include -#include - -#include "src/core/lib/support/env.h" -#include "test/core/util/port.h" -#include "test/core/util/port_server_client.h" - -// Almost everything in this file has been copied from port_posix.c - -static int *chosen_ports = NULL; -static size_t num_chosen_ports = 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); - } - } - gpr_free(env); - return found; -} - -static void free_chosen_ports(void) { - char *env = gpr_getenv("GRPC_TEST_PORT_SERVER"); - if (env != NULL) { - size_t i; - for (i = 0; i < num_chosen_ports; i++) { - grpc_free_port_using_server(env, chosen_ports[i]); - } - gpr_free(env); - } - - gpr_free(chosen_ports); -} - -static void chose_port(int port) { - if (chosen_ports == NULL) { - atexit(free_chosen_ports); - } - num_chosen_ports++; - chosen_ports = gpr_realloc(chosen_ports, sizeof(int) * num_chosen_ports); - chosen_ports[num_chosen_ports - 1] = port; -} - -int grpc_pick_unused_port(void) { - // Currently only works with the port server - char *env = gpr_getenv("GRPC_TEST_PORT_SERVER"); - GPR_ASSERT(env); - int port = grpc_pick_port_using_server(env); - gpr_free(env); - if (port != 0) { - chose_port(port); - } - return port; -} - -int grpc_pick_unused_port_or_die(void) { - int port = grpc_pick_unused_port(); - GPR_ASSERT(port > 0); - return port; -} - -void grpc_recycle_unused_port(int port) { GPR_ASSERT(free_chosen_port(port)); } - -#endif /* GRPC_UV && GRPC_TEST_PICK_PORT */ diff --git a/test/core/util/port_windows.c b/test/core/util/port_windows.c deleted file mode 100644 index 0c50a46644..0000000000 --- a/test/core/util/port_windows.c +++ /dev/null @@ -1,247 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -#include "src/core/lib/iomgr/port.h" -#include "test/core/util/test_config.h" -#if defined(GRPC_WINSOCK_SOCKET) && defined(GRPC_TEST_PICK_PORT) - -#include "src/core/lib/iomgr/sockaddr.h" - -#include "test/core/util/port.h" - -#include -#include -#include -#include - -#include -#include -#include - -#include "src/core/lib/http/httpcli.h" -#include "src/core/lib/iomgr/sockaddr_utils.h" -#include "src/core/lib/support/env.h" -#include "test/core/util/port_server_client.h" - -#if GPR_GETPID_IN_UNISTD_H -#include -static int _getpid() { return getpid(); } -#endif - -#define NUM_RANDOM_PORTS_TO_PICK 100 - -static int *chosen_ports = NULL; -static size_t num_chosen_ports = 0; - -static int has_port_been_chosen(int port) { - size_t i; - for (i = 0; i < num_chosen_ports; i++) { - if (chosen_ports[i] == port) { - return 1; - } - } - 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) { - size_t i; - for (i = 0; i < num_chosen_ports; i++) { - grpc_free_port_using_server(env, chosen_ports[i]); - } - gpr_free(env); - } - - gpr_free(chosen_ports); -} - -static void chose_port(int port) { - if (chosen_ports == NULL) { - atexit(free_chosen_ports); - } - num_chosen_ports++; - chosen_ports = gpr_realloc(chosen_ports, sizeof(int) * num_chosen_ports); - chosen_ports[num_chosen_ports - 1] = port; -} - -static int is_port_available(int *port, int is_tcp) { - const int proto = is_tcp ? IPPROTO_TCP : 0; - const SOCKET fd = socket(AF_INET, is_tcp ? SOCK_STREAM : SOCK_DGRAM, proto); - int one = 1; - struct sockaddr_in addr; - socklen_t alen = sizeof(addr); - int actual_port; - - GPR_ASSERT(*port >= 0); - GPR_ASSERT(*port <= 65535); - if (INVALID_SOCKET == fd) { - gpr_log(GPR_ERROR, "socket() failed: %s", strerror(errno)); - return 0; - } - - /* Reuseaddr lets us start up a server immediately after it exits */ - if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (const char *)&one, - sizeof(one)) < 0) { - gpr_log(GPR_ERROR, "setsockopt() failed: %s", strerror(errno)); - closesocket(fd); - return 0; - } - - /* Try binding to port */ - addr.sin_family = AF_INET; - addr.sin_addr.s_addr = INADDR_ANY; - addr.sin_port = htons((u_short)*port); - if (bind(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) { - gpr_log(GPR_DEBUG, "bind(port=%d) failed: %s", *port, strerror(errno)); - closesocket(fd); - return 0; - } - - /* Get the bound port number */ - if (getsockname(fd, (struct sockaddr *)&addr, &alen) < 0) { - gpr_log(GPR_ERROR, "getsockname() failed: %s", strerror(errno)); - closesocket(fd); - return 0; - } - GPR_ASSERT(alen <= (socklen_t)sizeof(addr)); - actual_port = ntohs(addr.sin_port); - GPR_ASSERT(actual_port > 0); - if (*port == 0) { - *port = actual_port; - } else { - GPR_ASSERT(*port == actual_port); - } - - closesocket(fd); - return 1; -} - -int grpc_pick_unused_port(void) { - /* We repeatedly pick a port and then see whether or not it is - available for use both as a TCP socket and a UDP socket. First, we - pick a random large port number. For subsequent - iterations, we bind to an anonymous port and let the OS pick the - port number. The random port picking reduces the probability of - races with other processes on kernels that want to reuse the same - port numbers over and over. */ - - /* In alternating iterations we trial UDP ports before TCP ports UDP - ports -- it could be the case that this machine has been using up - UDP ports and they are scarcer. */ - - /* Type of port to first pick in next iteration */ - int is_tcp = 1; - int trial = 0; - - char *env = gpr_getenv("GRPC_TEST_PORT_SERVER"); - if (env) { - int port = grpc_pick_port_using_server(env); - gpr_free(env); - if (port != 0) { - return port; - } - } - - for (;;) { - int port; - trial++; - if (trial == 1) { - port = _getpid() % (65536 - 30000) + 30000; - } else if (trial <= NUM_RANDOM_PORTS_TO_PICK) { - port = rand() % (65536 - 30000) + 30000; - } else { - port = 0; - } - - if (has_port_been_chosen(port)) { - continue; - } - - if (!is_port_available(&port, is_tcp)) { - continue; - } - - GPR_ASSERT(port > 0); - /* Check that the port # is free for the other type of socket also */ - if (!is_port_available(&port, !is_tcp)) { - /* In the next iteration trial to bind to the other type first - because perhaps it is more rare. */ - is_tcp = !is_tcp; - continue; - } - - /* TODO(ctiller): consider caching this port in some structure, to avoid - handing it out again */ - - chose_port(port); - return port; - } - - /* The port iterator reached the end without finding a suitable port. */ - return 0; -} - -int grpc_pick_unused_port_or_die(void) { - int port = grpc_pick_unused_port(); - GPR_ASSERT(port > 0); - return port; -} - -void grpc_recycle_unused_port(int port) { GPR_ASSERT(free_chosen_port(port)); } - -#endif /* GRPC_WINSOCK_SOCKET && GRPC_TEST_PICK_PORT */ -- cgit v1.2.3