From 65fa1d9bf339c2f661f6ff7973a6716ebc433265 Mon Sep 17 00:00:00 2001
From: cohcho <chat.freenode.net/cohcho>
Date: Mon, 12 Oct 2020 04:09:09 +0000
Subject: [PATCH] uv: fix performance issue

unix implementation of uv_async_t has been wasting cpu cycles for nothing since 1.29.0 release
implement efficient callback scheduling for linux
---
 src/base/base.cmake     |  3 ++
 src/base/io/Async.cpp   | 78 +++++++++++++++++++++++++++++++++++++++++
 src/base/io/Async.h     | 27 ++++++++++++++
 src/crypto/rx/RxQueue.h |  1 +
 src/net/JobResults.cpp  |  1 +
 5 files changed, 110 insertions(+)
 create mode 100644 src/base/io/Async.cpp
 create mode 100644 src/base/io/Async.h

diff --git a/src/base/base.cmake b/src/base/base.cmake
index da53d5ea8..0574d617e 100644
--- a/src/base/base.cmake
+++ b/src/base/base.cmake
@@ -130,6 +130,9 @@ else()
         src/base/io/json/Json_unix.cpp
         src/base/kernel/Platform_unix.cpp
         )
+    list(APPEND SOURCES_BASE
+        src/base/io/Async.cpp
+        )
 endif()
 
 
diff --git a/src/base/io/Async.cpp b/src/base/io/Async.cpp
new file mode 100644
index 000000000..9ac8b02a2
--- /dev/null
+++ b/src/base/io/Async.cpp
@@ -0,0 +1,78 @@
+#include "base/io/Async.h"
+
+
+#if defined(XMRIG_UV_PERFORMANCE_BUG)
+#include <sys/eventfd.h>
+#include <sys/poll.h>
+#include <unistd.h>
+#include <cstdlib>
+
+
+namespace xmrig {
+
+
+uv_async_t::~uv_async_t()
+{
+    close(m_fd);
+}
+
+
+static void on_schedule(uv_poll_t *handle, int status, int events)
+{
+    static uint64_t val;
+    uv_async_t *async = reinterpret_cast<uv_async_t *>(handle);
+    for (;;) {
+        int r = read(async->m_fd, &val, sizeof(val));
+
+        if (r == sizeof(val))
+            continue;
+
+        if (r != -1)
+            break;
+
+        if (errno == EAGAIN || errno == EWOULDBLOCK)
+            break;
+
+        if (errno == EINTR)
+            continue;
+
+        abort();
+    }
+    if (async->m_cb) {
+        (*async->m_cb)(async);
+    }
+}
+
+
+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) {
+        return uv_translate_sys_error(errno);
+    }
+    uv_poll_init(loop, (uv_poll_t *)async, fd);
+    uv_poll_start((uv_poll_t *)async, POLLIN, on_schedule);
+    async->m_cb = cb;
+    async->m_fd = fd;
+    return 0;
+}
+
+
+int uv_async_send(uv_async_t *async)
+{
+    static const uint64_t val = 1;
+    int r;
+    do {
+        r = write(async->m_fd, &val, sizeof(val));
+    }
+    while (r == -1 && errno == EINTR);
+    if (r == sizeof(val) || (r == 1 && (errno == EAGAIN || errno == EWOULDBLOCK))) {
+        return 0;
+    }
+    abort();
+}
+
+
+
+} // namespace xmrig
+#endif
diff --git a/src/base/io/Async.h b/src/base/io/Async.h
new file mode 100644
index 000000000..4af45f3f1
--- /dev/null
+++ b/src/base/io/Async.h
@@ -0,0 +1,27 @@
+#pragma once
+
+#include <uv.h>
+
+
+// since 2019.05.16, Version 1.29.0 (Stable)
+#if (UV_VERSION_MAJOR >= 1) && (UV_VERSION_MINOR >= 29) && defined(__linux__)
+#define XMRIG_UV_PERFORMANCE_BUG
+namespace xmrig {
+
+
+struct uv_async_t: uv_poll_t
+{
+    typedef void (*uv_async_cb)(uv_async_t* handle);
+    ~uv_async_t();
+    int m_fd = -1;
+    uv_async_cb m_cb = nullptr;
+};
+
+
+using uv_async_cb = uv_async_t::uv_async_cb;
+extern int uv_async_init(uv_loop_t *loop, uv_async_t *async, uv_async_cb cb);
+extern int uv_async_send(uv_async_t *async);
+
+
+} // namespace xmrig
+#endif
diff --git a/src/crypto/rx/RxQueue.h b/src/crypto/rx/RxQueue.h
index fca4a12fc..bee2bbd2a 100644
--- a/src/crypto/rx/RxQueue.h
+++ b/src/crypto/rx/RxQueue.h
@@ -28,6 +28,7 @@
 #define XMRIG_RX_QUEUE_H
 
 
+#include "base/io/Async.h"
 #include "base/tools/Object.h"
 #include "crypto/common/HugePagesInfo.h"
 #include "crypto/rx/RxConfig.h"
diff --git a/src/net/JobResults.cpp b/src/net/JobResults.cpp
index 06118657f..c54b85d34 100644
--- a/src/net/JobResults.cpp
+++ b/src/net/JobResults.cpp
@@ -24,6 +24,7 @@
 
 
 #include "net/JobResults.h"
+#include "base/io/Async.h"
 #include "base/io/log/Log.h"
 #include "base/tools/Handle.h"
 #include "base/tools/Object.h"