text: /* UlordRig
   * 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-2017 XMRig       <support@xmrig.com>
   * Copyright 2018      UlordRig    <https://github.com/UlordChain/ulordrig>
   *
   *
   *   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 <cmath>
  
  
  #include "api/Api.h"
  #include "interfaces/IJobResultListener.h"
  #include "Mem.h"
  #include "Options.h"
  #include "workers/DoubleWorker.h"
  #include "workers/Handle.h"
  #include "workers/Hashrate.h"
  #include "workers/SingleWorker.h"
  #include "workers/Workers.h"
  
  
  bool Workers::m_active = false;
  bool Workers::m_enabled = true;
  Hashrate *Workers::m_hashrate = nullptr;
  IJobResultListener *Workers::m_listener = nullptr;
  Job Workers::m_job;
  std::atomic<int> Workers::m_paused;
  std::atomic<uint64_t> Workers::m_sequence;
  std::list<JobResult> Workers::m_queue;
  std::vector<Handle*> Workers::m_workers;
  uint64_t Workers::m_ticks = 0;
  uv_async_t Workers::m_async;
  uv_mutex_t Workers::m_mutex;
  uv_rwlock_t Workers::m_rwlock;
  uv_timer_t Workers::m_timer;
  
  
  Job Workers::job()
  {
      uv_rwlock_rdlock(&m_rwlock);
      Job job = m_job;
      uv_rwlock_rdunlock(&m_rwlock);
  
      return job;
  }
  
  
  void Workers::printHashrate(bool detail)
  {
      m_hashrate->print();
  }
  
  double Workers::ret_Hashrate()
  {
     return  m_hashrate->calc(2500);
  }
  
  void Workers::setEnabled(bool enabled)
  {
      if (m_enabled == enabled) {
          return;
      }
  
      m_enabled = enabled;
      if (!m_active) {
          return;
      }
  
      m_paused = enabled ? 0 : 1;
      m_sequence++;
  }
  
  
  void Workers::setJob(const Job &job, bool donate)
  {
      uv_rwlock_wrlock(&m_rwlock);
      m_job = job;
  
      if (donate) {
          m_job.setPoolId(-1);
      }
      uv_rwlock_wrunlock(&m_rwlock);
  
      m_active = true;
      if (!m_enabled) {
          return;
      }
  
      m_sequence++;
      m_paused = 0;
  }
  
  
  void Workers::start(int64_t affinity, int priority)
  {
      const int threads =  Mem::threads();
      m_hashrate = new Hashrate(threads);
  
      uv_mutex_init(&m_mutex);
      uv_rwlock_init(&m_rwlock);
  
      m_sequence = 1;
      m_paused   = 1;
  
      uv_async_init(uv_default_loop(), &m_async, Workers::onResult);
      uv_timer_init(uv_default_loop(), &m_timer);
      uv_timer_start(&m_timer, Workers::onTick, 500, 500);
  
      for (int i = 0; i < threads; ++i) {
          Handle *handle = new Handle(i, threads, affinity, priority);
          m_workers.push_back(handle);
          handle->start(Workers::onReady);
      }
  }
  
  
  void Workers::stop()
  {
      uv_timer_stop(&m_timer);
      m_hashrate->stop();
  
      uv_close(reinterpret_cast<uv_handle_t*>(&m_async), nullptr);
      m_paused   = 0;
      m_sequence = 0;
  
      for (size_t i = 0; i < m_workers.size(); ++i) {
          m_workers[i]->join();
      }
  }
  
  
  void Workers::submit(const JobResult &result)
  {
      uv_mutex_lock(&m_mutex);
      m_queue.push_back(result);
      uv_mutex_unlock(&m_mutex);
  
      uv_async_send(&m_async);
  }
  
  
  void Workers::onReady(void *arg)
  {
      auto handle = static_cast<Handle*>(arg);
      if (/*Mem::isDoubleHash()*/false) {
          handle->setWorker(new DoubleWorker(handle));
      }
      else {
          handle->setWorker(new SingleWorker(handle));
      }
  
      handle->worker()->start();
  }
  
  
  void Workers::onResult(uv_async_t *handle)
  {
      std::list<JobResult> results;
  
      uv_mutex_lock(&m_mutex);
      while (!m_queue.empty()) {
          results.push_back(std::move(m_queue.front()));
          m_queue.pop_front();
      }
      uv_mutex_unlock(&m_mutex);
  
      for (auto result : results) {
          m_listener->onJobResult(result);
      }
  
      results.clear();
  }
  
  
  void Workers::onTick(uv_timer_t *handle)
  {
      for (Handle *handle : m_workers) {
          if (!handle->worker()) {
              return;
          }
  
          m_hashrate->add(handle->threadId(), handle->worker()->hashCount(), handle->worker()->timestamp());
      }
  
      if ((m_ticks++ & 0xF) == 0)  {
          m_hashrate->updateHighest();
      }
  
  #   ifndef ULORDRIG_NO_API
      Api::tick(m_hashrate);
  #   endif
  }
  