|  | @@ -0,0 +1,635 @@
 | 
	
		
			
				|  |  | +/*
 | 
	
		
			
				|  |  | + *
 | 
	
		
			
				|  |  | + * Copyright 2016, 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"
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +/* This polling engine is only relevant on linux kernels supporting epoll() */
 | 
	
		
			
				|  |  | +#ifdef GRPC_LINUX_EPOLL
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +#include "src/core/lib/iomgr/ev_epoll_linux.h"
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +#include <assert.h>
 | 
	
		
			
				|  |  | +#include <errno.h>
 | 
	
		
			
				|  |  | +#include <poll.h>
 | 
	
		
			
				|  |  | +#include <pthread.h>
 | 
	
		
			
				|  |  | +#include <string.h>
 | 
	
		
			
				|  |  | +#include <sys/epoll.h>
 | 
	
		
			
				|  |  | +#include <sys/socket.h>
 | 
	
		
			
				|  |  | +#include <unistd.h>
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +#include <grpc/support/alloc.h>
 | 
	
		
			
				|  |  | +#include <grpc/support/log.h>
 | 
	
		
			
				|  |  | +#include <grpc/support/string_util.h>
 | 
	
		
			
				|  |  | +#include <grpc/support/tls.h>
 | 
	
		
			
				|  |  | +#include <grpc/support/useful.h>
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +#include "src/core/lib/iomgr/ev_posix.h"
 | 
	
		
			
				|  |  | +#include "src/core/lib/iomgr/iomgr_internal.h"
 | 
	
		
			
				|  |  | +#include "src/core/lib/iomgr/lockfree_event.h"
 | 
	
		
			
				|  |  | +#include "src/core/lib/iomgr/timer.h"
 | 
	
		
			
				|  |  | +#include "src/core/lib/iomgr/wakeup_fd_posix.h"
 | 
	
		
			
				|  |  | +#include "src/core/lib/iomgr/workqueue.h"
 | 
	
		
			
				|  |  | +#include "src/core/lib/profiling/timers.h"
 | 
	
		
			
				|  |  | +#include "src/core/lib/support/block_annotate.h"
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +/* Uncomment the following to enable extra checks on poll_object operations */
 | 
	
		
			
				|  |  | +/* #define PO_DEBUG */
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +/* TODO: sreek: Right now, this wakes up all pollers. In future we should make
 | 
	
		
			
				|  |  | + * sure to wake up one polling thread (which can wake up other threads if
 | 
	
		
			
				|  |  | + * needed) */
 | 
	
		
			
				|  |  | +static grpc_wakeup_fd global_wakeup_fd;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +/*******************************************************************************
 | 
	
		
			
				|  |  | + * Fd Declarations
 | 
	
		
			
				|  |  | + */
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +#define FD_FROM_PO(po) ((grpc_fd *)(po))
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +struct grpc_fd {
 | 
	
		
			
				|  |  | +  gpr_mu mu;
 | 
	
		
			
				|  |  | +  int fd;
 | 
	
		
			
				|  |  | +  /* refst format:
 | 
	
		
			
				|  |  | +       bit 0    : 1=Active / 0=Orphaned
 | 
	
		
			
				|  |  | +       bits 1-n : refcount
 | 
	
		
			
				|  |  | +     Ref/Unref by two to avoid altering the orphaned bit */
 | 
	
		
			
				|  |  | +  gpr_atm refst;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  /* The fd is either closed or we relinquished control of it. In either
 | 
	
		
			
				|  |  | +     cases, this indicates that the 'fd' on this structure is no longer
 | 
	
		
			
				|  |  | +     valid */
 | 
	
		
			
				|  |  | +  bool orphaned;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  gpr_atm read_closure;
 | 
	
		
			
				|  |  | +  gpr_atm write_closure;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  struct grpc_fd *freelist_next;
 | 
	
		
			
				|  |  | +  grpc_closure *on_done_closure;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  /* The pollset that last noticed that the fd is readable. The actual type
 | 
	
		
			
				|  |  | +   * stored in this is (grpc_pollset *) */
 | 
	
		
			
				|  |  | +  gpr_atm read_notifier_pollset;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  grpc_iomgr_object iomgr_object;
 | 
	
		
			
				|  |  | +};
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +/* Reference counting for fds */
 | 
	
		
			
				|  |  | +// #define GRPC_FD_REF_COUNT_DEBUG
 | 
	
		
			
				|  |  | +#ifdef GRPC_FD_REF_COUNT_DEBUG
 | 
	
		
			
				|  |  | +static void fd_ref(grpc_fd *fd, const char *reason, const char *file, int line);
 | 
	
		
			
				|  |  | +static void fd_unref(grpc_fd *fd, const char *reason, const char *file,
 | 
	
		
			
				|  |  | +                     int line);
 | 
	
		
			
				|  |  | +#define GRPC_FD_REF(fd, reason) fd_ref(fd, reason, __FILE__, __LINE__)
 | 
	
		
			
				|  |  | +#define GRPC_FD_UNREF(fd, reason) fd_unref(fd, reason, __FILE__, __LINE__)
 | 
	
		
			
				|  |  | +#else
 | 
	
		
			
				|  |  | +static void fd_ref(grpc_fd *fd);
 | 
	
		
			
				|  |  | +static void fd_unref(grpc_fd *fd);
 | 
	
		
			
				|  |  | +#define GRPC_FD_REF(fd, reason) fd_ref(fd)
 | 
	
		
			
				|  |  | +#define GRPC_FD_UNREF(fd, reason) fd_unref(fd)
 | 
	
		
			
				|  |  | +#endif
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +static void fd_global_init(void);
 | 
	
		
			
				|  |  | +static void fd_global_shutdown(void);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +static void workqueue_enqueue(grpc_exec_ctx *exec_ctx, grpc_closure *closure,
 | 
	
		
			
				|  |  | +                              grpc_error *error);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +static const grpc_closure_scheduler_vtable workqueue_scheduler_vtable = {
 | 
	
		
			
				|  |  | +    workqueue_enqueue, workqueue_enqueue, "workqueue"};
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +/*******************************************************************************
 | 
	
		
			
				|  |  | + * Pollset Declarations
 | 
	
		
			
				|  |  | + */
 | 
	
		
			
				|  |  | +struct grpc_pollset_worker {
 | 
	
		
			
				|  |  | +  /* Thread id of this worker */
 | 
	
		
			
				|  |  | +  pthread_t pt_id;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  /* Used to prevent a worker from getting kicked multiple times */
 | 
	
		
			
				|  |  | +  gpr_atm is_kicked;
 | 
	
		
			
				|  |  | +  struct grpc_pollset_worker *next;
 | 
	
		
			
				|  |  | +  struct grpc_pollset_worker *prev;
 | 
	
		
			
				|  |  | +};
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +struct grpc_pollset {};
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +/*******************************************************************************
 | 
	
		
			
				|  |  | + * Pollset-set Declarations
 | 
	
		
			
				|  |  | + */
 | 
	
		
			
				|  |  | +struct grpc_pollset_set {};
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +/*******************************************************************************
 | 
	
		
			
				|  |  | + * Common helpers
 | 
	
		
			
				|  |  | + */
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +#ifdef GRPC_WORKQUEUE_REFCOUNT_DEBUG
 | 
	
		
			
				|  |  | +static grpc_workqueue *workqueue_ref(grpc_workqueue *workqueue,
 | 
	
		
			
				|  |  | +                                     const char *file, int line,
 | 
	
		
			
				|  |  | +                                     const char *reason) {
 | 
	
		
			
				|  |  | +  if (workqueue != NULL) {
 | 
	
		
			
				|  |  | +    abort();
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +  return workqueue;
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +static void workqueue_unref(grpc_exec_ctx *exec_ctx, grpc_workqueue *workqueue,
 | 
	
		
			
				|  |  | +                            const char *file, int line, const char *reason) {
 | 
	
		
			
				|  |  | +  if (workqueue != NULL) {
 | 
	
		
			
				|  |  | +    abort();
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +#else
 | 
	
		
			
				|  |  | +static grpc_workqueue *workqueue_ref(grpc_workqueue *workqueue) {
 | 
	
		
			
				|  |  | +  if (workqueue != NULL) {
 | 
	
		
			
				|  |  | +    abort();
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +  return workqueue;
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +static void workqueue_unref(grpc_exec_ctx *exec_ctx,
 | 
	
		
			
				|  |  | +                            grpc_workqueue *workqueue) {
 | 
	
		
			
				|  |  | +  if (workqueue != NULL) {
 | 
	
		
			
				|  |  | +    abort();
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +#endif
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +static void workqueue_enqueue(grpc_exec_ctx *exec_ctx, grpc_closure *closure,
 | 
	
		
			
				|  |  | +                              grpc_error *error) {
 | 
	
		
			
				|  |  | +  GPR_TIMER_BEGIN("workqueue.enqueue", 0);
 | 
	
		
			
				|  |  | +  // grpc_workqueue *workqueue = (grpc_workqueue *)closure->scheduler;
 | 
	
		
			
				|  |  | +  abort();
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +static grpc_closure_scheduler *workqueue_scheduler(grpc_workqueue *workqueue) {
 | 
	
		
			
				|  |  | +  abort();
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +/*******************************************************************************
 | 
	
		
			
				|  |  | + * Fd Definitions
 | 
	
		
			
				|  |  | + */
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +/* We need to keep a freelist not because of any concerns of malloc performance
 | 
	
		
			
				|  |  | + * but instead so that implementations with multiple threads in (for example)
 | 
	
		
			
				|  |  | + * epoll_wait deal with the race between pollset removal and incoming poll
 | 
	
		
			
				|  |  | + * notifications.
 | 
	
		
			
				|  |  | + *
 | 
	
		
			
				|  |  | + * The problem is that the poller ultimately holds a reference to this
 | 
	
		
			
				|  |  | + * object, so it is very difficult to know when is safe to free it, at least
 | 
	
		
			
				|  |  | + * without some expensive synchronization.
 | 
	
		
			
				|  |  | + *
 | 
	
		
			
				|  |  | + * If we keep the object freelisted, in the worst case losing this race just
 | 
	
		
			
				|  |  | + * becomes a spurious read notification on a reused fd.
 | 
	
		
			
				|  |  | + */
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +/* The alarm system needs to be able to wakeup 'some poller' sometimes
 | 
	
		
			
				|  |  | + * (specifically when a new alarm needs to be triggered earlier than the next
 | 
	
		
			
				|  |  | + * alarm 'epoch'). This wakeup_fd gives us something to alert on when such a
 | 
	
		
			
				|  |  | + * case occurs. */
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +static grpc_fd *fd_freelist = NULL;
 | 
	
		
			
				|  |  | +static gpr_mu fd_freelist_mu;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +#ifdef GRPC_FD_REF_COUNT_DEBUG
 | 
	
		
			
				|  |  | +#define REF_BY(fd, n, reason) ref_by(fd, n, reason, __FILE__, __LINE__)
 | 
	
		
			
				|  |  | +#define UNREF_BY(fd, n, reason) unref_by(fd, n, reason, __FILE__, __LINE__)
 | 
	
		
			
				|  |  | +static void ref_by(grpc_fd *fd, int n, const char *reason, const char *file,
 | 
	
		
			
				|  |  | +                   int line) {
 | 
	
		
			
				|  |  | +  gpr_log(GPR_DEBUG, "FD %d %p   ref %d %ld -> %ld [%s; %s:%d]", fd->fd,
 | 
	
		
			
				|  |  | +          (void *)fd, n, gpr_atm_no_barrier_load(&fd->refst),
 | 
	
		
			
				|  |  | +          gpr_atm_no_barrier_load(&fd->refst) + n, reason, file, line);
 | 
	
		
			
				|  |  | +#else
 | 
	
		
			
				|  |  | +#define REF_BY(fd, n, reason) ref_by(fd, n)
 | 
	
		
			
				|  |  | +#define UNREF_BY(fd, n, reason) unref_by(fd, n)
 | 
	
		
			
				|  |  | +static void ref_by(grpc_fd *fd, int n) {
 | 
	
		
			
				|  |  | +#endif
 | 
	
		
			
				|  |  | +  GPR_ASSERT(gpr_atm_no_barrier_fetch_add(&fd->refst, n) > 0);
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +#ifdef GRPC_FD_REF_COUNT_DEBUG
 | 
	
		
			
				|  |  | +static void unref_by(grpc_fd *fd, int n, const char *reason, const char *file,
 | 
	
		
			
				|  |  | +                     int line) {
 | 
	
		
			
				|  |  | +  gpr_atm old;
 | 
	
		
			
				|  |  | +  gpr_log(GPR_DEBUG, "FD %d %p unref %d %ld -> %ld [%s; %s:%d]", fd->fd,
 | 
	
		
			
				|  |  | +          (void *)fd, n, gpr_atm_no_barrier_load(&fd->refst),
 | 
	
		
			
				|  |  | +          gpr_atm_no_barrier_load(&fd->refst) - n, reason, file, line);
 | 
	
		
			
				|  |  | +#else
 | 
	
		
			
				|  |  | +static void unref_by(grpc_fd *fd, int n) {
 | 
	
		
			
				|  |  | +  gpr_atm old;
 | 
	
		
			
				|  |  | +#endif
 | 
	
		
			
				|  |  | +  old = gpr_atm_full_fetch_add(&fd->refst, -n);
 | 
	
		
			
				|  |  | +  if (old == n) {
 | 
	
		
			
				|  |  | +    /* Add the fd to the freelist */
 | 
	
		
			
				|  |  | +    gpr_mu_lock(&fd_freelist_mu);
 | 
	
		
			
				|  |  | +    fd->freelist_next = fd_freelist;
 | 
	
		
			
				|  |  | +    fd_freelist = fd;
 | 
	
		
			
				|  |  | +    grpc_iomgr_unregister_object(&fd->iomgr_object);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    grpc_lfev_destroy(&fd->read_closure);
 | 
	
		
			
				|  |  | +    grpc_lfev_destroy(&fd->write_closure);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    gpr_mu_unlock(&fd_freelist_mu);
 | 
	
		
			
				|  |  | +  } else {
 | 
	
		
			
				|  |  | +    GPR_ASSERT(old > n);
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +static void fd_global_init(void) { gpr_mu_init(&fd_freelist_mu); }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +static void fd_global_shutdown(void) {
 | 
	
		
			
				|  |  | +  gpr_mu_lock(&fd_freelist_mu);
 | 
	
		
			
				|  |  | +  gpr_mu_unlock(&fd_freelist_mu);
 | 
	
		
			
				|  |  | +  while (fd_freelist != NULL) {
 | 
	
		
			
				|  |  | +    grpc_fd *fd = fd_freelist;
 | 
	
		
			
				|  |  | +    fd_freelist = fd_freelist->freelist_next;
 | 
	
		
			
				|  |  | +    gpr_mu_destroy(&fd->mu);
 | 
	
		
			
				|  |  | +    gpr_free(fd);
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +  gpr_mu_destroy(&fd_freelist_mu);
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +static grpc_fd *fd_create(int fd, const char *name) {
 | 
	
		
			
				|  |  | +  grpc_fd *new_fd = NULL;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  gpr_mu_lock(&fd_freelist_mu);
 | 
	
		
			
				|  |  | +  if (fd_freelist != NULL) {
 | 
	
		
			
				|  |  | +    new_fd = fd_freelist;
 | 
	
		
			
				|  |  | +    fd_freelist = fd_freelist->freelist_next;
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +  gpr_mu_unlock(&fd_freelist_mu);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  if (new_fd == NULL) {
 | 
	
		
			
				|  |  | +    new_fd = gpr_malloc(sizeof(grpc_fd));
 | 
	
		
			
				|  |  | +    gpr_mu_init(&new_fd->mu);
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  /* Note: It is not really needed to get the new_fd->mu lock here. If this
 | 
	
		
			
				|  |  | +   * is a newly created fd (or an fd we got from the freelist), no one else
 | 
	
		
			
				|  |  | +   * would be holding a lock to it anyway. */
 | 
	
		
			
				|  |  | +  gpr_mu_lock(&new_fd->mu);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  gpr_atm_rel_store(&new_fd->refst, (gpr_atm)1);
 | 
	
		
			
				|  |  | +  new_fd->fd = fd;
 | 
	
		
			
				|  |  | +  new_fd->orphaned = false;
 | 
	
		
			
				|  |  | +  grpc_lfev_init(&new_fd->read_closure);
 | 
	
		
			
				|  |  | +  grpc_lfev_init(&new_fd->write_closure);
 | 
	
		
			
				|  |  | +  gpr_atm_no_barrier_store(&new_fd->read_notifier_pollset, (gpr_atm)NULL);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  new_fd->freelist_next = NULL;
 | 
	
		
			
				|  |  | +  new_fd->on_done_closure = NULL;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  gpr_mu_unlock(&new_fd->mu);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  char *fd_name;
 | 
	
		
			
				|  |  | +  gpr_asprintf(&fd_name, "%s fd=%d", name, fd);
 | 
	
		
			
				|  |  | +  grpc_iomgr_register_object(&new_fd->iomgr_object, fd_name);
 | 
	
		
			
				|  |  | +#ifdef GRPC_FD_REF_COUNT_DEBUG
 | 
	
		
			
				|  |  | +  gpr_log(GPR_DEBUG, "FD %d %p create %s", fd, (void *)new_fd, fd_name);
 | 
	
		
			
				|  |  | +#endif
 | 
	
		
			
				|  |  | +  gpr_free(fd_name);
 | 
	
		
			
				|  |  | +  return new_fd;
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +static int fd_wrapped_fd(grpc_fd *fd) {
 | 
	
		
			
				|  |  | +  int ret_fd = -1;
 | 
	
		
			
				|  |  | +  gpr_mu_lock(&fd->mu);
 | 
	
		
			
				|  |  | +  if (!fd->orphaned) {
 | 
	
		
			
				|  |  | +    ret_fd = fd->fd;
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +  gpr_mu_unlock(&fd->mu);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  return ret_fd;
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +static void fd_orphan(grpc_exec_ctx *exec_ctx, grpc_fd *fd,
 | 
	
		
			
				|  |  | +                      grpc_closure *on_done, int *release_fd,
 | 
	
		
			
				|  |  | +                      const char *reason) {
 | 
	
		
			
				|  |  | +  bool is_fd_closed = false;
 | 
	
		
			
				|  |  | +  grpc_error *error = GRPC_ERROR_NONE;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  gpr_mu_lock(&fd->mu);
 | 
	
		
			
				|  |  | +  fd->on_done_closure = on_done;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  /* If release_fd is not NULL, we should be relinquishing control of the file
 | 
	
		
			
				|  |  | +     descriptor fd->fd (but we still own the grpc_fd structure). */
 | 
	
		
			
				|  |  | +  if (release_fd != NULL) {
 | 
	
		
			
				|  |  | +    *release_fd = fd->fd;
 | 
	
		
			
				|  |  | +  } else {
 | 
	
		
			
				|  |  | +    close(fd->fd);
 | 
	
		
			
				|  |  | +    is_fd_closed = true;
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  fd->orphaned = true;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  /* Remove the active status but keep referenced. We want this grpc_fd struct
 | 
	
		
			
				|  |  | +     to be alive (and not added to freelist) until the end of this function */
 | 
	
		
			
				|  |  | +  REF_BY(fd, 1, reason);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  grpc_closure_sched(exec_ctx, fd->on_done_closure, GRPC_ERROR_REF(error));
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  gpr_mu_unlock(&fd->mu);
 | 
	
		
			
				|  |  | +  UNREF_BY(fd, 2, reason); /* Drop the reference */
 | 
	
		
			
				|  |  | +  GRPC_LOG_IF_ERROR("fd_orphan", GRPC_ERROR_REF(error));
 | 
	
		
			
				|  |  | +  GRPC_ERROR_UNREF(error);
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +static grpc_pollset *fd_get_read_notifier_pollset(grpc_exec_ctx *exec_ctx,
 | 
	
		
			
				|  |  | +                                                  grpc_fd *fd) {
 | 
	
		
			
				|  |  | +  gpr_atm notifier = gpr_atm_acq_load(&fd->read_notifier_pollset);
 | 
	
		
			
				|  |  | +  return (grpc_pollset *)notifier;
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +static bool fd_is_shutdown(grpc_fd *fd) {
 | 
	
		
			
				|  |  | +  return grpc_lfev_is_shutdown(&fd->read_closure);
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +/* Might be called multiple times */
 | 
	
		
			
				|  |  | +static void fd_shutdown(grpc_exec_ctx *exec_ctx, grpc_fd *fd, grpc_error *why) {
 | 
	
		
			
				|  |  | +  if (grpc_lfev_set_shutdown(exec_ctx, &fd->read_closure,
 | 
	
		
			
				|  |  | +                             GRPC_ERROR_REF(why))) {
 | 
	
		
			
				|  |  | +    shutdown(fd->fd, SHUT_RDWR);
 | 
	
		
			
				|  |  | +    grpc_lfev_set_shutdown(exec_ctx, &fd->write_closure, GRPC_ERROR_REF(why));
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +  GRPC_ERROR_UNREF(why);
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +static void fd_notify_on_read(grpc_exec_ctx *exec_ctx, grpc_fd *fd,
 | 
	
		
			
				|  |  | +                              grpc_closure *closure) {
 | 
	
		
			
				|  |  | +  grpc_lfev_notify_on(exec_ctx, &fd->read_closure, closure);
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +static void fd_notify_on_write(grpc_exec_ctx *exec_ctx, grpc_fd *fd,
 | 
	
		
			
				|  |  | +                               grpc_closure *closure) {
 | 
	
		
			
				|  |  | +  grpc_lfev_notify_on(exec_ctx, &fd->write_closure, closure);
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +static grpc_workqueue *fd_get_workqueue(grpc_fd *fd) { abort(); }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +/*******************************************************************************
 | 
	
		
			
				|  |  | + * Pollset Definitions
 | 
	
		
			
				|  |  | + */
 | 
	
		
			
				|  |  | +GPR_TLS_DECL(g_current_thread_pollset);
 | 
	
		
			
				|  |  | +GPR_TLS_DECL(g_current_thread_worker);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +static void poller_kick_init() {}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +/* Global state management */
 | 
	
		
			
				|  |  | +static grpc_error *pollset_global_init(void) {
 | 
	
		
			
				|  |  | +  gpr_tls_init(&g_current_thread_pollset);
 | 
	
		
			
				|  |  | +  gpr_tls_init(&g_current_thread_worker);
 | 
	
		
			
				|  |  | +  poller_kick_init();
 | 
	
		
			
				|  |  | +  return grpc_wakeup_fd_init(&global_wakeup_fd);
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +static void pollset_global_shutdown(void) {
 | 
	
		
			
				|  |  | +  grpc_wakeup_fd_destroy(&global_wakeup_fd);
 | 
	
		
			
				|  |  | +  gpr_tls_destroy(&g_current_thread_pollset);
 | 
	
		
			
				|  |  | +  gpr_tls_destroy(&g_current_thread_worker);
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +/* p->mu must be held before calling this function */
 | 
	
		
			
				|  |  | +static grpc_error *pollset_kick(grpc_pollset *p,
 | 
	
		
			
				|  |  | +                                grpc_pollset_worker *specific_worker) {
 | 
	
		
			
				|  |  | +  abort();
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +static grpc_error *kick_poller(void) { abort(); }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +static void pollset_init(grpc_pollset *pollset, gpr_mu **mu) {}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +/* Convert a timespec to milliseconds:
 | 
	
		
			
				|  |  | +   - Very small or negative poll times are clamped to zero to do a non-blocking
 | 
	
		
			
				|  |  | +     poll (which becomes spin polling)
 | 
	
		
			
				|  |  | +   - Other small values are rounded up to one millisecond
 | 
	
		
			
				|  |  | +   - Longer than a millisecond polls are rounded up to the next nearest
 | 
	
		
			
				|  |  | +     millisecond to avoid spinning
 | 
	
		
			
				|  |  | +   - Infinite timeouts are converted to -1 */
 | 
	
		
			
				|  |  | +static int poll_deadline_to_millis_timeout(gpr_timespec deadline,
 | 
	
		
			
				|  |  | +                                           gpr_timespec now) {
 | 
	
		
			
				|  |  | +  gpr_timespec timeout;
 | 
	
		
			
				|  |  | +  static const int64_t max_spin_polling_us = 10;
 | 
	
		
			
				|  |  | +  if (gpr_time_cmp(deadline, gpr_inf_future(deadline.clock_type)) == 0) {
 | 
	
		
			
				|  |  | +    return -1;
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  if (gpr_time_cmp(deadline, gpr_time_add(now, gpr_time_from_micros(
 | 
	
		
			
				|  |  | +                                                   max_spin_polling_us,
 | 
	
		
			
				|  |  | +                                                   GPR_TIMESPAN))) <= 0) {
 | 
	
		
			
				|  |  | +    return 0;
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +  timeout = gpr_time_sub(deadline, now);
 | 
	
		
			
				|  |  | +  int millis = gpr_time_to_millis(gpr_time_add(
 | 
	
		
			
				|  |  | +      timeout, gpr_time_from_nanos(GPR_NS_PER_MS - 1, GPR_TIMESPAN)));
 | 
	
		
			
				|  |  | +  return millis >= 1 ? millis : 1;
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +static void fd_become_readable(grpc_exec_ctx *exec_ctx, grpc_fd *fd,
 | 
	
		
			
				|  |  | +                               grpc_pollset *notifier) {
 | 
	
		
			
				|  |  | +  grpc_lfev_set_ready(exec_ctx, &fd->read_closure);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  /* Note, it is possible that fd_become_readable might be called twice with
 | 
	
		
			
				|  |  | +     different 'notifier's when an fd becomes readable and it is in two epoll
 | 
	
		
			
				|  |  | +     sets (This can happen briefly during polling island merges). In such cases
 | 
	
		
			
				|  |  | +     it does not really matter which notifer is set as the read_notifier_pollset
 | 
	
		
			
				|  |  | +     (They would both point to the same polling island anyway) */
 | 
	
		
			
				|  |  | +  /* Use release store to match with acquire load in fd_get_read_notifier */
 | 
	
		
			
				|  |  | +  gpr_atm_rel_store(&fd->read_notifier_pollset, (gpr_atm)notifier);
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +static void fd_become_writable(grpc_exec_ctx *exec_ctx, grpc_fd *fd) {
 | 
	
		
			
				|  |  | +  grpc_lfev_set_ready(exec_ctx, &fd->write_closure);
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +static void finish_shutdown_locked(grpc_exec_ctx *exec_ctx,
 | 
	
		
			
				|  |  | +                                   grpc_pollset *pollset) {
 | 
	
		
			
				|  |  | +  abort();
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +/* pollset->po.mu lock must be held by the caller before calling this */
 | 
	
		
			
				|  |  | +static void pollset_shutdown(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset,
 | 
	
		
			
				|  |  | +                             grpc_closure *closure) {}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +/* pollset_shutdown is guaranteed to be called before pollset_destroy. So other
 | 
	
		
			
				|  |  | + * than destroying the mutexes, there is nothing special that needs to be done
 | 
	
		
			
				|  |  | + * here */
 | 
	
		
			
				|  |  | +static void pollset_destroy(grpc_pollset *pollset) {}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +/* pollset->po.mu lock must be held by the caller before calling this.
 | 
	
		
			
				|  |  | +   The function pollset_work() may temporarily release the lock (pollset->po.mu)
 | 
	
		
			
				|  |  | +   during the course of its execution but it will always re-acquire the lock and
 | 
	
		
			
				|  |  | +   ensure that it is held by the time the function returns */
 | 
	
		
			
				|  |  | +static grpc_error *pollset_work(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset,
 | 
	
		
			
				|  |  | +                                grpc_pollset_worker **worker_hdl,
 | 
	
		
			
				|  |  | +                                gpr_timespec now, gpr_timespec deadline) {
 | 
	
		
			
				|  |  | +  abort();
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +static void pollset_add_fd(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset,
 | 
	
		
			
				|  |  | +                           grpc_fd *fd) {
 | 
	
		
			
				|  |  | +  abort();
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +/*******************************************************************************
 | 
	
		
			
				|  |  | + * Pollset-set Definitions
 | 
	
		
			
				|  |  | + */
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +static grpc_pollset_set *pollset_set_create(void) {
 | 
	
		
			
				|  |  | +  grpc_pollset_set *pss = gpr_malloc(sizeof(*pss));
 | 
	
		
			
				|  |  | +  return pss;
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +static void pollset_set_destroy(grpc_exec_ctx *exec_ctx,
 | 
	
		
			
				|  |  | +                                grpc_pollset_set *pss) {
 | 
	
		
			
				|  |  | +  gpr_free(pss);
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +static void pollset_set_add_fd(grpc_exec_ctx *exec_ctx, grpc_pollset_set *pss,
 | 
	
		
			
				|  |  | +                               grpc_fd *fd) {
 | 
	
		
			
				|  |  | +  abort();
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +static void pollset_set_del_fd(grpc_exec_ctx *exec_ctx, grpc_pollset_set *pss,
 | 
	
		
			
				|  |  | +                               grpc_fd *fd) {
 | 
	
		
			
				|  |  | +  abort();
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +static void pollset_set_add_pollset(grpc_exec_ctx *exec_ctx,
 | 
	
		
			
				|  |  | +                                    grpc_pollset_set *pss, grpc_pollset *ps) {
 | 
	
		
			
				|  |  | +  abort();
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +static void pollset_set_del_pollset(grpc_exec_ctx *exec_ctx,
 | 
	
		
			
				|  |  | +                                    grpc_pollset_set *pss, grpc_pollset *ps) {
 | 
	
		
			
				|  |  | +  abort();
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +static void pollset_set_add_pollset_set(grpc_exec_ctx *exec_ctx,
 | 
	
		
			
				|  |  | +                                        grpc_pollset_set *bag,
 | 
	
		
			
				|  |  | +                                        grpc_pollset_set *item) {
 | 
	
		
			
				|  |  | +  abort();
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +static void pollset_set_del_pollset_set(grpc_exec_ctx *exec_ctx,
 | 
	
		
			
				|  |  | +                                        grpc_pollset_set *bag,
 | 
	
		
			
				|  |  | +                                        grpc_pollset_set *item) {
 | 
	
		
			
				|  |  | +  abort();
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +/*******************************************************************************
 | 
	
		
			
				|  |  | + * Event engine binding
 | 
	
		
			
				|  |  | + */
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +static void shutdown_engine(void) {
 | 
	
		
			
				|  |  | +  fd_global_shutdown();
 | 
	
		
			
				|  |  | +  pollset_global_shutdown();
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +static const grpc_event_engine_vtable vtable = {
 | 
	
		
			
				|  |  | +    .pollset_size = sizeof(grpc_pollset),
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    .fd_create = fd_create,
 | 
	
		
			
				|  |  | +    .fd_wrapped_fd = fd_wrapped_fd,
 | 
	
		
			
				|  |  | +    .fd_orphan = fd_orphan,
 | 
	
		
			
				|  |  | +    .fd_shutdown = fd_shutdown,
 | 
	
		
			
				|  |  | +    .fd_is_shutdown = fd_is_shutdown,
 | 
	
		
			
				|  |  | +    .fd_notify_on_read = fd_notify_on_read,
 | 
	
		
			
				|  |  | +    .fd_notify_on_write = fd_notify_on_write,
 | 
	
		
			
				|  |  | +    .fd_get_read_notifier_pollset = fd_get_read_notifier_pollset,
 | 
	
		
			
				|  |  | +    .fd_get_workqueue = fd_get_workqueue,
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    .pollset_init = pollset_init,
 | 
	
		
			
				|  |  | +    .pollset_shutdown = pollset_shutdown,
 | 
	
		
			
				|  |  | +    .pollset_destroy = pollset_destroy,
 | 
	
		
			
				|  |  | +    .pollset_work = pollset_work,
 | 
	
		
			
				|  |  | +    .pollset_kick = pollset_kick,
 | 
	
		
			
				|  |  | +    .pollset_add_fd = pollset_add_fd,
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    .pollset_set_create = pollset_set_create,
 | 
	
		
			
				|  |  | +    .pollset_set_destroy = pollset_set_destroy,
 | 
	
		
			
				|  |  | +    .pollset_set_add_pollset = pollset_set_add_pollset,
 | 
	
		
			
				|  |  | +    .pollset_set_del_pollset = pollset_set_del_pollset,
 | 
	
		
			
				|  |  | +    .pollset_set_add_pollset_set = pollset_set_add_pollset_set,
 | 
	
		
			
				|  |  | +    .pollset_set_del_pollset_set = pollset_set_del_pollset_set,
 | 
	
		
			
				|  |  | +    .pollset_set_add_fd = pollset_set_add_fd,
 | 
	
		
			
				|  |  | +    .pollset_set_del_fd = pollset_set_del_fd,
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    .kick_poller = kick_poller,
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    .workqueue_ref = workqueue_ref,
 | 
	
		
			
				|  |  | +    .workqueue_unref = workqueue_unref,
 | 
	
		
			
				|  |  | +    .workqueue_scheduler = workqueue_scheduler,
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    .shutdown_engine = shutdown_engine,
 | 
	
		
			
				|  |  | +};
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +/* It is possible that GLIBC has epoll but the underlying kernel doesn't.
 | 
	
		
			
				|  |  | + * Create a dummy epoll_fd to make sure epoll support is available */
 | 
	
		
			
				|  |  | +static bool is_epollex_available() {
 | 
	
		
			
				|  |  | +  int fd = epoll_create1(EPOLL_CLOEXEC);
 | 
	
		
			
				|  |  | +  if (fd < 0) {
 | 
	
		
			
				|  |  | +    gpr_log(
 | 
	
		
			
				|  |  | +        GPR_ERROR,
 | 
	
		
			
				|  |  | +        "epoll_create1 failed with error: %d. Not using epoll polling engine",
 | 
	
		
			
				|  |  | +        fd);
 | 
	
		
			
				|  |  | +    return false;
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +  close(fd);
 | 
	
		
			
				|  |  | +  return true;
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +const grpc_event_engine_vtable *grpc_init_epollex_linux(void) {
 | 
	
		
			
				|  |  | +  if (!grpc_has_wakeup_fd()) {
 | 
	
		
			
				|  |  | +    return NULL;
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  if (!is_epollex_available()) {
 | 
	
		
			
				|  |  | +    return NULL;
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  fd_global_init();
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  if (!GRPC_LOG_IF_ERROR("pollset_global_init", pollset_global_init())) {
 | 
	
		
			
				|  |  | +    return NULL;
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  return &vtable;
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +#else /* defined(GRPC_LINUX_EPOLL) */
 | 
	
		
			
				|  |  | +#if defined(GRPC_POSIX_SOCKET)
 | 
	
		
			
				|  |  | +#include "src/core/lib/iomgr/ev_posix.h"
 | 
	
		
			
				|  |  | +/* If GRPC_LINUX_EPOLL is not defined, it means epoll is not available. Return
 | 
	
		
			
				|  |  | + * NULL */
 | 
	
		
			
				|  |  | +const grpc_event_engine_vtable *grpc_init_epoll_linux(void) { return NULL; }
 | 
	
		
			
				|  |  | +#endif /* defined(GRPC_POSIX_SOCKET) */
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +#endif /* !defined(GRPC_LINUX_EPOLL) */
 |