From a042cbf8856de5df95bac73d7413e63483a56db7 Mon Sep 17 00:00:00 2001
From: XMRig <support@xmrig.com>
Date: Sun, 1 Apr 2018 22:49:21 +0700
Subject: [PATCH] Added classes IThread, CpuThread and API endpoint "GET
 /1/threads".

---
 CMakeLists.txt                        |  3 ++
 src/App.cpp                           |  2 +-
 src/Mem.cpp                           |  2 +-
 src/Mem_win.cpp                       |  2 +-
 src/Summary.cpp                       |  2 +-
 src/api/ApiRouter.cpp                 | 26 ++++++++--
 src/api/ApiRouter.h                   |  1 +
 src/core/CommonConfig.cpp             |  6 +--
 src/core/CommonConfig.h               |  5 +-
 src/core/Config.cpp                   | 58 ++++++++++++----------
 src/core/Config.h                     | 41 +++++++---------
 src/crypto/CryptoNight.cpp            |  6 +--
 src/interfaces/IThread.h              | 64 ++++++++++++++++++++++++
 src/net/Job.cpp                       |  4 +-
 src/net/Url.cpp                       |  6 +--
 src/net/strategies/DonateStrategy.cpp |  2 +-
 src/workers/CpuThread.cpp             | 68 +++++++++++++++++++++++++
 src/workers/CpuThread.h               | 71 +++++++++++++++++++++++++++
 src/xmrig.h                           | 29 ++++++++---
 19 files changed, 320 insertions(+), 78 deletions(-)
 create mode 100644 src/interfaces/IThread.h
 create mode 100644 src/workers/CpuThread.cpp
 create mode 100644 src/workers/CpuThread.h

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 1f42950ed..c9d6624fc 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -32,6 +32,7 @@ set(HEADERS
     src/interfaces/ILogBackend.h
     src/interfaces/IStrategy.h
     src/interfaces/IStrategyListener.h
+    src/interfaces/IThread.h
     src/interfaces/IWatcherListener.h
     src/interfaces/IWorker.h
     src/log/ConsoleLog.h
@@ -52,6 +53,7 @@ set(HEADERS
     src/Platform.h
     src/Summary.h
     src/version.h
+    src/workers/CpuThread.h
     src/workers/DoubleWorker.h
     src/workers/Handle.h
     src/workers/Hashrate.h
@@ -106,6 +108,7 @@ set(SOURCES
     src/net/Url.cpp
     src/Platform.cpp
     src/Summary.cpp
+    src/workers/CpuThread.cpp
     src/workers/DoubleWorker.cpp
     src/workers/Handle.cpp
     src/workers/Hashrate.cpp
diff --git a/src/App.cpp b/src/App.cpp
index 9bdd381ee..a9d107743 100644
--- a/src/App.cpp
+++ b/src/App.cpp
@@ -107,7 +107,7 @@ int App::exec()
     }
 
     Mem::allocate(m_controller->config()->algorithm(),
-                  m_controller->config()->threads(),
+                  m_controller->config()->threadsCount(),
                   m_controller->config()->isDoubleHash(),
                   m_controller->config()->isHugePages()
                   );
diff --git a/src/Mem.cpp b/src/Mem.cpp
index 991f4398d..f5da7865c 100644
--- a/src/Mem.cpp
+++ b/src/Mem.cpp
@@ -43,7 +43,7 @@ alignas(16) uint8_t *Mem::m_memory = nullptr;
 cryptonight_ctx *Mem::create(int threadId)
 {
 #   ifndef XMRIG_NO_AEON
-    if (m_algo == xmrig::ALGO_CRYPTONIGHT_LITE) {
+    if (m_algo == xmrig::CRYPTONIGHT_LITE) {
         return createLite(threadId);
     }
 #   endif
diff --git a/src/Mem_win.cpp b/src/Mem_win.cpp
index 1b35c7041..6cc0a6eee 100644
--- a/src/Mem_win.cpp
+++ b/src/Mem_win.cpp
@@ -151,7 +151,7 @@ bool Mem::allocate(int algo, int threads, bool doubleHash, bool enabled)
     m_threads    = threads;
     m_doubleHash = doubleHash;
 
-    const int ratio = (doubleHash && algo != xmrig::ALGO_CRYPTONIGHT_LITE) ? 2 : 1;
+    const int ratio = (doubleHash && algo != xmrig::CRYPTONIGHT_LITE) ? 2 : 1;
     m_size          = MONERO_MEMORY * (threads * ratio + 1);
 
     if (!enabled) {
diff --git a/src/Summary.cpp b/src/Summary.cpp
index 6aa42b745..7e341ce0a 100644
--- a/src/Summary.cpp
+++ b/src/Summary.cpp
@@ -101,7 +101,7 @@ static void print_threads(xmrig::Config *config)
     }
 
     Log::i()->text(config->isColors() ? "\x1B[01;32m * \x1B[01;37mTHREADS:      \x1B[01;36m%d\x1B[01;37m, %s, av=%d, %sdonate=%d%%%s" : " * THREADS:      %d, %s, av=%d, %sdonate=%d%%%s",
-                   config->threads(),
+                   config->threadsCount(),
                    config->algoName(),
                    config->algoVariant(),
                    config->isColors() && config->donateLevel() == 0 ? "\x1B[01;31m" : "",
diff --git a/src/api/ApiRouter.cpp b/src/api/ApiRouter.cpp
index e1ba9e174..6c4f1f271 100644
--- a/src/api/ApiRouter.cpp
+++ b/src/api/ApiRouter.cpp
@@ -38,6 +38,7 @@
 #include "core/Config.h"
 #include "core/Controller.h"
 #include "Cpu.h"
+#include "interfaces/IThread.h"
 #include "Mem.h"
 #include "net/Job.h"
 #include "Platform.h"
@@ -67,7 +68,7 @@ static inline double normalize(double d)
 ApiRouter::ApiRouter(xmrig::Controller *controller) :
     m_controller(controller)
 {
-    m_threads  = controller->config()->threads();
+    m_threads  = controller->config()->threadsCount();
     m_hashrate = new double[m_threads * 3]();
 
     memset(m_totalHashrate, 0, sizeof(m_totalHashrate));
@@ -87,7 +88,6 @@ ApiRouter::~ApiRouter()
 void ApiRouter::ApiRouter::get(const xmrig::HttpRequest &req, xmrig::HttpReply &reply) const
 {
     rapidjson::Document doc;
-    doc.SetObject();
 
     if (req.match("/1/config")) {
         if (req.isRestricted()) {
@@ -100,6 +100,14 @@ void ApiRouter::ApiRouter::get(const xmrig::HttpRequest &req, xmrig::HttpReply &
         return finalize(reply, doc);
     }
 
+    if (req.match("/1/threads")) {
+        getThreads(doc);
+
+        return finalize(reply, doc);
+    }
+
+    doc.SetObject();
+
     getIdentify(doc);
     getMiner(doc);
     getHashrate(doc);
@@ -144,7 +152,7 @@ void ApiRouter::tick(const NetworkState &network)
 
 void ApiRouter::onConfigChanged(xmrig::Config *config, xmrig::Config *previousConfig)
 {
-//    updateWorkerId(config->apiWorkerId(), previousConfig->apiWorkerId());
+    updateWorkerId(config->apiWorkerId(), previousConfig->apiWorkerId());
 }
 
 
@@ -288,6 +296,18 @@ void ApiRouter::getResults(rapidjson::Document &doc) const
 }
 
 
+void ApiRouter::getThreads(rapidjson::Document &doc) const
+{
+    doc.SetArray();
+
+    const std::vector<xmrig::IThread *> &threads = m_controller->config()->threads();
+
+    for (const xmrig::IThread *thread : threads) {
+       doc.PushBack(thread->toAPI(doc), doc.GetAllocator());
+    }
+}
+
+
 void ApiRouter::setWorkerId(const char *id)
 {
     memset(m_workerId, 0, sizeof(m_workerId));
diff --git a/src/api/ApiRouter.h b/src/api/ApiRouter.h
index 2ae1cc80c..e14f9e879 100644
--- a/src/api/ApiRouter.h
+++ b/src/api/ApiRouter.h
@@ -63,6 +63,7 @@ private:
     void getIdentify(rapidjson::Document &doc) const;
     void getMiner(rapidjson::Document &doc) const;
     void getResults(rapidjson::Document &doc) const;
+    void getThreads(rapidjson::Document &doc) const;
     void setWorkerId(const char *id);
     void updateWorkerId(const char *id, const char *previousId);
 
diff --git a/src/core/CommonConfig.cpp b/src/core/CommonConfig.cpp
index ea17342cd..514486547 100644
--- a/src/core/CommonConfig.cpp
+++ b/src/core/CommonConfig.cpp
@@ -51,6 +51,7 @@ static const char *algoNames[] = {
 
 
 xmrig::CommonConfig::CommonConfig() :
+    m_algorithm(CRYPTONIGHT),
     m_adjusted(false),
     m_apiIPv6(true),
     m_apiRestricted(true),
@@ -63,7 +64,6 @@ xmrig::CommonConfig::CommonConfig() :
     m_fileName(nullptr),
     m_logFile(nullptr),
     m_userAgent(nullptr),
-    m_algorithm(ALGO_CRYPTONIGHT),
     m_apiPort(0),
     m_donateLevel(kDefaultDonateLevel),
     m_printTime(60),
@@ -367,7 +367,7 @@ void xmrig::CommonConfig::setAlgo(const char *algo)
     if (strcasecmp(algo, "cryptonight-light") == 0) {
         fprintf(stderr, "Algorithm \"cryptonight-light\" is deprecated, use \"cryptonight-lite\" instead\n");
 
-        m_algorithm = ALGO_CRYPTONIGHT_LITE;
+        m_algorithm = CRYPTONIGHT_LITE;
         return;
     }
 
@@ -375,7 +375,7 @@ void xmrig::CommonConfig::setAlgo(const char *algo)
 
     for (size_t i = 0; i < size; i++) {
         if (algoNames[i] && strcasecmp(algo, algoNames[i]) == 0) {
-            m_algorithm = (int) i;
+            m_algorithm = static_cast<Algo>(i);
             break;
         }
     }
diff --git a/src/core/CommonConfig.h b/src/core/CommonConfig.h
index ab840a48b..0f6f6b8c9 100644
--- a/src/core/CommonConfig.h
+++ b/src/core/CommonConfig.h
@@ -29,6 +29,7 @@
 
 
 #include "interfaces/IConfig.h"
+#include "xmrig.h"
 
 
 class Url;
@@ -45,6 +46,7 @@ public:
 
     const char *algoName() const;
 
+    inline Algo algorithm() const                  { return m_algorithm; }
     inline bool isApiIPv6() const                  { return m_apiIPv6; }
     inline bool isApiRestricted() const            { return m_apiRestricted; }
     inline bool isBackground() const               { return m_background; }
@@ -55,7 +57,6 @@ public:
     inline const char *logFile() const             { return m_logFile; }
     inline const char *userAgent() const           { return m_userAgent; }
     inline const std::vector<Url*> &pools() const  { return m_pools; }
-    inline int algorithm() const                   { return m_algorithm; }
     inline int apiPort() const                     { return m_apiPort; }
     inline int donateLevel() const                 { return m_donateLevel; }
     inline int printTime() const                   { return m_printTime; }
@@ -75,6 +76,7 @@ protected:
     bool save() override;
     void setFileName(const char *fileName) override;
 
+    Algo m_algorithm;
     bool m_adjusted;
     bool m_apiIPv6;
     bool m_apiRestricted;
@@ -87,7 +89,6 @@ protected:
     char *m_fileName;
     char *m_logFile;
     char *m_userAgent;
-    int m_algorithm;
     int m_apiPort;
     int m_donateLevel;
     int m_printTime;
diff --git a/src/core/Config.cpp b/src/core/Config.cpp
index 49b40aa83..6bc2dc0fc 100644
--- a/src/core/Config.cpp
+++ b/src/core/Config.cpp
@@ -34,6 +34,7 @@
 #include "rapidjson/document.h"
 #include "rapidjson/filewritestream.h"
 #include "rapidjson/prettywriter.h"
+#include "workers/CpuThread.h"
 #include "xmrig.h"
 
 
@@ -41,18 +42,17 @@ static char affinity_tmp[20] = { 0 };
 
 
 xmrig::Config::Config() : xmrig::CommonConfig(),
+    m_algoVariant(AV_AUTO),
     m_doubleHash(false),
     m_dryRun(false),
     m_hugePages(true),
     m_safe(false),
-    m_algoVariant(0),
     m_maxCpuUsage(75),
     m_printTime(60),
     m_priority(-1),
-    m_threads(0),
-    m_affinity(-1L)
+    m_affinity(-1L),
+    m_threadsCount(0)
 {
-
 }
 
 
@@ -135,7 +135,7 @@ void xmrig::Config::getJSON(rapidjson::Document &doc) const
     doc.AddMember("retries",       retries(), allocator);
     doc.AddMember("retry-pause",   retryPause(), allocator);
     doc.AddMember("safe",          m_safe, allocator);
-    doc.AddMember("threads",       threads(), allocator);
+    doc.AddMember("threads",       threadsCount(), allocator);
     doc.AddMember("user-agent",    userAgent() ? rapidjson::Value(rapidjson::StringRef(userAgent())).Move() : rapidjson::Value(rapidjson::kNullType).Move(), allocator);
 
 #   ifdef HAVE_SYSLOG_H
@@ -159,20 +159,24 @@ bool xmrig::Config::adjust()
     }
 
     m_algoVariant = getAlgoVariant();
-    if (m_algoVariant == AV2_AESNI_DOUBLE || m_algoVariant == AV4_SOFT_AES_DOUBLE) {
+    if (m_algoVariant == AV_DOUBLE || m_algoVariant == AV_DOUBLE_SOFT) {
         m_doubleHash = true;
     }
 
-    if (!m_threads) {
-        m_threads = Cpu::optimalThreadsCount(m_algorithm, m_doubleHash, m_maxCpuUsage);
+    if (!m_threadsCount) {
+        m_threadsCount = Cpu::optimalThreadsCount(m_algorithm, m_doubleHash, m_maxCpuUsage);
     }
     else if (m_safe) {
-        const int count = Cpu::optimalThreadsCount(m_algorithm, m_doubleHash, m_maxCpuUsage);
-        if (m_threads > count) {
-            m_threads = count;
+        const size_t count = Cpu::optimalThreadsCount(m_algorithm, m_doubleHash, m_maxCpuUsage);
+        if (m_threadsCount > count) {
+            m_threadsCount = count;
         }
     }
 
+    for (size_t i = 0; i < m_threadsCount; ++i) {
+        m_threads.push_back(CpuThread::createFromAV(i, m_algorithm, m_algoVariant, m_affinity, m_priority));
+    }
+
     return true;
 }
 
@@ -225,7 +229,7 @@ bool xmrig::Config::parseString(int key, const char *arg)
 
     case xmrig::IConfig::ThreadsKey:  /* --threads */
         if (strncmp(arg, "all", 3) == 0) {
-            m_threads = Cpu::threads();
+            m_threadsCount = Cpu::threads();
             return true;
         }
 
@@ -275,14 +279,14 @@ bool xmrig::Config::parseInt(int key, int arg)
 {
     switch (key) {
     case xmrig::IConfig::ThreadsKey: /* --threads */
-        if (m_threads >= 0 && arg < 1024) {
-            m_threads = arg;
+        if (m_threadsCount >= 0 && arg < 1024) {
+            m_threadsCount = arg;
         }
         break;
 
     case xmrig::IConfig::AVKey: /* --av */
-        if (arg >= AV0_AUTO && arg < AV_MAX) {
-            m_algoVariant = arg;
+        if (arg >= AV_AUTO && arg < AV_MAX) {
+            m_algoVariant = static_cast<AlgoVariant>(arg);
         }
         break;
 
@@ -306,20 +310,20 @@ bool xmrig::Config::parseInt(int key, int arg)
 }
 
 
-int xmrig::Config::getAlgoVariant() const
+xmrig::AlgoVariant xmrig::Config::getAlgoVariant() const
 {
 #   ifndef XMRIG_NO_AEON
-    if (m_algorithm == xmrig::ALGO_CRYPTONIGHT_LITE) {
+    if (m_algorithm == xmrig::CRYPTONIGHT_LITE) {
         return getAlgoVariantLite();
     }
 #   endif
 
-    if (m_algoVariant <= AV0_AUTO || m_algoVariant >= AV_MAX) {
-        return Cpu::hasAES() ? AV1_AESNI : AV3_SOFT_AES;
+    if (m_algoVariant <= AV_AUTO || m_algoVariant >= AV_MAX) {
+        return Cpu::hasAES() ? AV_SINGLE : AV_SINGLE_SOFT;
     }
 
-    if (m_safe && !Cpu::hasAES() && m_algoVariant <= AV2_AESNI_DOUBLE) {
-        return m_algoVariant + 2;
+    if (m_safe && !Cpu::hasAES() && m_algoVariant <= AV_DOUBLE) {
+        return static_cast<AlgoVariant>(m_algoVariant + 2);
     }
 
     return m_algoVariant;
@@ -327,14 +331,14 @@ int xmrig::Config::getAlgoVariant() const
 
 
 #ifndef XMRIG_NO_AEON
-int xmrig::Config::getAlgoVariantLite() const
+xmrig::AlgoVariant xmrig::Config::getAlgoVariantLite() const
 {
-    if (m_algoVariant <= AV0_AUTO || m_algoVariant >= AV_MAX) {
-        return Cpu::hasAES() ? AV2_AESNI_DOUBLE : AV4_SOFT_AES_DOUBLE;
+    if (m_algoVariant <= AV_AUTO || m_algoVariant >= AV_MAX) {
+        return Cpu::hasAES() ? AV_DOUBLE : AV_DOUBLE_SOFT;
     }
 
-    if (m_safe && !Cpu::hasAES() && m_algoVariant <= AV2_AESNI_DOUBLE) {
-        return m_algoVariant + 2;
+    if (m_safe && !Cpu::hasAES() && m_algoVariant <= AV_DOUBLE) {
+        return static_cast<AlgoVariant>(m_algoVariant + 2);
     }
 
     return m_algoVariant;
diff --git a/src/core/Config.h b/src/core/Config.h
index 110c61a74..536e1c012 100644
--- a/src/core/Config.h
+++ b/src/core/Config.h
@@ -29,8 +29,9 @@
 #include <vector>
 
 
-#include "rapidjson/fwd.h"
 #include "core/CommonConfig.h"
+#include "rapidjson/fwd.h"
+#include "xmrig.h"
 
 
 class Addr;
@@ -41,6 +42,7 @@ namespace xmrig {
 
 
 class ConfigLoader;
+class IThread;
 class IWatcherListener;
 
 
@@ -57,18 +59,7 @@ class IWatcherListener;
  */
 class Config : public CommonConfig
 {
-    friend class ConfigLoader;
-
 public:
-    enum AlgoVariant {
-        AV0_AUTO,
-        AV1_AESNI,
-        AV2_AESNI_DOUBLE,
-        AV3_SOFT_AES,
-        AV4_SOFT_AES_DOUBLE,
-        AV_MAX
-    };
-
     Config();
     ~Config();
 
@@ -76,14 +67,15 @@ public:
 
     void getJSON(rapidjson::Document &doc) const override;
 
-    inline bool isDoubleHash() const              { return m_doubleHash; }
-    inline bool isDryRun() const                  { return m_dryRun; }
-    inline bool isHugePages() const               { return m_hugePages; }
-    inline int algoVariant() const                { return m_algoVariant; }
-    inline int printTime() const                  { return m_printTime; }
-    inline int priority() const                   { return m_priority; }
-    inline int threads() const                    { return m_threads; }
-    inline int64_t affinity() const               { return m_affinity; }
+    inline AlgoVariant algoVariant() const               { return m_algoVariant; }
+    inline bool isDoubleHash() const                     { return m_doubleHash; }
+    inline bool isDryRun() const                         { return m_dryRun; }
+    inline bool isHugePages() const                      { return m_hugePages; }
+    inline const std::vector<IThread *> &threads() const { return m_threads; }
+    inline int printTime() const                         { return m_printTime; }
+    inline int priority() const                          { return m_priority; }
+    inline int threadsCount() const                      { return m_threadsCount; }
+    inline int64_t affinity() const                      { return m_affinity; }
 
     static Config *load(int argc, char **argv, IWatcherListener *listener);
 
@@ -97,21 +89,22 @@ protected:
 private:
     bool parseInt(int key, int arg);
 
-    int getAlgoVariant() const;
+    AlgoVariant getAlgoVariant() const;
 #   ifndef XMRIG_NO_AEON
-    int getAlgoVariantLite() const;
+    AlgoVariant getAlgoVariantLite() const;
 #   endif
 
+    AlgoVariant m_algoVariant;
     bool m_doubleHash;
     bool m_dryRun;
     bool m_hugePages;
     bool m_safe;
-    int m_algoVariant;
     int m_maxCpuUsage;
     int m_printTime;
     int m_priority;
-    int m_threads;
     int64_t m_affinity;
+    size_t m_threadsCount;
+    std::vector<IThread *> m_threads;
 };
 
 
diff --git a/src/crypto/CryptoNight.cpp b/src/crypto/CryptoNight.cpp
index c9a2e499a..f7dc1a378 100644
--- a/src/crypto/CryptoNight.cpp
+++ b/src/crypto/CryptoNight.cpp
@@ -137,7 +137,7 @@ bool CryptoNight::init(int algo, int variant, bool doubleHash)
     }
 
 #   ifndef XMRIG_NO_AEON
-    const int index = algo == xmrig::ALGO_CRYPTONIGHT_LITE ? (variant + 3) : (variant - 1);
+    const int index = algo == xmrig::CRYPTONIGHT_LITE ? (variant + 3) : (variant - 1);
 #   else
     const int index = variant - 1;
 #   endif
@@ -167,7 +167,7 @@ bool CryptoNight::selfTest(int algo, bool doubleHash) {
     cryptonight_hash_ctx(test_input, 76, output, ctx, 0);
 
 #   ifndef XMRIG_NO_AEON
-    bool rc = memcmp(output, algo == xmrig::ALGO_CRYPTONIGHT_LITE ? test_output_v0_lite : test_output_v0, (doubleHash ? 64 : 32)) == 0;
+    bool rc = memcmp(output, algo == xmrig::CRYPTONIGHT_LITE ? test_output_v0_lite : test_output_v0, (doubleHash ? 64 : 32)) == 0;
 #   else
     bool rc = memcmp(output, test_output_v0, (doubleHash ? 64 : 32)) == 0;
 #   endif
@@ -176,7 +176,7 @@ bool CryptoNight::selfTest(int algo, bool doubleHash) {
         cryptonight_hash_ctx(test_input, 76, output, ctx, 1);
 
 #       ifndef XMRIG_NO_AEON
-        rc = memcmp(output, algo == xmrig::ALGO_CRYPTONIGHT_LITE ? test_output_v1_lite : test_output_v1, (doubleHash ? 64 : 32)) == 0;
+        rc = memcmp(output, algo == xmrig::CRYPTONIGHT_LITE ? test_output_v1_lite : test_output_v1, (doubleHash ? 64 : 32)) == 0;
 #       else
         rc = memcmp(output, test_output_v1, (doubleHash ? 64 : 32)) == 0;
 #       endif
diff --git a/src/interfaces/IThread.h b/src/interfaces/IThread.h
new file mode 100644
index 000000000..e2325c729
--- /dev/null
+++ b/src/interfaces/IThread.h
@@ -0,0 +1,64 @@
+/* XMRig
+ * Copyright 2010      Jeff Garzik <jgarzik@pobox.com>
+ * Copyright 2012-2014 pooler      <pooler@litecoinpool.org>
+ * Copyright 2014      Lucas Jones <https://github.com/lucasjones>
+ * Copyright 2014-2016 Wolf9466    <https://github.com/OhGodAPet>
+ * Copyright 2016      Jay D Dee   <jayddee246@gmail.com>
+ * Copyright 2016-2018 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 __ITHREAD_H__
+#define __ITHREAD_H__
+
+
+#include <stdint.h>
+
+
+#include "rapidjson/fwd.h"
+#include "xmrig.h"
+
+
+namespace xmrig {
+
+
+class IThread
+{
+public:
+    enum Type {
+        CPU,
+        OpenCL,
+        CUDA
+    };
+
+    virtual ~IThread() {}
+
+    virtual Algo algorithm() const   = 0;
+    virtual int multiway() const     = 0;
+    virtual int priority() const     = 0;
+    virtual int64_t affinity() const = 0;
+    virtual size_t index() const     = 0;
+    virtual Type type() const        = 0;
+
+#   ifndef XMRIG_NO_API
+    virtual rapidjson::Value toAPI(rapidjson::Document &doc) const = 0;
+#   endif
+};
+
+
+} /* namespace xmrig */
+
+
+#endif // __ITHREAD_H__
diff --git a/src/net/Job.cpp b/src/net/Job.cpp
index 7d137fac9..e57143767 100644
--- a/src/net/Job.cpp
+++ b/src/net/Job.cpp
@@ -59,7 +59,7 @@ static inline char hf_bin2hex(unsigned char c)
 Job::Job() :
     m_nicehash(false),
     m_coin(),
-    m_algo(xmrig::ALGO_CRYPTONIGHT),
+    m_algo(xmrig::CRYPTONIGHT),
     m_poolId(-2),
     m_threadId(-1),
     m_variant(xmrig::VARIANT_AUTO),
@@ -164,7 +164,7 @@ void Job::setCoin(const char *coin)
     }
 
     strncpy(m_coin, coin, sizeof(m_coin));
-    m_algo = strcmp(m_coin, "AEON") == 0 ? xmrig::ALGO_CRYPTONIGHT_LITE : xmrig::ALGO_CRYPTONIGHT;
+    m_algo = strcmp(m_coin, "AEON") == 0 ? xmrig::CRYPTONIGHT_LITE : xmrig::CRYPTONIGHT;
 }
 
 
diff --git a/src/net/Url.cpp b/src/net/Url.cpp
index 8905e9190..3d5f2cdd1 100644
--- a/src/net/Url.cpp
+++ b/src/net/Url.cpp
@@ -41,7 +41,7 @@ Url::Url() :
     m_host(nullptr),
     m_password(nullptr),
     m_user(nullptr),
-    m_algo(xmrig::ALGO_CRYPTONIGHT),
+    m_algo(xmrig::CRYPTONIGHT),
     m_keepAlive(0),
     m_variant(xmrig::VARIANT_AUTO),
     m_url(nullptr),
@@ -66,7 +66,7 @@ Url::Url(const char *url) :
     m_host(nullptr),
     m_password(nullptr),
     m_user(nullptr),
-    m_algo(xmrig::ALGO_CRYPTONIGHT),
+    m_algo(xmrig::CRYPTONIGHT),
     m_keepAlive(0),
     m_variant(xmrig::VARIANT_AUTO),
     m_url(nullptr),
@@ -80,7 +80,7 @@ Url::Url(const char *host, uint16_t port, const char *user, const char *password
     m_nicehash(nicehash),
     m_password(password ? strdup(password) : nullptr),
     m_user(user ? strdup(user) : nullptr),
-    m_algo(xmrig::ALGO_CRYPTONIGHT),
+    m_algo(xmrig::CRYPTONIGHT),
     m_keepAlive(keepAlive),
     m_variant(variant),
     m_url(nullptr),
diff --git a/src/net/strategies/DonateStrategy.cpp b/src/net/strategies/DonateStrategy.cpp
index ae707e21f..495038207 100644
--- a/src/net/strategies/DonateStrategy.cpp
+++ b/src/net/strategies/DonateStrategy.cpp
@@ -59,7 +59,7 @@ DonateStrategy::DonateStrategy(int level, const char *user, int algo, IStrategyL
     keccak(reinterpret_cast<const uint8_t *>(user), static_cast<int>(strlen(user)), hash, sizeof(hash));
     Job::toHex(hash, 32, userId);
 
-    if (algo == xmrig::ALGO_CRYPTONIGHT) {
+    if (algo == xmrig::CRYPTONIGHT) {
         m_pools.push_back(new Url(kDonatePool1, 6666, userId, nullptr, false, true));
         m_pools.push_back(new Url(kDonatePool1, 80,   userId, nullptr, false, true));
         m_pools.push_back(new Url(kDonatePool2, 5555, "48edfHu7V9Z84YzzMa6fUueoELZ9ZRXq9VetWzYGzKt52XU5xvqgzYnDK9URnRoJMk1j8nLwEVsaSWJ4fhdUyZijBGUicoD", "emergency", false, false));
diff --git a/src/workers/CpuThread.cpp b/src/workers/CpuThread.cpp
new file mode 100644
index 000000000..ff3aaed73
--- /dev/null
+++ b/src/workers/CpuThread.cpp
@@ -0,0 +1,68 @@
+/* XMRig
+ * Copyright 2010      Jeff Garzik <jgarzik@pobox.com>
+ * Copyright 2012-2014 pooler      <pooler@litecoinpool.org>
+ * Copyright 2014      Lucas Jones <https://github.com/lucasjones>
+ * Copyright 2014-2016 Wolf9466    <https://github.com/OhGodAPet>
+ * Copyright 2016      Jay D Dee   <jayddee246@gmail.com>
+ * Copyright 2017-2018 XMR-Stak    <https://github.com/fireice-uk>, <https://github.com/psychocrypt>
+ * Copyright 2016-2018 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/>.
+ */
+
+
+#include "rapidjson/document.h"
+#include "workers/CpuThread.h"
+
+
+xmrig::CpuThread::CpuThread(size_t index, Algo algorithm, int multiway, int64_t affinity, int priority, bool softAES, bool prefetch) :
+    m_algorithm(algorithm),
+    m_prefetch(prefetch),
+    m_softAES(softAES),
+    m_multiway(multiway),
+    m_priority(priority),
+    m_affinity(affinity),
+    m_index(index)
+{
+}
+
+
+xmrig::CpuThread::~CpuThread()
+{
+}
+
+
+xmrig::CpuThread *xmrig::CpuThread::createFromAV(size_t index, Algo algorithm, AlgoVariant av, int64_t affinity, int priority)
+{
+    return new CpuThread(index, algorithm, 1, affinity, priority, false, false);
+}
+
+
+#ifndef XMRIG_NO_API
+rapidjson::Value xmrig::CpuThread::toAPI(rapidjson::Document &doc) const
+{
+    rapidjson::Value obj(rapidjson::kObjectType);
+    auto &allocator = doc.GetAllocator();
+
+    obj.AddMember("type",          "cpu", allocator);
+    obj.AddMember("algo",           algorithm(), allocator);
+    obj.AddMember("low_power_mode", multiway(), allocator);
+    obj.AddMember("affine_to_cpu",  affinity(), allocator);
+    obj.AddMember("priority",       priority(), allocator);
+    obj.AddMember("prefetch",       isPrefetch(), allocator);
+    obj.AddMember("soft_aes",       isSoftAES(), allocator);
+
+    return obj;
+}
+#endif
diff --git a/src/workers/CpuThread.h b/src/workers/CpuThread.h
new file mode 100644
index 000000000..93ef50d59
--- /dev/null
+++ b/src/workers/CpuThread.h
@@ -0,0 +1,71 @@
+/* XMRig
+ * Copyright 2010      Jeff Garzik <jgarzik@pobox.com>
+ * Copyright 2012-2014 pooler      <pooler@litecoinpool.org>
+ * Copyright 2014      Lucas Jones <https://github.com/lucasjones>
+ * Copyright 2014-2016 Wolf9466    <https://github.com/OhGodAPet>
+ * Copyright 2016      Jay D Dee   <jayddee246@gmail.com>
+ * Copyright 2017-2018 XMR-Stak    <https://github.com/fireice-uk>, <https://github.com/psychocrypt>
+ * Copyright 2016-2018 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 __CPUTHREAD_H__
+#define __CPUTHREAD_H__
+
+
+#include "interfaces/IThread.h"
+#include "xmrig.h"
+
+
+namespace xmrig {
+
+
+class CpuThread : public IThread
+{
+public:
+    CpuThread(size_t index, Algo algorithm, int multiway, int64_t affinity, int priority, bool softAES, bool prefetch);
+    ~CpuThread();
+
+    static CpuThread *createFromAV(size_t index, Algo algorithm, AlgoVariant av, int64_t affinity, int priority);
+
+    inline bool isPrefetch() const            { return m_prefetch; }
+    inline bool isSoftAES() const             { return m_softAES; }
+
+    inline Algo algorithm() const override    { return m_algorithm; }
+    inline int multiway() const override      { return m_multiway; }
+    inline int priority() const override      { return m_priority; }
+    inline int64_t affinity() const override  { return m_affinity; }
+    inline size_t index() const override      { return m_affinity; }
+    inline Type type() const override         { return CPU; }
+
+#   ifndef XMRIG_NO_API
+    rapidjson::Value toAPI(rapidjson::Document &doc) const override;
+#   endif
+
+private:
+    const Algo m_algorithm;
+    const bool m_prefetch;
+    const bool m_softAES;
+    const int m_multiway;
+    const int m_priority;
+    const int64_t m_affinity;
+    const size_t m_index;
+};
+
+
+} /* namespace xmrig */
+
+
+#endif /* __CPUTHREAD_H__ */
diff --git a/src/xmrig.h b/src/xmrig.h
index 9cae73c77..805b7ceba 100644
--- a/src/xmrig.h
+++ b/src/xmrig.h
@@ -30,18 +30,35 @@ namespace xmrig
 
 
 enum Algo {
-    ALGO_CRYPTONIGHT,       /* CryptoNight (Monero) */
-    ALGO_CRYPTONIGHT_LITE,  /* CryptoNight-Lite (AEON) */
-    ALGO_CRYPTONIGHT_HEAVY, /* CryptoNight-Heavy (SUMO) */
+    CRYPTONIGHT,       /* CryptoNight (Monero) */
+    CRYPTONIGHT_LITE,  /* CryptoNight-Lite (AEON) */
+    CRYPTONIGHT_HEAVY, /* CryptoNight-Heavy (SUMO) */
+};
+
+
+enum AlgoVariant {
+    AV_AUTO,
+    AV_SINGLE,
+    AV_DOUBLE,
+    AV_SINGLE_SOFT,
+    AV_DOUBLE_SOFT,
+    AV_TRIPLE,
+    AV_QUAD,
+    AV_PENTA,
+    AV_TRIPLE_SOFT,
+    AV_QUAD_SOFT,
+    AV_PENTA_SOFT,
+    AV_MAX
 };
 
 
 enum Variant {
-    VARIANT_AUTO = -1,
-    VARIANT_NONE = 0,
-    VARIANT_V1   = 1
+    VARIANT_AUTO = -1, // Autodetect
+    VARIANT_NONE = 0,  // Original CryptoNight
+    VARIANT_V1   = 1   // Monero v7 PoW
 };
 
+
 } /* xmrig */