gRPC(C Base) polling engine is built with 'epollex' despite being under linux kernel version 4.5

Viewed 486

I have a question about gRPC Core(C Base).

I have been implementing servers with gRPC(C++) async API in CentOS7. I have been analyzing the gRPC internal code(tag: v1.35.0) for my curiosity. I have recently read a document about poller engine. This document explains as follows.

  • epollex (default but requires kernel version >= 4.5),
  • epoll1 (If epollex is not available and glibc version >= 2.9)
  • poll (If kernel does not have epoll support)

However, as far as I have confirmed, this is not true.

I built gRPC in linux kernel version 3.10, then I thought this gRPC poller engine have to be built based on epoll1, but this gRPC poller engine was built into epollex.

I checked gRPC code in detail to find the reason. In the code, the kernel version has not been checked by the macro condition. (only check GRPC_LINUX_EPOLL_CREATE1, then, it is true in my system because the macro variable is satisfied with glibc version above 2.8)

Also, I checked g_factories and GPR_GLOBAL_CONFIG_DEFINE_STRING.

static event_engine_factory g_factories[] = {
    {ENGINE_HEAD_CUSTOM, nullptr},        {ENGINE_HEAD_CUSTOM, nullptr},
    {ENGINE_HEAD_CUSTOM, nullptr},        {ENGINE_HEAD_CUSTOM, nullptr},
    {"epollex", grpc_init_epollex_linux}, {"epoll1", grpc_init_epoll1_linux},
    {"poll", grpc_init_poll_posix},       {"none", init_non_polling},
    {ENGINE_TAIL_CUSTOM, nullptr},        {ENGINE_TAIL_CUSTOM, nullptr},
    {ENGINE_TAIL_CUSTOM, nullptr},        {ENGINE_TAIL_CUSTOM, nullptr},
};

static bool is(const char* want, const char* have) {
  return 0 == strcmp(want, "all") || 0 == strcmp(want, have);
}

static void try_engine(const char* engine) {
  for (size_t i = 0; i < GPR_ARRAY_SIZE(g_factories); i++) {
    if (g_factories[i].factory != nullptr && is(engine, g_factories[i].name)) {
      if ((g_event_engine = g_factories[i].factory(
               0 == strcmp(engine, g_factories[i].name)))) {
        g_poll_strategy_name = g_factories[i].name;
        gpr_log(GPR_DEBUG, "Using polling engine: %s", g_factories[i].name);
        return;
      }
    }
  }
}

GPR_GLOBAL_CONFIG_DEFINE_STRING(
    grpc_poll_strategy, "all",
    "Declares which polling engines to try when starting gRPC. "
    "This is a comma-separated list of engines, which are tried in priority "
    "order first -> last.")

epollex is used in my system on the above conditions

epollex uses EPOLLEXCLUSIVE which can avoid thundering herd problems.

When a wakeup event occurs and multiple epoll file descriptors are attached to the same target file using EPOLLEXCLUSIVE, one or more of the epoll file descriptors will receive an event with epoll_wait(2).
https://man7.org/linux/man-pages/man2/epoll_ctl.2.html

EPOLLEXCLUSIVE is supported by the 4.5 or higher kernel version. Therefore, epollex cannot avoid the above problem in my system. Then, I think it could be inefficient to run into epollex that has a mechanism.

Thus, I would like to ask two questions.

  1. Is this the intended behavior?

epollex (default but requires kernel version >= 4.5),

  1. Then, Is this wrong information?
2 Answers

TL;DR

  • RHEL7/CentOS7's kernel 3.10.x may have EPOLLEXCLUSIVE.
  • epollex engine does NOT exist in gRPC source anymore.

Details

CentOS, or RHEL seems to have EPOLLEXCLUSIVE backported into its kernel 3.10.x, which is available in release >= 7.3.

gRPC has kernel feature availability check code which actually tries epoll system call with EPOLLEXCLUSIVE flag on. It does not depends on actual version of linux kernel.

https://github.com/grpc/grpc/blob/77e2827f3d70650182474624b4de22e053ac01f6/src/core/lib/iomgr/is_epollexclusive_available.cc#L63-L95

/* This polling engine is only relevant on linux kernels supporting epoll() */
bool grpc_is_epollexclusive_available(void) {

  ...

  struct epoll_event ev;
  /* choose events that should cause an error on
     EPOLLEXCLUSIVE enabled kernels - specifically the combination of
     EPOLLONESHOT and EPOLLEXCLUSIVE */
  ev.events =
      static_cast<uint32_t>(EPOLLET | EPOLLIN | EPOLLEXCLUSIVE | EPOLLONESHOT);
  ev.data.ptr = nullptr;
  if (epoll_ctl(fd, EPOLL_CTL_ADD, evfd, &ev) != 0) {
    if (errno != EINVAL) {
      if (!logged_why_not) {
        gpr_log(
            GPR_ERROR,
            "epoll_ctl with EPOLLEXCLUSIVE | EPOLLONESHOT failed with error: "
            "%d. Not using epollex polling engine.",
            errno);
        logged_why_not = true;
      }
      close(fd);
      close(evfd);
      return false;
    }

  ...


BTW epollex polling engine is now removed from gRPC repository for some unknown reason.

I believe some of the document is out-of-date, such as ev_epoll1_posix.cc and other files are moved. But I don't think the description about choosing engine is wrong.


I used the same environment(kernel v3.10.107-1) but I found the epoll1 was used. I'm doubting how you got the conclusion.

Check gRPC version(master branch):

> grep "Version()" ./src/cpp/common/version_cc.cc
std::string Version() { return "1.40.0-dev"; }

Check libc version:

> ldd --version
ldd (GNU libc) 2.17
Copyright (C) 2012 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
Written by Roland McGrath and Ulrich Drepper.

Check kernel version:

> uname -r
3.10.107-1-tlinux2_kvm_guest-0050

Check bazel version:

> bazel version
INFO: Running bazel wrapper (see //tools/bazel for details), bazel version 3.7.1 will be used instead of system-wide bazel installation.
Build label: 3.7.1
Build target: bazel-out/k8-opt/bin/src/main/java/com/google/devtools/build/lib/bazel/BazelServer_deploy.jar
Build time: Tue Nov 24 17:38:30 2020 (1606239510)
Build timestamp: 1606239510
Build timestamp as int: 1606239510

Check the engine(Using polling engine: epoll1):

> GRPC_VERBOSITY=DEBUG CC=`which gcc` bazel run //examples/cpp/helloworld:greeter_server 2>&1 | grep polling 
D0810 16:27:41.592830634    2111 is_epollexclusive_available.cc:86] epoll_ctl with EPOLLEXCLUSIVE | EPOLLONESHOT succeeded. This is evidence of no EPOLLEXCLUSIVE support. Not using epollex polling engine.
D0810 16:27:41.593064805    2111 ev_posix.cc:173]            Using polling engine: epoll1
Related