Revert "Remove unused eventmanager_libuv code (#25796)" (#25807)

This caused problems internally. The interface deletion needs a cherrypick, but I will do an overnight test first, before I reintroduce. CC @nicolasnoble
diff --git a/BUILD b/BUILD
index a322d62..597b191 100644
--- a/BUILD
+++ b/BUILD
@@ -1060,6 +1060,7 @@
     public_hdrs = GRPC_PUBLIC_HDRS,
     deps = [
         "dual_ref_counted",
+        "eventmanager_libuv",
         "gpr_base",
         "grpc_codegen",
         "grpc_trace",
@@ -3604,3 +3605,25 @@
     ],
     visibility = ["//visibility:public"],
 )
+
+# Base classes of EventManagerInterface
+grpc_cc_library(
+    name = "eventmanager_interface",
+    hdrs = [
+        "src/core/lib/iomgr/poller/eventmanager_interface.h",
+    ],
+)
+
+# Libuv-based EventManager implementation
+grpc_cc_library(
+    name = "eventmanager_libuv",
+    srcs = [
+        "src/core/lib/iomgr/poller/eventmanager_libuv.cc",
+    ],
+    hdrs = [
+        "src/core/lib/iomgr/poller/eventmanager_libuv.h",
+    ],
+    deps = [
+        "gpr_base",
+    ],
+)
diff --git a/BUILD.gn b/BUILD.gn
index d49cb35..1d8f340 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -934,6 +934,8 @@
         "src/core/lib/iomgr/nameser.h",
         "src/core/lib/iomgr/parse_address.cc",
         "src/core/lib/iomgr/parse_address.h",
+        "src/core/lib/iomgr/poller/eventmanager_libuv.cc",
+        "src/core/lib/iomgr/poller/eventmanager_libuv.h",
         "src/core/lib/iomgr/polling_entity.cc",
         "src/core/lib/iomgr/polling_entity.h",
         "src/core/lib/iomgr/pollset.cc",
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 3c805aa..f463115 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -841,6 +841,7 @@
   add_dependencies(buildtests_cxx end2end_test)
   add_dependencies(buildtests_cxx error_details_test)
   add_dependencies(buildtests_cxx evaluate_args_test)
+  add_dependencies(buildtests_cxx eventmanager_libuv_test)
   if(_gRPC_PLATFORM_LINUX OR _gRPC_PLATFORM_MAC OR _gRPC_PLATFORM_POSIX)
     add_dependencies(buildtests_cxx examine_stack_test)
   endif()
@@ -1836,6 +1837,7 @@
   src/core/lib/iomgr/load_file.cc
   src/core/lib/iomgr/lockfree_event.cc
   src/core/lib/iomgr/parse_address.cc
+  src/core/lib/iomgr/poller/eventmanager_libuv.cc
   src/core/lib/iomgr/polling_entity.cc
   src/core/lib/iomgr/pollset.cc
   src/core/lib/iomgr/pollset_custom.cc
@@ -2462,6 +2464,7 @@
   src/core/lib/iomgr/load_file.cc
   src/core/lib/iomgr/lockfree_event.cc
   src/core/lib/iomgr/parse_address.cc
+  src/core/lib/iomgr/poller/eventmanager_libuv.cc
   src/core/lib/iomgr/polling_entity.cc
   src/core/lib/iomgr/pollset.cc
   src/core/lib/iomgr/pollset_custom.cc
@@ -10282,6 +10285,41 @@
 
 endif()
 if(gRPC_BUILD_TESTS)
+
+add_executable(eventmanager_libuv_test
+  test/core/iomgr/poller/eventmanager_libuv_test.cc
+  third_party/googletest/googletest/src/gtest-all.cc
+  third_party/googletest/googlemock/src/gmock-all.cc
+)
+
+target_include_directories(eventmanager_libuv_test
+  PRIVATE
+    ${CMAKE_CURRENT_SOURCE_DIR}
+    ${CMAKE_CURRENT_SOURCE_DIR}/include
+    ${_gRPC_ADDRESS_SORTING_INCLUDE_DIR}
+    ${_gRPC_RE2_INCLUDE_DIR}
+    ${_gRPC_SSL_INCLUDE_DIR}
+    ${_gRPC_UPB_GENERATED_DIR}
+    ${_gRPC_UPB_GRPC_GENERATED_DIR}
+    ${_gRPC_UPB_INCLUDE_DIR}
+    ${_gRPC_XXHASH_INCLUDE_DIR}
+    ${_gRPC_ZLIB_INCLUDE_DIR}
+    third_party/googletest/googletest/include
+    third_party/googletest/googletest
+    third_party/googletest/googlemock/include
+    third_party/googletest/googlemock
+    ${_gRPC_PROTO_GENS_DIR}
+)
+
+target_link_libraries(eventmanager_libuv_test
+  ${_gRPC_PROTOBUF_LIBRARIES}
+  ${_gRPC_ALLTARGETS_LIBRARIES}
+  grpc_test_util
+)
+
+
+endif()
+if(gRPC_BUILD_TESTS)
 if(_gRPC_PLATFORM_LINUX OR _gRPC_PLATFORM_MAC OR _gRPC_PLATFORM_POSIX)
 
   add_executable(examine_stack_test
diff --git a/Makefile b/Makefile
index cb798de..8e7dd5f 100644
--- a/Makefile
+++ b/Makefile
@@ -1408,6 +1408,7 @@
     src/core/lib/iomgr/load_file.cc \
     src/core/lib/iomgr/lockfree_event.cc \
     src/core/lib/iomgr/parse_address.cc \
+    src/core/lib/iomgr/poller/eventmanager_libuv.cc \
     src/core/lib/iomgr/polling_entity.cc \
     src/core/lib/iomgr/pollset.cc \
     src/core/lib/iomgr/pollset_custom.cc \
@@ -1882,6 +1883,7 @@
     src/core/lib/iomgr/load_file.cc \
     src/core/lib/iomgr/lockfree_event.cc \
     src/core/lib/iomgr/parse_address.cc \
+    src/core/lib/iomgr/poller/eventmanager_libuv.cc \
     src/core/lib/iomgr/polling_entity.cc \
     src/core/lib/iomgr/pollset.cc \
     src/core/lib/iomgr/pollset_custom.cc \
diff --git a/build_autogenerated.yaml b/build_autogenerated.yaml
index 60ac944..f1dfdac 100644
--- a/build_autogenerated.yaml
+++ b/build_autogenerated.yaml
@@ -724,6 +724,7 @@
   - src/core/lib/iomgr/lockfree_event.h
   - src/core/lib/iomgr/nameser.h
   - src/core/lib/iomgr/parse_address.h
+  - src/core/lib/iomgr/poller/eventmanager_libuv.h
   - src/core/lib/iomgr/polling_entity.h
   - src/core/lib/iomgr/pollset.h
   - src/core/lib/iomgr/pollset_custom.h
@@ -1249,6 +1250,7 @@
   - src/core/lib/iomgr/load_file.cc
   - src/core/lib/iomgr/lockfree_event.cc
   - src/core/lib/iomgr/parse_address.cc
+  - src/core/lib/iomgr/poller/eventmanager_libuv.cc
   - src/core/lib/iomgr/polling_entity.cc
   - src/core/lib/iomgr/pollset.cc
   - src/core/lib/iomgr/pollset_custom.cc
@@ -1736,6 +1738,7 @@
   - src/core/lib/iomgr/lockfree_event.h
   - src/core/lib/iomgr/nameser.h
   - src/core/lib/iomgr/parse_address.h
+  - src/core/lib/iomgr/poller/eventmanager_libuv.h
   - src/core/lib/iomgr/polling_entity.h
   - src/core/lib/iomgr/pollset.h
   - src/core/lib/iomgr/pollset_custom.h
@@ -1996,6 +1999,7 @@
   - src/core/lib/iomgr/load_file.cc
   - src/core/lib/iomgr/lockfree_event.cc
   - src/core/lib/iomgr/parse_address.cc
+  - src/core/lib/iomgr/poller/eventmanager_libuv.cc
   - src/core/lib/iomgr/polling_entity.cc
   - src/core/lib/iomgr/pollset.cc
   - src/core/lib/iomgr/pollset_custom.cc
@@ -5033,6 +5037,16 @@
   - test/core/security/evaluate_args_test.cc
   deps:
   - grpc_test_util
+- name: eventmanager_libuv_test
+  gtest: true
+  build: test
+  language: c++
+  headers: []
+  src:
+  - test/core/iomgr/poller/eventmanager_libuv_test.cc
+  deps:
+  - grpc_test_util
+  uses_polling: false
 - name: examine_stack_test
   gtest: true
   build: test
diff --git a/config.m4 b/config.m4
index f7c32f4..1579483 100644
--- a/config.m4
+++ b/config.m4
@@ -459,6 +459,7 @@
     src/core/lib/iomgr/load_file.cc \
     src/core/lib/iomgr/lockfree_event.cc \
     src/core/lib/iomgr/parse_address.cc \
+    src/core/lib/iomgr/poller/eventmanager_libuv.cc \
     src/core/lib/iomgr/polling_entity.cc \
     src/core/lib/iomgr/pollset.cc \
     src/core/lib/iomgr/pollset_custom.cc \
@@ -1157,6 +1158,7 @@
   PHP_ADD_BUILD_DIR($ext_builddir/src/core/lib/http)
   PHP_ADD_BUILD_DIR($ext_builddir/src/core/lib/iomgr)
   PHP_ADD_BUILD_DIR($ext_builddir/src/core/lib/iomgr/executor)
+  PHP_ADD_BUILD_DIR($ext_builddir/src/core/lib/iomgr/poller)
   PHP_ADD_BUILD_DIR($ext_builddir/src/core/lib/json)
   PHP_ADD_BUILD_DIR($ext_builddir/src/core/lib/matchers)
   PHP_ADD_BUILD_DIR($ext_builddir/src/core/lib/profiling)
diff --git a/config.w32 b/config.w32
index a47d826..f2745a6 100644
--- a/config.w32
+++ b/config.w32
@@ -425,6 +425,7 @@
     "src\\core\\lib\\iomgr\\load_file.cc " +
     "src\\core\\lib\\iomgr\\lockfree_event.cc " +
     "src\\core\\lib\\iomgr\\parse_address.cc " +
+    "src\\core\\lib\\iomgr\\poller\\eventmanager_libuv.cc " +
     "src\\core\\lib\\iomgr\\polling_entity.cc " +
     "src\\core\\lib\\iomgr\\pollset.cc " +
     "src\\core\\lib\\iomgr\\pollset_custom.cc " +
@@ -1257,6 +1258,7 @@
   FSO.CreateFolder(base_dir+"\\ext\\grpc\\src\\core\\lib\\http");
   FSO.CreateFolder(base_dir+"\\ext\\grpc\\src\\core\\lib\\iomgr");
   FSO.CreateFolder(base_dir+"\\ext\\grpc\\src\\core\\lib\\iomgr\\executor");
+  FSO.CreateFolder(base_dir+"\\ext\\grpc\\src\\core\\lib\\iomgr\\poller");
   FSO.CreateFolder(base_dir+"\\ext\\grpc\\src\\core\\lib\\json");
   FSO.CreateFolder(base_dir+"\\ext\\grpc\\src\\core\\lib\\matchers");
   FSO.CreateFolder(base_dir+"\\ext\\grpc\\src\\core\\lib\\profiling");
diff --git a/gRPC-C++.podspec b/gRPC-C++.podspec
index dd3b928..fd6e4e9 100644
--- a/gRPC-C++.podspec
+++ b/gRPC-C++.podspec
@@ -583,6 +583,7 @@
                       'src/core/lib/iomgr/lockfree_event.h',
                       'src/core/lib/iomgr/nameser.h',
                       'src/core/lib/iomgr/parse_address.h',
+                      'src/core/lib/iomgr/poller/eventmanager_libuv.h',
                       'src/core/lib/iomgr/polling_entity.h',
                       'src/core/lib/iomgr/pollset.h',
                       'src/core/lib/iomgr/pollset_custom.h',
@@ -1221,6 +1222,7 @@
                               'src/core/lib/iomgr/lockfree_event.h',
                               'src/core/lib/iomgr/nameser.h',
                               'src/core/lib/iomgr/parse_address.h',
+                              'src/core/lib/iomgr/poller/eventmanager_libuv.h',
                               'src/core/lib/iomgr/polling_entity.h',
                               'src/core/lib/iomgr/pollset.h',
                               'src/core/lib/iomgr/pollset_custom.h',
diff --git a/gRPC-Core.podspec b/gRPC-Core.podspec
index 01ae73a..093876f 100644
--- a/gRPC-Core.podspec
+++ b/gRPC-Core.podspec
@@ -990,6 +990,8 @@
                       'src/core/lib/iomgr/nameser.h',
                       'src/core/lib/iomgr/parse_address.cc',
                       'src/core/lib/iomgr/parse_address.h',
+                      'src/core/lib/iomgr/poller/eventmanager_libuv.cc',
+                      'src/core/lib/iomgr/poller/eventmanager_libuv.h',
                       'src/core/lib/iomgr/polling_entity.cc',
                       'src/core/lib/iomgr/polling_entity.h',
                       'src/core/lib/iomgr/pollset.cc',
@@ -1778,6 +1780,7 @@
                               'src/core/lib/iomgr/lockfree_event.h',
                               'src/core/lib/iomgr/nameser.h',
                               'src/core/lib/iomgr/parse_address.h',
+                              'src/core/lib/iomgr/poller/eventmanager_libuv.h',
                               'src/core/lib/iomgr/polling_entity.h',
                               'src/core/lib/iomgr/pollset.h',
                               'src/core/lib/iomgr/pollset_custom.h',
diff --git a/grpc.gemspec b/grpc.gemspec
index 77ccf46..51895f7 100644
--- a/grpc.gemspec
+++ b/grpc.gemspec
@@ -906,6 +906,8 @@
   s.files += %w( src/core/lib/iomgr/nameser.h )
   s.files += %w( src/core/lib/iomgr/parse_address.cc )
   s.files += %w( src/core/lib/iomgr/parse_address.h )
+  s.files += %w( src/core/lib/iomgr/poller/eventmanager_libuv.cc )
+  s.files += %w( src/core/lib/iomgr/poller/eventmanager_libuv.h )
   s.files += %w( src/core/lib/iomgr/polling_entity.cc )
   s.files += %w( src/core/lib/iomgr/polling_entity.h )
   s.files += %w( src/core/lib/iomgr/pollset.cc )
diff --git a/grpc.gyp b/grpc.gyp
index d8736e6..479bf55 100644
--- a/grpc.gyp
+++ b/grpc.gyp
@@ -818,6 +818,7 @@
         'src/core/lib/iomgr/load_file.cc',
         'src/core/lib/iomgr/lockfree_event.cc',
         'src/core/lib/iomgr/parse_address.cc',
+        'src/core/lib/iomgr/poller/eventmanager_libuv.cc',
         'src/core/lib/iomgr/polling_entity.cc',
         'src/core/lib/iomgr/pollset.cc',
         'src/core/lib/iomgr/pollset_custom.cc',
@@ -1270,6 +1271,7 @@
         'src/core/lib/iomgr/load_file.cc',
         'src/core/lib/iomgr/lockfree_event.cc',
         'src/core/lib/iomgr/parse_address.cc',
+        'src/core/lib/iomgr/poller/eventmanager_libuv.cc',
         'src/core/lib/iomgr/polling_entity.cc',
         'src/core/lib/iomgr/pollset.cc',
         'src/core/lib/iomgr/pollset_custom.cc',
diff --git a/package.xml b/package.xml
index 12b038b..40a7f31 100644
--- a/package.xml
+++ b/package.xml
@@ -886,6 +886,8 @@
     <file baseinstalldir="/" name="src/core/lib/iomgr/nameser.h" role="src" />
     <file baseinstalldir="/" name="src/core/lib/iomgr/parse_address.cc" role="src" />
     <file baseinstalldir="/" name="src/core/lib/iomgr/parse_address.h" role="src" />
+    <file baseinstalldir="/" name="src/core/lib/iomgr/poller/eventmanager_libuv.cc" role="src" />
+    <file baseinstalldir="/" name="src/core/lib/iomgr/poller/eventmanager_libuv.h" role="src" />
     <file baseinstalldir="/" name="src/core/lib/iomgr/polling_entity.cc" role="src" />
     <file baseinstalldir="/" name="src/core/lib/iomgr/polling_entity.h" role="src" />
     <file baseinstalldir="/" name="src/core/lib/iomgr/pollset.cc" role="src" />
diff --git a/src/core/lib/iomgr/poller/eventmanager_interface.h b/src/core/lib/iomgr/poller/eventmanager_interface.h
new file mode 100644
index 0000000..b4fbaeb
--- /dev/null
+++ b/src/core/lib/iomgr/poller/eventmanager_interface.h
@@ -0,0 +1,35 @@
+/*
+ *
+ * Copyright 2019 gRPC authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_IOMGR_POLLER_EVENTMANAGER_INTERFACE_H
+#define GRPC_CORE_LIB_IOMGR_POLLER_EVENTMANAGER_INTERFACE_H
+
+namespace grpc {
+namespace experimental {
+
+class BaseEventManagerInterface {
+ public:
+  virtual ~BaseEventManagerInterface() {}
+};
+
+class EpollEventManagerInterface : public BaseEventManagerInterface {};
+
+}  // namespace experimental
+}  // namespace grpc
+
+#endif /* GRPC_CORE_LIB_IOMGR_POLLER_EVENTMANAGER_INTERFACE_H */
diff --git a/src/core/lib/iomgr/poller/eventmanager_libuv.cc b/src/core/lib/iomgr/poller/eventmanager_libuv.cc
new file mode 100644
index 0000000..c34430a
--- /dev/null
+++ b/src/core/lib/iomgr/poller/eventmanager_libuv.cc
@@ -0,0 +1,88 @@
+/*
+ *
+ * Copyright 2019 gRPC authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <grpc/support/port_platform.h>
+
+#include "src/core/lib/iomgr/poller/eventmanager_libuv.h"
+
+#include <grpc/support/time.h>
+
+grpc::experimental::LibuvEventManager::Options::Options() : num_workers_(-1) {}
+grpc::experimental::LibuvEventManager::Options::Options(int num_workers)
+    : num_workers_(num_workers) {}
+
+grpc::experimental::LibuvEventManager::LibuvEventManager(const Options& options)
+    : options_(options) {
+  int num_workers = options_.num_workers();
+  // Number of workers can't be 0 if we do not accept thread donation.
+  // TODO(guantaol): replaces the hard-coded number with a flag.
+  if (num_workers <= 0) num_workers = 32;
+
+  for (int i = 0; i < num_workers; i++) {
+    workers_.emplace_back(
+        options_.thread_name_prefix().c_str(),
+        [](void* em) { static_cast<LibuvEventManager*>(em)->RunWorkerLoop(); },
+        this);
+    workers_.back().Start();
+  }
+}
+
+grpc::experimental::LibuvEventManager::~LibuvEventManager() {
+  Shutdown();
+  for (auto& th : workers_) {
+    th.Join();
+  }
+}
+
+void grpc::experimental::LibuvEventManager::RunWorkerLoop() {
+  while (true) {
+    // TODO(guantaol): extend the worker loop with real work.
+    if (ShouldStop()) return;
+    gpr_sleep_until(gpr_time_add(gpr_now(GPR_CLOCK_MONOTONIC),
+                                 gpr_time_from_micros(10, GPR_TIMESPAN)));
+  }
+}
+
+bool grpc::experimental::LibuvEventManager::ShouldStop() {
+  return should_stop_.Load(grpc_core::MemoryOrder::ACQUIRE) != 0;
+}
+
+void grpc::experimental::LibuvEventManager::Shutdown() {
+  if (should_stop_.Load(grpc_core::MemoryOrder::ACQUIRE)) {
+    return;  // Already shut down.
+  }
+
+  {
+    grpc_core::MutexLock lock(&shutdown_mu_);
+    while (shutdown_refcount_.Load(grpc_core::MemoryOrder::ACQUIRE) > 0) {
+      shutdown_cv_.Wait(&shutdown_mu_);
+    }
+  }
+  should_stop_.Store(true, grpc_core::MemoryOrder::RELEASE);
+}
+
+void grpc::experimental::LibuvEventManager::ShutdownRef() {
+  shutdown_refcount_.FetchAdd(1, grpc_core::MemoryOrder::RELAXED);
+}
+
+void grpc::experimental::LibuvEventManager::ShutdownUnref() {
+  if (shutdown_refcount_.FetchSub(1, grpc_core::MemoryOrder::ACQ_REL) == 1) {
+    grpc_core::MutexLock lock(&shutdown_mu_);
+    shutdown_cv_.Signal();
+  }
+}
diff --git a/src/core/lib/iomgr/poller/eventmanager_libuv.h b/src/core/lib/iomgr/poller/eventmanager_libuv.h
new file mode 100644
index 0000000..0bd0ecc
--- /dev/null
+++ b/src/core/lib/iomgr/poller/eventmanager_libuv.h
@@ -0,0 +1,88 @@
+/*
+ *
+ * Copyright 2019 gRPC authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_IOMGR_POLLER_EVENTMANAGER_LIBUV_H
+#define GRPC_CORE_LIB_IOMGR_POLLER_EVENTMANAGER_LIBUV_H
+
+#include <grpc/support/port_platform.h>
+
+#include <string>
+#include <vector>
+
+#include "src/core/lib/gprpp/atomic.h"
+#include "src/core/lib/gprpp/sync.h"
+#include "src/core/lib/gprpp/thd.h"
+
+namespace grpc {
+namespace experimental {
+
+class LibuvEventManager {
+ public:
+  class Options {
+   public:
+    Options();
+    explicit Options(int num_workers);
+
+    int num_workers() const { return num_workers_; }
+    void set_num_workers(int num) { num_workers_ = num; }
+
+    const std::string& thread_name_prefix() const {
+      return thread_name_prefix_;
+    }
+    void set_thread_name_prefix(const std::string& name) {
+      thread_name_prefix_ = name;
+    }
+
+   private:
+    // Number of worker threads to create at startup. If less than 0, uses the
+    // default value of 32.
+    int num_workers_;
+    // Name prefix used for worker.
+    std::string thread_name_prefix_;
+  };
+
+  explicit LibuvEventManager(const Options& options);
+  virtual ~LibuvEventManager();
+
+  void Shutdown();
+  void ShutdownRef();
+  void ShutdownUnref();
+
+ private:
+  // Function run by the worker threads.
+  void RunWorkerLoop();
+
+  // Whether the EventManager has been shut down.
+  bool ShouldStop();
+
+  const Options options_;
+  // Whether the EventManager workers should be stopped.
+  grpc_core::Atomic<bool> should_stop_{false};
+  // A refcount preventing the EventManager from shutdown.
+  grpc_core::Atomic<int> shutdown_refcount_{0};
+  // Worker threads of the EventManager.
+  std::vector<grpc_core::Thread> workers_;
+  // Mutex and condition variable used for shutdown.
+  grpc_core::Mutex shutdown_mu_;
+  grpc_core::CondVar shutdown_cv_;
+};
+
+}  // namespace experimental
+}  // namespace grpc
+
+#endif /* GRPC_CORE_LIB_IOMGR_POLLER_EVENTMANAGER_LIBUV_H */
diff --git a/src/python/grpcio/grpc_core_dependencies.py b/src/python/grpcio/grpc_core_dependencies.py
index a97bf7e..e7ee6a7 100644
--- a/src/python/grpcio/grpc_core_dependencies.py
+++ b/src/python/grpcio/grpc_core_dependencies.py
@@ -434,6 +434,7 @@
     'src/core/lib/iomgr/load_file.cc',
     'src/core/lib/iomgr/lockfree_event.cc',
     'src/core/lib/iomgr/parse_address.cc',
+    'src/core/lib/iomgr/poller/eventmanager_libuv.cc',
     'src/core/lib/iomgr/polling_entity.cc',
     'src/core/lib/iomgr/pollset.cc',
     'src/core/lib/iomgr/pollset_custom.cc',
diff --git a/test/core/iomgr/poller/BUILD b/test/core/iomgr/poller/BUILD
new file mode 100644
index 0000000..e5998f3
--- /dev/null
+++ b/test/core/iomgr/poller/BUILD
@@ -0,0 +1,42 @@
+# Copyright 2019 gRPC authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+load("//bazel:grpc_build_system.bzl", "grpc_cc_test", "grpc_package")
+
+licenses(["notice"])
+
+grpc_package(
+    name = "test/core/iomgr/poller",
+    visibility = "public",
+)  # Used to test IO poller implementations.
+
+grpc_cc_test(
+    name = "eventmanager_libuv_test",
+    srcs = ["eventmanager_libuv_test.cc"],
+    external_deps = [
+        "gtest",
+    ],
+    language = "C++",
+    tags = [
+        # TSAN has a false-positive for ShutdownRefAsync
+        # https://github.com/grpc/grpc/issues/24242
+        "notsan",
+    ],
+    uses_polling = False,
+    deps = [
+        "//:eventmanager_libuv",
+        "//:grpc_base_c",
+        "//test/core/util:grpc_test_util",
+    ],
+)
diff --git a/test/core/iomgr/poller/eventmanager_libuv_test.cc b/test/core/iomgr/poller/eventmanager_libuv_test.cc
new file mode 100644
index 0000000..82b6de1
--- /dev/null
+++ b/test/core/iomgr/poller/eventmanager_libuv_test.cc
@@ -0,0 +1,90 @@
+
+/*
+ *
+ * Copyright 2019 gRPC authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include "src/core/lib/iomgr/poller/eventmanager_libuv.h"
+
+#include <grpc/grpc.h>
+#include <grpc/support/time.h>
+#include <gtest/gtest.h>
+
+#include "test/core/util/test_config.h"
+
+using grpc::experimental::LibuvEventManager;
+
+namespace grpc_core {
+namespace {
+
+TEST(LibuvEventManager, Allocation) {
+  for (int i = 0; i < 10; i++) {
+    LibuvEventManager* em =
+        new LibuvEventManager(LibuvEventManager::Options(i));
+    gpr_sleep_until(grpc_timeout_milliseconds_to_deadline(1));
+    delete em;
+  }
+}
+
+TEST(LibuvEventManager, ShutdownRef) {
+  for (int i = 0; i < 10; i++) {
+    LibuvEventManager* em =
+        new LibuvEventManager(LibuvEventManager::Options(i));
+    for (int j = 0; j < i; j++) {
+      em->ShutdownRef();
+    }
+    gpr_sleep_until(grpc_timeout_milliseconds_to_deadline(1));
+    for (int j = 0; j < i; j++) {
+      em->ShutdownUnref();
+    }
+    delete em;
+  }
+}
+
+TEST(LibuvEventManager, ShutdownRefAsync) {
+  for (int i = 0; i < 10; i++) {
+    LibuvEventManager* em =
+        new LibuvEventManager(LibuvEventManager::Options(i));
+    for (int j = 0; j < i; j++) {
+      em->ShutdownRef();
+    }
+    // TSAN doesn't like this approach although this would work. TSAN considers
+    // it dangerous to have a destructor being called while its member function
+    // is called but LibuvEventManager handles this by making LibuvEventManager
+    // wait until all pending operations finish.
+    grpc_core::Thread deleter(
+        "deleter", [](void* em) { delete static_cast<LibuvEventManager*>(em); },
+        em);
+    deleter.Start();
+    gpr_sleep_until(grpc_timeout_milliseconds_to_deadline(1));
+    for (int j = 0; j < i; j++) {
+      em->ShutdownUnref();
+    }
+    deleter.Join();
+  }
+}
+
+}  // namespace
+}  // namespace grpc_core
+
+int main(int argc, char** argv) {
+  grpc_init();
+  grpc::testing::TestEnvironment env(argc, argv);
+  ::testing::InitGoogleTest(&argc, argv);
+  int retval = RUN_ALL_TESTS();
+  grpc_shutdown();
+  return retval;
+}
diff --git a/tools/doxygen/Doxyfile.c++.internal b/tools/doxygen/Doxyfile.c++.internal
index c4a4f43..a306983 100644
--- a/tools/doxygen/Doxyfile.c++.internal
+++ b/tools/doxygen/Doxyfile.c++.internal
@@ -1838,6 +1838,8 @@
 src/core/lib/iomgr/nameser.h \
 src/core/lib/iomgr/parse_address.cc \
 src/core/lib/iomgr/parse_address.h \
+src/core/lib/iomgr/poller/eventmanager_libuv.cc \
+src/core/lib/iomgr/poller/eventmanager_libuv.h \
 src/core/lib/iomgr/polling_entity.cc \
 src/core/lib/iomgr/polling_entity.h \
 src/core/lib/iomgr/pollset.cc \
diff --git a/tools/doxygen/Doxyfile.core.internal b/tools/doxygen/Doxyfile.core.internal
index b77a05d..d912a73 100644
--- a/tools/doxygen/Doxyfile.core.internal
+++ b/tools/doxygen/Doxyfile.core.internal
@@ -1678,6 +1678,8 @@
 src/core/lib/iomgr/nameser.h \
 src/core/lib/iomgr/parse_address.cc \
 src/core/lib/iomgr/parse_address.h \
+src/core/lib/iomgr/poller/eventmanager_libuv.cc \
+src/core/lib/iomgr/poller/eventmanager_libuv.h \
 src/core/lib/iomgr/polling_entity.cc \
 src/core/lib/iomgr/polling_entity.h \
 src/core/lib/iomgr/pollset.cc \
diff --git a/tools/run_tests/generated/tests.json b/tools/run_tests/generated/tests.json
index 6e73afa..d902312 100644
--- a/tools/run_tests/generated/tests.json
+++ b/tools/run_tests/generated/tests.json
@@ -4421,6 +4421,30 @@
     "ci_platforms": [
       "linux",
       "mac",
+      "posix",
+      "windows"
+    ],
+    "cpu_cost": 1.0,
+    "exclude_configs": [],
+    "exclude_iomgrs": [],
+    "flaky": false,
+    "gtest": true,
+    "language": "c++",
+    "name": "eventmanager_libuv_test",
+    "platforms": [
+      "linux",
+      "mac",
+      "posix",
+      "windows"
+    ],
+    "uses_polling": false
+  },
+  {
+    "args": [],
+    "benchmark": false,
+    "ci_platforms": [
+      "linux",
+      "mac",
       "posix"
     ],
     "cpu_cost": 1.0,