diff --git a/src/base/base.cmake b/src/base/base.cmake
index a49a717ee..533b551c4 100644
--- a/src/base/base.cmake
+++ b/src/base/base.cmake
@@ -4,6 +4,7 @@ set(HEADERS_BASE
     src/base/crypto/Coin.h
     src/base/crypto/keccak.h
     src/base/crypto/sha3.h
+    src/base/io/Async.h
     src/base/io/Console.h
     src/base/io/Env.h
     src/base/io/json/Json.h
@@ -21,6 +22,7 @@ set(HEADERS_BASE
     src/base/kernel/config/BaseTransform.h
     src/base/kernel/config/Title.h
     src/base/kernel/Entry.h
+    src/base/kernel/interfaces/IAsyncListener.h
     src/base/kernel/interfaces/IBaseListener.h
     src/base/kernel/interfaces/IClient.h
     src/base/kernel/interfaces/IClientListener.h
@@ -73,6 +75,7 @@ set(SOURCES_BASE
     src/base/crypto/Coin.cpp
     src/base/crypto/keccak.cpp
     src/base/crypto/sha3.cpp
+    src/base/io/Async.cpp
     src/base/io/Console.cpp
     src/base/io/Env.cpp
     src/base/io/json/Json.cpp
@@ -127,7 +130,6 @@ elseif (APPLE)
         )
 else()
     set(SOURCES_OS
-        src/base/io/Async.cpp
         src/base/io/json/Json_unix.cpp
         src/base/kernel/Platform_unix.cpp
         )
diff --git a/src/base/io/Async.cpp b/src/base/io/Async.cpp
index f2bc5ff26..5ee637f2c 100644
--- a/src/base/io/Async.cpp
+++ b/src/base/io/Async.cpp
@@ -19,9 +19,12 @@
  */
 
 #include "base/io/Async.h"
+#include "base/kernel/interfaces/IAsyncListener.h"
+#include "base/tools/Handle.h"
 
 
-#if defined(XMRIG_UV_PERFORMANCE_BUG)
+// since 2019.05.16, Version 1.29.0 (Stable) https://github.com/xmrig/xmrig/pull/1889
+#if (UV_VERSION_MAJOR >= 1) && (UV_VERSION_MINOR >= 29) && defined(__linux__)
 #include <sys/eventfd.h>
 #include <sys/poll.h>
 #include <unistd.h>
@@ -31,16 +34,28 @@
 namespace xmrig {
 
 
+struct uv_async_t: uv_poll_t
+{
+    using uv_async_cb = void (*)(uv_async_t *);
+    ~uv_async_t();
+    int m_fd = -1;
+    uv_async_cb m_cb = nullptr;
+};
+
+
+using uv_async_cb = uv_async_t::uv_async_cb;
+
+
 uv_async_t::~uv_async_t()
 {
     close(m_fd);
 }
 
 
-static void on_schedule(uv_poll_t *handle, int status, int events)
+static void on_schedule(uv_poll_t *handle, int, int)
 {
     static uint64_t val;
-    uv_async_t *async = reinterpret_cast<uv_async_t *>(handle);
+    auto async = reinterpret_cast<uv_async_t *>(handle);
     for (;;) {
         int r = read(async->m_fd, &val, sizeof(val));
 
@@ -64,7 +79,7 @@ static void on_schedule(uv_poll_t *handle, int status, int events)
 }
 
 
-int uv_async_init(uv_loop_t *loop, uv_async_t *async, uv_async_cb cb)
+static int uv_async_init(uv_loop_t *loop, uv_async_t *async, uv_async_cb cb)
 {
     int fd = eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK);
     if (fd < 0) {
@@ -78,7 +93,7 @@ int uv_async_init(uv_loop_t *loop, uv_async_t *async, uv_async_cb cb)
 }
 
 
-int uv_async_send(uv_async_t *async)
+static int uv_async_send(uv_async_t *async)
 {
     static const uint64_t val = 1;
     int r;
@@ -96,3 +111,41 @@ int uv_async_send(uv_async_t *async)
 
 } // namespace xmrig
 #endif
+
+
+namespace xmrig {
+
+
+class AsyncPrivate
+{
+public:
+    IAsyncListener *listener    = nullptr;
+    uv_async_t *async           = nullptr;
+};
+
+
+} // namespace xmrig
+
+
+xmrig::Async::Async(IAsyncListener *listener) : d_ptr(new AsyncPrivate())
+{
+    d_ptr->listener     = listener;
+    d_ptr->async        = new uv_async_t;
+    d_ptr->async->data  = this;
+
+    uv_async_init(uv_default_loop(), d_ptr->async, [](uv_async_t *handle) { static_cast<Async *>(handle->data)->d_ptr->listener->onAsync(); });
+}
+
+
+xmrig::Async::~Async()
+{
+    Handle::close(d_ptr->async);
+
+    delete d_ptr;
+}
+
+
+void xmrig::Async::send()
+{
+    uv_async_send(d_ptr->async);
+}
diff --git a/src/base/io/Async.h b/src/base/io/Async.h
index 7d1993367..acf3b2cc2 100644
--- a/src/base/io/Async.h
+++ b/src/base/io/Async.h
@@ -22,7 +22,7 @@
 #define XMRIG_ASYNC_H
 
 
-#include <uv.h>
+#include "base/tools/Object.h"
 
 
 // since 2019.05.16, Version 1.29.0 (Stable)
@@ -49,4 +49,29 @@ extern int uv_async_send(uv_async_t *async);
 #endif
 
 
+namespace xmrig {
+
+
+class AsyncPrivate;
+class IAsyncListener;
+
+
+class Async
+{
+public:
+    XMRIG_DISABLE_COPY_MOVE_DEFAULT(Async)
+
+    Async(IAsyncListener *listener);
+    ~Async();
+
+    void send();
+
+private:
+    AsyncPrivate *d_ptr;
+};
+
+
+} // namespace xmrig
+
+
 #endif /* XMRIG_ASYNC_H */
diff --git a/src/base/kernel/interfaces/IAsyncListener.h b/src/base/kernel/interfaces/IAsyncListener.h
new file mode 100644
index 000000000..868646feb
--- /dev/null
+++ b/src/base/kernel/interfaces/IAsyncListener.h
@@ -0,0 +1,47 @@
+/* XMRig
+ * Copyright 2018-2020 SChernykh   <https://github.com/SChernykh>
+ * Copyright 2016-2020 XMRig       <https://github.com/xmrig>, <support@xmrig.com>
+ *
+ *   This program is free software: you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation, either version 3 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef XMRIG_IASYNCLISTENER_H
+#define XMRIG_IASYNCLISTENER_H
+
+
+#include "base/tools/Object.h"
+
+
+namespace xmrig {
+
+
+class Async;
+
+
+class IAsyncListener
+{
+public:
+    XMRIG_DISABLE_COPY_MOVE(IAsyncListener)
+
+    IAsyncListener()            = default;
+    virtual ~IAsyncListener()   = default;
+
+    virtual void onAsync()      = 0;
+};
+
+
+} /* namespace xmrig */
+
+
+#endif // XMRIG_IASYNCLISTENER_H
diff --git a/src/crypto/rx/RxQueue.cpp b/src/crypto/rx/RxQueue.cpp
index af139106f..8badab50f 100644
--- a/src/crypto/rx/RxQueue.cpp
+++ b/src/crypto/rx/RxQueue.cpp
@@ -27,9 +27,9 @@
 
 #include "crypto/rx/RxQueue.h"
 #include "backend/common/interfaces/IRxListener.h"
+#include "base/io/Async.h"
 #include "base/io/log/Log.h"
 #include "base/io/log/Tags.h"
-#include "base/tools/Handle.h"
 #include "crypto/rx/RxBasicStorage.h"
 
 
@@ -41,11 +41,7 @@
 xmrig::RxQueue::RxQueue(IRxListener *listener) :
     m_listener(listener)
 {
-    m_async = new uv_async_t;
-    m_async->data = this;
-
-    uv_async_init(uv_default_loop(), m_async, [](uv_async_t *handle) { static_cast<RxQueue *>(handle->data)->onReady(); });
-
+    m_async  = std::make_shared<Async>(this);
     m_thread = std::thread(&RxQueue::backgroundInit, this);
 }
 
@@ -61,8 +57,6 @@ xmrig::RxQueue::~RxQueue()
     m_thread.join();
 
     delete m_storage;
-
-    Handle::close(m_async);
 }
 
 
@@ -167,7 +161,7 @@ void xmrig::RxQueue::backgroundInit()
         }
 
         m_state = STATE_IDLE;
-        uv_async_send(m_async);
+        m_async->send();
     }
 }
 
diff --git a/src/crypto/rx/RxQueue.h b/src/crypto/rx/RxQueue.h
index bee2bbd2a..7b462cf4e 100644
--- a/src/crypto/rx/RxQueue.h
+++ b/src/crypto/rx/RxQueue.h
@@ -28,7 +28,7 @@
 #define XMRIG_RX_QUEUE_H
 
 
-#include "base/io/Async.h"
+#include "base/kernel/interfaces/IAsyncListener.h"
 #include "base/tools/Object.h"
 #include "crypto/common/HugePagesInfo.h"
 #include "crypto/rx/RxConfig.h"
@@ -40,9 +40,6 @@
 #include <thread>
 
 
-using uv_async_t = struct uv_async_s;
-
-
 namespace xmrig
 {
 
@@ -75,19 +72,22 @@ public:
 };
 
 
-class RxQueue
+class RxQueue : public IAsyncListener
 {
 public:
     XMRIG_DISABLE_COPY_MOVE(RxQueue);
 
     RxQueue(IRxListener *listener);
-    ~RxQueue();
+    ~RxQueue() override;
 
     HugePagesInfo hugePages();
     RxDataset *dataset(const Job &job, uint32_t nodeId);
     template<typename T> bool isReady(const T &seed);
     void enqueue(const RxSeed &seed, const std::vector<uint32_t> &nodeset, uint32_t threads, bool hugePages, bool oneGbPages, RxConfig::Mode mode, int priority);
 
+protected:
+    inline void onAsync() override  { onReady(); }
+
 private:
     enum State {
         STATE_IDLE,
@@ -105,9 +105,9 @@ private:
     State m_state = STATE_IDLE;
     std::condition_variable m_cv;
     std::mutex m_mutex;
+    std::shared_ptr<Async> m_async;
     std::thread m_thread;
     std::vector<RxQueueItem> m_queue;
-    uv_async_t *m_async     = nullptr;
 };
 
 
diff --git a/src/net/JobResults.cpp b/src/net/JobResults.cpp
index c54b85d34..bb3771ef3 100644
--- a/src/net/JobResults.cpp
+++ b/src/net/JobResults.cpp
@@ -24,13 +24,13 @@
 
 
 #include "net/JobResults.h"
+#include "backend/common/Tags.h"
 #include "base/io/Async.h"
 #include "base/io/log/Log.h"
-#include "base/tools/Handle.h"
+#include "base/kernel/interfaces/IAsyncListener.h"
 #include "base/tools/Object.h"
 #include "net/interfaces/IJobResultListener.h"
 #include "net/JobResult.h"
-#include "backend/common/Tags.h"
 
 
 #ifdef XMRIG_ALGO_RANDOMX
@@ -57,6 +57,7 @@
 
 #include <cassert>
 #include <list>
+#include <memory>
 #include <mutex>
 #include <uv.h>
 
@@ -193,7 +194,7 @@ static void getResults(JobBundle &bundle, std::vector<JobResult> &results, uint3
 #endif
 
 
-class JobResultsPrivate
+class JobResultsPrivate : public IAsyncListener
 {
 public:
     XMRIG_DISABLE_COPY_MOVE_DEFAULT(JobResultsPrivate)
@@ -202,17 +203,11 @@ public:
         m_hwAES(hwAES),
         m_listener(listener)
     {
-        m_async = new uv_async_t;
-        m_async->data = this;
-
-        uv_async_init(uv_default_loop(), m_async, JobResultsPrivate::onResult);
+        m_async = std::make_shared<Async>(this);
     }
 
 
-    inline ~JobResultsPrivate()
-    {
-        Handle::close(m_async);
-    }
+    ~JobResultsPrivate() override = default;
 
 
     inline void submit(const JobResult &result)
@@ -220,7 +215,7 @@ public:
         std::lock_guard<std::mutex> lock(m_mutex);
         m_results.push_back(result);
 
-        uv_async_send(m_async);
+        m_async->send();
     }
 
 
@@ -230,11 +225,15 @@ public:
         std::lock_guard<std::mutex> lock(m_mutex);
         m_bundles.emplace_back(job, results, count, device_index);
 
-        uv_async_send(m_async);
+        m_async->send();
     }
 #   endif
 
 
+protected:
+    inline void onAsync() override  { submit(); }
+
+
 private:
     static void onResult(uv_async_t *handle) { static_cast<JobResultsPrivate*>(handle->data)->submit(); }
 
@@ -300,7 +299,7 @@ private:
     IJobResultListener *m_listener;
     std::list<JobResult> m_results;
     std::mutex m_mutex;
-    uv_async_t *m_async;
+    std::shared_ptr<Async> m_async;
 
 #   if defined(XMRIG_FEATURE_OPENCL) || defined(XMRIG_FEATURE_CUDA)
     std::list<JobBundle> m_bundles;