diff --git a/src/backend/common/Thread.h b/src/backend/common/Thread.h
index 3bef53194..ab88441c9 100644
--- a/src/backend/common/Thread.h
+++ b/src/backend/common/Thread.h
@@ -26,10 +26,11 @@
 #define XMRIG_THREAD_H
 
 
-#include <thread>
-
-
 #include "backend/common/interfaces/IWorker.h"
+#include "base/tools/Object.h"
+
+
+#include <thread>
 
 
 namespace xmrig {
@@ -42,6 +43,8 @@ template<class T>
 class Thread
 {
 public:
+    XMRIG_DISABLE_COPY_MOVE_DEFAULT(Thread)
+
     inline Thread(IBackend *backend, size_t id, const T &config) : m_id(id), m_config(config), m_backend(backend) {}
     inline ~Thread() { m_thread.join(); delete m_worker; }
 
diff --git a/src/backend/common/Workers.cpp b/src/backend/common/Workers.cpp
index 2d0729f32..2c90dfe02 100644
--- a/src/backend/common/Workers.cpp
+++ b/src/backend/common/Workers.cpp
@@ -133,7 +133,7 @@ void xmrig::Workers<T>::tick(uint64_t)
 
     for (Thread<T> *handle : m_workers) {
         if (!handle->worker()) {
-            return;
+            continue;
         }
 
         d_ptr->hashrate->add(handle->id(), handle->worker()->hashCount(), handle->worker()->timestamp());
@@ -156,17 +156,21 @@ void xmrig::Workers<T>::onReady(void *arg)
     auto handle = static_cast<Thread<T>* >(arg);
 
     IWorker *worker = create(handle);
+    assert(worker != nullptr);
+
     if (!worker || !worker->selfTest()) {
         LOG_ERR("%s " RED("thread ") RED_BOLD("#%zu") RED(" self-test failed"), T::tag(), worker->id());
 
+        handle->backend()->start(worker, false);
         delete worker;
+
         return;
     }
 
     assert(handle->backend() != nullptr);
 
     handle->setWorker(worker);
-    handle->backend()->start(worker);
+    handle->backend()->start(worker, true);
 }
 
 
diff --git a/src/backend/common/Workers.h b/src/backend/common/Workers.h
index bb35157a4..2688a1e39 100644
--- a/src/backend/common/Workers.h
+++ b/src/backend/common/Workers.h
@@ -29,6 +29,7 @@
 
 #include "backend/common/Thread.h"
 #include "backend/cpu/CpuLaunchData.h"
+#include "base/tools/Object.h"
 
 
 #ifdef XMRIG_FEATURE_OPENCL
@@ -47,6 +48,8 @@ template<class T>
 class Workers
 {
 public:
+    XMRIG_DISABLE_COPY_MOVE(Workers)
+
     Workers();
     ~Workers();
 
diff --git a/src/backend/common/interfaces/IBackend.h b/src/backend/common/interfaces/IBackend.h
index b284d1881..f90732293 100644
--- a/src/backend/common/interfaces/IBackend.h
+++ b/src/backend/common/interfaces/IBackend.h
@@ -56,7 +56,7 @@ public:
     virtual void prepare(const Job &nextJob)                            = 0;
     virtual void printHashrate(bool details)                            = 0;
     virtual void setJob(const Job &job)                                 = 0;
-    virtual void start(IWorker *worker)                                 = 0;
+    virtual void start(IWorker *worker, bool ready)                     = 0;
     virtual void stop()                                                 = 0;
     virtual void tick(uint64_t ticks)                                   = 0;
 
diff --git a/src/backend/common/interfaces/IWorker.h b/src/backend/common/interfaces/IWorker.h
index 0d7fe1d26..9dd1274d3 100644
--- a/src/backend/common/interfaces/IWorker.h
+++ b/src/backend/common/interfaces/IWorker.h
@@ -26,8 +26,8 @@
 #define XMRIG_IWORKER_H
 
 
-#include <stdint.h>
-#include <stddef.h>
+#include <cstdint>
+#include <cstddef>
 
 
 namespace xmrig {
@@ -44,6 +44,7 @@ public:
     virtual bool selfTest()                         = 0;
     virtual const VirtualMemory *memory() const     = 0;
     virtual size_t id() const                       = 0;
+    virtual size_t intensity() const                = 0;
     virtual uint64_t hashCount() const              = 0;
     virtual uint64_t timestamp() const              = 0;
     virtual void start()                            = 0;
diff --git a/src/backend/cpu/CpuBackend.cpp b/src/backend/cpu/CpuBackend.cpp
index 22ae63d87..de2e61103 100644
--- a/src/backend/cpu/CpuBackend.cpp
+++ b/src/backend/cpu/CpuBackend.cpp
@@ -60,7 +60,7 @@ namespace xmrig {
 extern template class Threads<CpuThreads>;
 
 
-static const char *tag      = CYAN_BG_BOLD(" cpu ");
+static const char *tag      = CYAN_BG_BOLD(WHITE_BOLD_S " cpu ");
 static const String kType   = "cpu";
 static std::mutex mutex;
 
@@ -80,38 +80,51 @@ public:
         m_memory    = memory;
         m_pages     = 0;
         m_started   = 0;
+        m_errors    = 0;
         m_threads   = threads.size();
         m_ways      = 0;
         m_ts        = Chrono::steadyMSecs();
-
-        for (const CpuLaunchData &data : threads) {
-            m_ways += data.intensity;
-        }
     }
 
-    inline bool started(const std::pair<size_t, size_t> &hugePages)
+    inline bool started(IWorker *worker, bool ready)
     {
-        m_started++;
-        m_hugePages += hugePages.first;
-        m_pages     += hugePages.second;
+        if (ready) {
+            auto hugePages = worker->memory()->hugePages();
 
-        return m_started == m_threads;
+            m_started++;
+            m_hugePages += hugePages.first;
+            m_pages     += hugePages.second;
+            m_ways      += worker->intensity();
+        }
+        else {
+            m_errors++;
+        }
+
+        return (m_started + m_errors) == m_threads;
     }
 
     inline void print() const
     {
-        LOG_INFO("%s" GREEN_BOLD(" READY") " threads " CYAN_BOLD("%zu(%zu)") " huge pages %s%zu/%zu %1.0f%%\x1B[0m memory " CYAN_BOLD("%zu KB") BLACK_BOLD(" (%" PRIu64 " ms)"),
+        if (m_started == 0) {
+            LOG_ERR("%s " RED_BOLD("disabled") YELLOW(" (failed to start threads)"), tag);
+
+            return;
+        }
+
+        LOG_INFO("%s" GREEN_BOLD(" READY") " threads %s%zu/%zu (%zu)" CLEAR " huge pages %s%zu/%zu %1.0f%%" CLEAR " memory " CYAN_BOLD("%zu KB") BLACK_BOLD(" (%" PRIu64 " ms)"),
                  tag,
-                 m_threads, m_ways,
+                 m_errors == 0 ? CYAN_BOLD_S : YELLOW_BOLD_S,
+                 m_started, m_threads, m_ways,
                  (m_hugePages == m_pages ? GREEN_BOLD_S : (m_hugePages == 0 ? RED_BOLD_S : YELLOW_BOLD_S)),
                  m_hugePages, m_pages,
                  m_hugePages == 0 ? 0.0 : static_cast<double>(m_hugePages) / m_pages * 100.0,
-                 m_ways * m_memory / 1024,
+                 memory() / 1024,
                  Chrono::steadyMSecs() - m_ts
                  );
     }
 
 private:
+    size_t m_errors       = 0;
     size_t m_hugePages    = 0;
     size_t m_memory       = 0;
     size_t m_pages        = 0;
@@ -322,17 +335,19 @@ void xmrig::CpuBackend::setJob(const Job &job)
 }
 
 
-void xmrig::CpuBackend::start(IWorker *worker)
+void xmrig::CpuBackend::start(IWorker *worker, bool ready)
 {
     mutex.lock();
 
-    if (d_ptr->status.started(worker->memory()->hugePages())) {
+    if (d_ptr->status.started(worker, ready)) {
         d_ptr->status.print();
     }
 
     mutex.unlock();
 
-    worker->start();
+    if (ready) {
+        worker->start();
+    }
 }
 
 
diff --git a/src/backend/cpu/CpuBackend.h b/src/backend/cpu/CpuBackend.h
index 9d6d85ac4..d5d9fbf6c 100644
--- a/src/backend/cpu/CpuBackend.h
+++ b/src/backend/cpu/CpuBackend.h
@@ -26,10 +26,11 @@
 #define XMRIG_CPUBACKEND_H
 
 
-#include <utility>
-
-
 #include "backend/common/interfaces/IBackend.h"
+#include "base/tools/Object.h"
+
+
+#include <utility>
 
 
 namespace xmrig {
@@ -43,6 +44,8 @@ class Miner;
 class CpuBackend : public IBackend
 {
 public:
+    XMRIG_DISABLE_COPY_MOVE_DEFAULT(CpuBackend)
+
     CpuBackend(Controller *controller);
     ~CpuBackend() override;
 
@@ -55,7 +58,7 @@ protected:
     void prepare(const Job &nextJob) override;
     void printHashrate(bool details) override;
     void setJob(const Job &job) override;
-    void start(IWorker *worker) override;
+    void start(IWorker *worker, bool ready) override;
     void stop() override;
     void tick(uint64_t ticks) override;
 
diff --git a/src/backend/cpu/CpuWorker.h b/src/backend/cpu/CpuWorker.h
index 02ce0826c..31819de97 100644
--- a/src/backend/cpu/CpuWorker.h
+++ b/src/backend/cpu/CpuWorker.h
@@ -54,6 +54,7 @@ protected:
     void start() override;
 
     inline const VirtualMemory *memory() const override { return m_memory; }
+    inline size_t intensity() const override            { return N; }
 
 private:
     inline cn_hash_fun fn(const Algorithm &algorithm) const { return CnHash::fn(algorithm, m_av, m_assembly); }
diff --git a/src/backend/opencl/OclBackend.cpp b/src/backend/opencl/OclBackend.cpp
index 614aa8d23..2bce87b84 100644
--- a/src/backend/opencl/OclBackend.cpp
+++ b/src/backend/opencl/OclBackend.cpp
@@ -72,12 +72,19 @@ static void printDisabled(const char *reason)
 struct OclLaunchStatus
 {
 public:
-    inline bool started()               { m_started++; return m_started == m_threads; }
-    inline size_t threads() const       { return m_threads; }
+    inline size_t threads() const { return m_threads; }
+
+    inline bool started(bool ready)
+    {
+        ready ? m_started++ : m_errors++;
+
+        return (m_started + m_errors) == m_threads;
+    }
 
     inline void start(size_t threads)
     {
         m_started        = 0;
+        m_errors         = 0;
         m_threads        = threads;
         m_ts             = Chrono::steadyMSecs();
         OclWorker::ready = false;
@@ -85,14 +92,23 @@ public:
 
     inline void print() const
     {
-        LOG_INFO("%s" GREEN_BOLD(" READY") " threads " CYAN_BOLD("%zu") BLACK_BOLD(" (%" PRIu64 " ms)"),
+        if (m_started == 0) {
+            LOG_ERR("%s " RED_BOLD("disabled") YELLOW(" (failed to start threads)"), tag);
+
+            return;
+        }
+
+        LOG_INFO("%s" GREEN_BOLD(" READY") " threads " "%s%zu/%zu" BLACK_BOLD(" (%" PRIu64 " ms)"),
                  tag,
+                 m_errors == 0 ? CYAN_BOLD_S : YELLOW_BOLD_S,
+                 m_started,
                  m_threads,
                  Chrono::steadyMSecs() - m_ts
                  );
     }
 
 private:
+    size_t m_errors     = 0;
     size_t m_started    = 0;
     size_t m_threads    = 0;
     uint64_t m_ts       = 0;
@@ -319,11 +335,11 @@ void xmrig::OclBackend::setJob(const Job &job)
 }
 
 
-void xmrig::OclBackend::start(IWorker *worker)
+void xmrig::OclBackend::start(IWorker *worker, bool ready)
 {
     mutex.lock();
 
-    if (d_ptr->status.started()) {
+    if (d_ptr->status.started(ready)) {
         d_ptr->status.print();
 
         OclWorker::ready = true;
@@ -331,7 +347,9 @@ void xmrig::OclBackend::start(IWorker *worker)
 
     mutex.unlock();
 
-    worker->start();
+    if (ready) {
+        worker->start();
+    }
 }
 
 
diff --git a/src/backend/opencl/OclBackend.h b/src/backend/opencl/OclBackend.h
index e6ba092ee..ade560baf 100644
--- a/src/backend/opencl/OclBackend.h
+++ b/src/backend/opencl/OclBackend.h
@@ -62,7 +62,7 @@ protected:
     void prepare(const Job &nextJob) override;
     void printHashrate(bool details) override;
     void setJob(const Job &job) override;
-    void start(IWorker *worker) override;
+    void start(IWorker *worker, bool ready) override;
     void stop() override;
     void tick(uint64_t ticks) override;
 
diff --git a/src/backend/opencl/OclWorker.cpp b/src/backend/opencl/OclWorker.cpp
index 4247fb786..b4132e3dd 100644
--- a/src/backend/opencl/OclWorker.cpp
+++ b/src/backend/opencl/OclWorker.cpp
@@ -137,6 +137,12 @@ bool xmrig::OclWorker::selfTest()
 }
 
 
+size_t xmrig::OclWorker::intensity() const
+{
+    return m_runner ? m_runner->intensity() : 0;
+}
+
+
 void xmrig::OclWorker::start()
 {
     cl_uint results[0x100];
diff --git a/src/backend/opencl/OclWorker.h b/src/backend/opencl/OclWorker.h
index e28c784dc..93de8afd7 100644
--- a/src/backend/opencl/OclWorker.h
+++ b/src/backend/opencl/OclWorker.h
@@ -56,6 +56,7 @@ public:
 
 protected:
     bool selfTest() override;
+    size_t intensity() const override;
     void start() override;
 
 private:
diff --git a/src/backend/opencl/interfaces/IOclRunner.h b/src/backend/opencl/interfaces/IOclRunner.h
index c1718a2a7..0b47bcd29 100644
--- a/src/backend/opencl/interfaces/IOclRunner.h
+++ b/src/backend/opencl/interfaces/IOclRunner.h
@@ -51,18 +51,19 @@ public:
     IOclRunner()          = default;
     virtual ~IOclRunner() = default;
 
-    virtual void run(uint32_t nonce, uint32_t *hashOutput)  = 0;
-    virtual void set(const Job &job, uint8_t *blob)         = 0;
     virtual cl_context ctx() const                          = 0;
     virtual const Algorithm &algorithm() const              = 0;
     virtual const char *buildOptions() const                = 0;
     virtual const char *deviceKey() const                   = 0;
     virtual const char *source() const                      = 0;
     virtual const OclLaunchData &data() const               = 0;
+    virtual size_t intensity() const                        = 0;
     virtual size_t threadId() const                         = 0;
     virtual uint32_t deviceIndex() const                    = 0;
     virtual void build()                                    = 0;
     virtual void init()                                     = 0;
+    virtual void run(uint32_t nonce, uint32_t *hashOutput)  = 0;
+    virtual void set(const Job &job, uint8_t *blob)         = 0;
 
 protected:
     virtual size_t bufferSize() const                       = 0;
diff --git a/src/backend/opencl/runners/OclBaseRunner.h b/src/backend/opencl/runners/OclBaseRunner.h
index ea115957b..558d68077 100644
--- a/src/backend/opencl/runners/OclBaseRunner.h
+++ b/src/backend/opencl/runners/OclBaseRunner.h
@@ -55,6 +55,7 @@ protected:
     inline const char *deviceKey() const override       { return m_deviceKey.c_str(); }
     inline const char *source() const override          { return m_source; }
     inline const OclLaunchData &data() const override   { return m_data; }
+    inline size_t intensity() const override            { return m_intensity; }
     inline size_t threadId() const override             { return m_threadId; }
 
     size_t bufferSize() const override;