static void on_writable(grpc_exec_ctx *exec_ctx, void *acp, int success) { async_connect *ac = acp; int so_error = 0; socklen_t so_error_size; int err; int done; grpc_endpoint **ep = ac->ep; grpc_closure *closure = ac->closure; grpc_fd *fd; if (grpc_tcp_trace) { gpr_log(GPR_DEBUG, "CLIENT_CONNECT: %s: on_writable: success=%d", ac->addr_str, success); } gpr_mu_lock(&ac->mu); GPR_ASSERT(ac->fd); fd = ac->fd; ac->fd = NULL; gpr_mu_unlock(&ac->mu); grpc_alarm_cancel(exec_ctx, &ac->alarm); gpr_mu_lock(&ac->mu); if (success) { do { so_error_size = sizeof(so_error); err = getsockopt(fd->fd, SOL_SOCKET, SO_ERROR, &so_error, &so_error_size); } while (err < 0 && errno == EINTR); if (err < 0) { gpr_log(GPR_ERROR, "failed to connect to '%s': getsockopt(ERROR): %s", ac->addr_str, strerror(errno)); goto finish; } else if (so_error != 0) { if (so_error == ENOBUFS) { /* We will get one of these errors if we have run out of memory in the kernel for the data structures allocated when you connect a socket. If this happens it is very likely that if we wait a little bit then try again the connection will work (since other programs or this program will close their network connections and free up memory). This does _not_ indicate that there is anything wrong with the server we are connecting to, this is a local problem. If you are looking at this code, then chances are that your program or another program on the same computer opened too many network connections. The "easy" fix: don't do that! */ gpr_log(GPR_ERROR, "kernel out of buffers"); gpr_mu_unlock(&ac->mu); grpc_fd_notify_on_write(exec_ctx, fd, &ac->write_closure); return; } else { switch (so_error) { case ECONNREFUSED: gpr_log( GPR_ERROR, "failed to connect to '%s': socket error: connection refused", ac->addr_str); break; default: gpr_log(GPR_ERROR, "failed to connect to '%s': socket error: %d", ac->addr_str, so_error); break; } goto finish; } } else { grpc_pollset_set_del_fd(exec_ctx, ac->interested_parties, fd); *ep = grpc_tcp_create(fd, GRPC_TCP_DEFAULT_READ_SLICE_SIZE, ac->addr_str); fd = NULL; goto finish; } } else { gpr_log(GPR_ERROR, "failed to connect to '%s': timeout occurred", ac->addr_str); goto finish; } GPR_UNREACHABLE_CODE(return ); finish: if (fd != NULL) { grpc_pollset_set_del_fd(exec_ctx, ac->interested_parties, fd); grpc_fd_orphan(exec_ctx, fd, NULL, "tcp_client_orphan"); fd = NULL; } done = (--ac->refs == 0); gpr_mu_unlock(&ac->mu); if (done) { gpr_mu_destroy(&ac->mu); gpr_free(ac->addr_str); gpr_free(ac); } grpc_exec_ctx_enqueue(exec_ctx, closure, *ep != NULL); }
/* Pollset_set with an empty pollset */ void pollset_set_test_empty_pollset() { /* We construct the following structure for this test: * * +---> PS0 (EMPTY) * | * +---> FD0 * | * PSS0---+ * | +---> FD1 * | | * +---> PS1--+ * | * +---> FD2 */ grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; grpc_pollset_worker *worker; gpr_timespec deadline; test_fd tfds[3]; test_pollset pollsets[2]; test_pollset_set pollset_set; const int num_fds = GPR_ARRAY_SIZE(tfds); const int num_ps = GPR_ARRAY_SIZE(pollsets); const int num_pss = 1; init_test_fds(&exec_ctx, tfds, num_fds); init_test_pollsets(pollsets, num_ps); init_test_pollset_sets(&pollset_set, num_pss); /* Construct the structure */ grpc_pollset_set_add_fd(&exec_ctx, pollset_set.pss, tfds[0].fd); grpc_pollset_add_fd(&exec_ctx, pollsets[1].ps, tfds[1].fd); grpc_pollset_add_fd(&exec_ctx, pollsets[1].ps, tfds[2].fd); grpc_pollset_set_add_pollset(&exec_ctx, pollset_set.pss, pollsets[0].ps); grpc_pollset_set_add_pollset(&exec_ctx, pollset_set.pss, pollsets[1].ps); /* Test. Make all FDs readable and make sure that can be observed by doing * grpc_pollset_work on the empty pollset 'PS0' */ make_test_fds_readable(tfds, num_fds); gpr_mu_lock(pollsets[0].mu); deadline = grpc_timeout_milliseconds_to_deadline(2); GPR_ASSERT(GRPC_ERROR_NONE == grpc_pollset_work(&exec_ctx, pollsets[0].ps, &worker, gpr_now(GPR_CLOCK_MONOTONIC), deadline)); gpr_mu_unlock(pollsets[0].mu); grpc_exec_ctx_flush(&exec_ctx); verify_readable_and_reset(&exec_ctx, tfds, num_fds); grpc_exec_ctx_flush(&exec_ctx); /* Tear down */ grpc_pollset_set_del_fd(&exec_ctx, pollset_set.pss, tfds[0].fd); grpc_pollset_set_del_pollset(&exec_ctx, pollset_set.pss, pollsets[0].ps); grpc_pollset_set_del_pollset(&exec_ctx, pollset_set.pss, pollsets[1].ps); grpc_exec_ctx_flush(&exec_ctx); cleanup_test_fds(&exec_ctx, tfds, num_fds); cleanup_test_pollsets(&exec_ctx, pollsets, num_ps); cleanup_test_pollset_sets(&exec_ctx, &pollset_set, num_pss); grpc_exec_ctx_finish(&exec_ctx); }
static void on_writable(grpc_exec_ctx *exec_ctx, void *acp, grpc_error *error) { async_connect *ac = acp; int so_error = 0; socklen_t so_error_size; int err; int done; grpc_endpoint **ep = ac->ep; grpc_closure *closure = ac->closure; grpc_fd *fd; GRPC_ERROR_REF(error); if (grpc_tcp_trace) { const char *str = grpc_error_string(error); gpr_log(GPR_DEBUG, "CLIENT_CONNECT: %s: on_writable: error=%s", ac->addr_str, str); } gpr_mu_lock(&ac->mu); GPR_ASSERT(ac->fd); fd = ac->fd; ac->fd = NULL; gpr_mu_unlock(&ac->mu); grpc_timer_cancel(exec_ctx, &ac->alarm); gpr_mu_lock(&ac->mu); if (error != GRPC_ERROR_NONE) { error = grpc_error_set_str(error, GRPC_ERROR_STR_OS_ERROR, grpc_slice_from_static_string("Timeout occurred")); goto finish; } do { so_error_size = sizeof(so_error); err = getsockopt(grpc_fd_wrapped_fd(fd), SOL_SOCKET, SO_ERROR, &so_error, &so_error_size); } while (err < 0 && errno == EINTR); if (err < 0) { error = GRPC_OS_ERROR(errno, "getsockopt"); goto finish; } switch (so_error) { case 0: grpc_pollset_set_del_fd(exec_ctx, ac->interested_parties, fd); *ep = grpc_tcp_client_create_from_fd(exec_ctx, fd, ac->channel_args, ac->addr_str); fd = NULL; break; case ENOBUFS: /* We will get one of these errors if we have run out of memory in the kernel for the data structures allocated when you connect a socket. If this happens it is very likely that if we wait a little bit then try again the connection will work (since other programs or this program will close their network connections and free up memory). This does _not_ indicate that there is anything wrong with the server we are connecting to, this is a local problem. If you are looking at this code, then chances are that your program or another program on the same computer opened too many network connections. The "easy" fix: don't do that! */ gpr_log(GPR_ERROR, "kernel out of buffers"); gpr_mu_unlock(&ac->mu); grpc_fd_notify_on_write(exec_ctx, fd, &ac->write_closure); return; case ECONNREFUSED: /* This error shouldn't happen for anything other than connect(). */ error = GRPC_OS_ERROR(so_error, "connect"); break; default: /* We don't really know which syscall triggered the problem here, so punt by reporting getsockopt(). */ error = GRPC_OS_ERROR(so_error, "getsockopt(SO_ERROR)"); break; } finish: if (fd != NULL) { grpc_pollset_set_del_fd(exec_ctx, ac->interested_parties, fd); grpc_fd_orphan(exec_ctx, fd, NULL, NULL, "tcp_client_orphan"); fd = NULL; } done = (--ac->refs == 0); gpr_mu_unlock(&ac->mu); if (error != GRPC_ERROR_NONE) { char *error_descr; grpc_slice str; bool ret = grpc_error_get_str(error, GRPC_ERROR_STR_DESCRIPTION, &str); GPR_ASSERT(ret); char *desc = grpc_slice_to_c_string(str); gpr_asprintf(&error_descr, "Failed to connect to remote host: %s", desc); error = grpc_error_set_str(error, GRPC_ERROR_STR_DESCRIPTION, grpc_slice_from_copied_string(error_descr)); gpr_free(error_descr); gpr_free(desc); error = grpc_error_set_str(error, GRPC_ERROR_STR_TARGET_ADDRESS, grpc_slice_from_copied_string(ac->addr_str)); } if (done) { gpr_mu_destroy(&ac->mu); gpr_free(ac->addr_str); grpc_channel_args_destroy(exec_ctx, ac->channel_args); gpr_free(ac); } grpc_closure_sched(exec_ctx, closure, error); }
/* Test some typical scenarios in pollset_set */ static void pollset_set_test_basic() { /* We construct the following structure for this test: * * +---> FD0 (Added before PSS1, PS1 and PS2 are added to PSS0) * | * +---> FD5 (Added after PSS1, PS1 and PS2 are added to PSS0) * | * | * | +---> FD1 (Added before PSS1 is added to PSS0) * | | * | +---> FD6 (Added after PSS1 is added to PSS0) * | | * +---> PSS1--+ +--> FD2 (Added before PS0 is added to PSS1) * | | | * | +---> PS0---+ * | | * PSS0---+ +--> FD7 (Added after PS0 is added to PSS1) * | * | * | +---> FD3 (Added before PS1 is added to PSS0) * | | * +---> PS1---+ * | | * | +---> FD8 (Added after PS1 added to PSS0) * | * | * | +---> FD4 (Added before PS2 is added to PSS0) * | | * +---> PS2---+ * | * +---> FD9 (Added after PS2 is added to PSS0) */ grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; grpc_pollset_worker *worker; gpr_timespec deadline; test_fd tfds[10]; test_pollset pollsets[3]; test_pollset_set pollset_sets[2]; const int num_fds = GPR_ARRAY_SIZE(tfds); const int num_ps = GPR_ARRAY_SIZE(pollsets); const int num_pss = GPR_ARRAY_SIZE(pollset_sets); init_test_fds(&exec_ctx, tfds, num_fds); init_test_pollsets(pollsets, num_ps); init_test_pollset_sets(pollset_sets, num_pss); /* Construct the pollset_set/pollset/fd tree (see diagram above) */ grpc_pollset_set_add_fd(&exec_ctx, pollset_sets[0].pss, tfds[0].fd); grpc_pollset_set_add_fd(&exec_ctx, pollset_sets[1].pss, tfds[1].fd); grpc_pollset_add_fd(&exec_ctx, pollsets[0].ps, tfds[2].fd); grpc_pollset_add_fd(&exec_ctx, pollsets[1].ps, tfds[3].fd); grpc_pollset_add_fd(&exec_ctx, pollsets[2].ps, tfds[4].fd); grpc_pollset_set_add_pollset_set(&exec_ctx, pollset_sets[0].pss, pollset_sets[1].pss); grpc_pollset_set_add_pollset(&exec_ctx, pollset_sets[1].pss, pollsets[0].ps); grpc_pollset_set_add_pollset(&exec_ctx, pollset_sets[0].pss, pollsets[1].ps); grpc_pollset_set_add_pollset(&exec_ctx, pollset_sets[0].pss, pollsets[2].ps); grpc_pollset_set_add_fd(&exec_ctx, pollset_sets[0].pss, tfds[5].fd); grpc_pollset_set_add_fd(&exec_ctx, pollset_sets[1].pss, tfds[6].fd); grpc_pollset_add_fd(&exec_ctx, pollsets[0].ps, tfds[7].fd); grpc_pollset_add_fd(&exec_ctx, pollsets[1].ps, tfds[8].fd); grpc_pollset_add_fd(&exec_ctx, pollsets[2].ps, tfds[9].fd); grpc_exec_ctx_flush(&exec_ctx); /* Test that if any FD in the above structure is readable, it is observable by * doing grpc_pollset_work on any pollset * * For every pollset, do the following: * - (Ensure that all FDs are in reset state) * - Make all FDs readable * - Call grpc_pollset_work() on the pollset * - Flush the exec_ctx * - Verify that on_readable call back was called for all FDs (and * reset the FDs) * */ for (int i = 0; i < num_ps; i++) { make_test_fds_readable(tfds, num_fds); gpr_mu_lock(pollsets[i].mu); deadline = grpc_timeout_milliseconds_to_deadline(2); GPR_ASSERT(GRPC_ERROR_NONE == grpc_pollset_work(&exec_ctx, pollsets[i].ps, &worker, gpr_now(GPR_CLOCK_MONOTONIC), deadline)); gpr_mu_unlock(pollsets[i].mu); grpc_exec_ctx_flush(&exec_ctx); verify_readable_and_reset(&exec_ctx, tfds, num_fds); grpc_exec_ctx_flush(&exec_ctx); } /* Test tear down */ grpc_pollset_set_del_fd(&exec_ctx, pollset_sets[0].pss, tfds[0].fd); grpc_pollset_set_del_fd(&exec_ctx, pollset_sets[0].pss, tfds[5].fd); grpc_pollset_set_del_fd(&exec_ctx, pollset_sets[1].pss, tfds[1].fd); grpc_pollset_set_del_fd(&exec_ctx, pollset_sets[1].pss, tfds[6].fd); grpc_exec_ctx_flush(&exec_ctx); grpc_pollset_set_del_pollset(&exec_ctx, pollset_sets[1].pss, pollsets[0].ps); grpc_pollset_set_del_pollset(&exec_ctx, pollset_sets[0].pss, pollsets[1].ps); grpc_pollset_set_del_pollset(&exec_ctx, pollset_sets[0].pss, pollsets[2].ps); grpc_pollset_set_del_pollset_set(&exec_ctx, pollset_sets[0].pss, pollset_sets[1].pss); grpc_exec_ctx_flush(&exec_ctx); cleanup_test_fds(&exec_ctx, tfds, num_fds); cleanup_test_pollsets(&exec_ctx, pollsets, num_ps); cleanup_test_pollset_sets(&exec_ctx, pollset_sets, num_pss); grpc_exec_ctx_finish(&exec_ctx); }