text: ﻿#include "stdafx.h"
  #include "Async.h"
  
  Async::Async(std::function<void()> job, Sync* sync)
      : job_(job), p_Sync(sync)
  {
      if (p_Sync)
          p_Sync->Inc();
  
      {
          std::unique_lock<std::mutex> lock(s_Mutex);
  
          while (s_ActiveThreads >= s_MaxThreads)
              s_Condition.wait(lock);
  
          s_ActiveThreads += 1;
      }
  
      p_Thread = new std::thread([this]()
      {
          job_();
  
          {
              std::unique_lock<std::mutex> lock(s_Mutex);
              s_ActiveThreads -= 1;
          }
  
          s_Condition.notify_one();
  
          if (p_Sync)
              p_Sync->Dec();
      });
  }
  
  Async::~Async()
  {
      p_Thread->join();
      delete p_Thread;
  }
  
  void Async::Wait(Sync* sync)
  {
      if (sync->Get() == 0)
          return;
  
      {
          std::unique_lock<std::mutex> lock(s_Mutex);
          s_ActiveThreads -= 1;
      }
      
      s_Condition.notify_one();
      
      sync->Wait();
  
      {
          std::unique_lock<std::mutex> lock(s_Mutex);
          s_Condition.wait(lock, []
          {
              return s_bExiting || (s_ActiveThreads < s_MaxThreads);
          });
  
          s_ActiveThreads += 1;
      }
  }
  
  AsyncPool::~AsyncPool()
  {
      Flush();
  }
  
  void AsyncPool::Flush()
  {
      for(int i = 0; i < pool_.size(); i++)
          delete pool_[i];
      pool_.clear();
  }
  
  void AsyncPool::AddAsyncTask(std::function<void()> job, Sync* sync)
  {
      pool_.push_back(new Async(job, sync));
  }
  
  /// 如果 AsyncPool 不存在，那么就同步执行任务
  void ExecAsyncIfThereIsAPool(AsyncPool *pAsyncPool, std::function<void()> job)
  {
      if (pAsyncPool != nullptr)
      {
          pAsyncPool->AddAsyncTask(job);
      }
      else
      {
          job();
      }
  }
  
  // static 变量定义
  
  int Async::s_ActiveThreads = 0;
  std::mutex Async::s_Mutex;
  std::condition_variable Async::s_Condition;
  bool Async::s_bExiting = false;
  int Async::s_MaxThreads = std::thread::hardware_concurrency();