| /* |
| * |
| * 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/iomgr/pollset_kick_posix.h" |
| |
| #include <errno.h> |
| #include <string.h> |
| #include <unistd.h> |
| |
| #include "src/core/iomgr/socket_utils_posix.h" |
| #include <grpc/support/alloc.h> |
| #include <grpc/support/log.h> |
| |
| /* This implementation is based on a freelist of pipes. */ |
| |
| typedef struct grpc_kick_pipe_info { |
| int pipe_read_fd; |
| int pipe_write_fd; |
| struct grpc_kick_pipe_info *next; |
| } grpc_kick_pipe_info; |
| |
| static grpc_kick_pipe_info *pipe_freelist = NULL; |
| static gpr_mu pipe_freelist_mu; |
| |
| static grpc_kick_pipe_info *allocate_pipe() { |
| grpc_kick_pipe_info *info; |
| gpr_mu_lock(&pipe_freelist_mu); |
| if (pipe_freelist != NULL) { |
| info = pipe_freelist; |
| pipe_freelist = pipe_freelist->next; |
| } else { |
| int pipefd[2]; |
| /* TODO(klempner): Make this nonfatal */ |
| GPR_ASSERT(0 == pipe(pipefd)); |
| GPR_ASSERT(grpc_set_socket_nonblocking(pipefd[0], 1)); |
| GPR_ASSERT(grpc_set_socket_nonblocking(pipefd[1], 1)); |
| info = gpr_malloc(sizeof(*info)); |
| info->pipe_read_fd = pipefd[0]; |
| info->pipe_write_fd = pipefd[1]; |
| info->next = NULL; |
| } |
| gpr_mu_unlock(&pipe_freelist_mu); |
| return info; |
| } |
| |
| static void free_pipe(grpc_kick_pipe_info *pipe_info) { |
| /* TODO(klempner): Start closing pipes if the free list gets too large */ |
| gpr_mu_lock(&pipe_freelist_mu); |
| pipe_info->next = pipe_freelist; |
| pipe_freelist = pipe_info; |
| gpr_mu_unlock(&pipe_freelist_mu); |
| } |
| |
| void grpc_pollset_kick_global_init() { |
| pipe_freelist = NULL; |
| gpr_mu_init(&pipe_freelist_mu); |
| } |
| |
| void grpc_pollset_kick_global_destroy() { |
| while (pipe_freelist != NULL) { |
| grpc_kick_pipe_info *current = pipe_freelist; |
| pipe_freelist = pipe_freelist->next; |
| close(current->pipe_read_fd); |
| close(current->pipe_write_fd); |
| gpr_free(current); |
| } |
| gpr_mu_destroy(&pipe_freelist_mu); |
| } |
| |
| void grpc_pollset_kick_init(grpc_pollset_kick_state *kick_state) { |
| gpr_mu_init(&kick_state->mu); |
| kick_state->kicked = 0; |
| kick_state->pipe_info = NULL; |
| } |
| |
| void grpc_pollset_kick_destroy(grpc_pollset_kick_state *kick_state) { |
| gpr_mu_destroy(&kick_state->mu); |
| GPR_ASSERT(kick_state->pipe_info == NULL); |
| } |
| |
| int grpc_pollset_kick_pre_poll(grpc_pollset_kick_state *kick_state) { |
| gpr_mu_lock(&kick_state->mu); |
| if (kick_state->kicked) { |
| kick_state->kicked = 0; |
| gpr_mu_unlock(&kick_state->mu); |
| return -1; |
| } |
| kick_state->pipe_info = allocate_pipe(); |
| gpr_mu_unlock(&kick_state->mu); |
| return kick_state->pipe_info->pipe_read_fd; |
| } |
| |
| void grpc_pollset_kick_consume(grpc_pollset_kick_state *kick_state) { |
| char buf[128]; |
| int r; |
| |
| for (;;) { |
| r = read(kick_state->pipe_info->pipe_read_fd, buf, sizeof(buf)); |
| if (r > 0) continue; |
| if (r == 0) return; |
| switch (errno) { |
| case EAGAIN: |
| return; |
| case EINTR: |
| continue; |
| default: |
| gpr_log(GPR_ERROR, "error reading pipe: %s", strerror(errno)); |
| return; |
| } |
| } |
| } |
| |
| void grpc_pollset_kick_post_poll(grpc_pollset_kick_state *kick_state) { |
| gpr_mu_lock(&kick_state->mu); |
| free_pipe(kick_state->pipe_info); |
| kick_state->pipe_info = NULL; |
| gpr_mu_unlock(&kick_state->mu); |
| } |
| |
| void grpc_pollset_kick_kick(grpc_pollset_kick_state *kick_state) { |
| gpr_mu_lock(&kick_state->mu); |
| if (kick_state->pipe_info != NULL) { |
| char c = 0; |
| while (write(kick_state->pipe_info->pipe_write_fd, &c, 1) != 1 && |
| errno == EINTR) |
| ; |
| } else { |
| kick_state->kicked = 1; |
| } |
| gpr_mu_unlock(&kick_state->mu); |
| } |