You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

287 line
7.8 KiB

  1. //
  2. // Lol Engine
  3. //
  4. // Copyright © 2010—2015 Sam Hocevar <sam@hocevar.net>
  5. // © 2014—2015 Benjamin “Touky” Huet <huet.benjamin@gmail.com>
  6. //
  7. // Lol Engine is free software. It comes without any warranty, to
  8. // the extent permitted by applicable law. You can redistribute it
  9. // and/or modify it under the terms of the Do What the Fuck You Want
  10. // to Public License, Version 2, as published by the WTFPL Task Force.
  11. // See http://www.wtfpl.net/ for more details.
  12. //
  13. #include <lol/engine-internal.h>
  14. namespace lol
  15. {
  16. //BaseThreadManager -----------------------------------------------------------
  17. BaseThreadManager::BaseThreadManager(int thread_max) : BaseThreadManager(thread_max, thread_max)
  18. { }
  19. BaseThreadManager::BaseThreadManager(int thread_max, int thread_min)
  20. {
  21. Setup(thread_max, thread_min);
  22. }
  23. BaseThreadManager::~BaseThreadManager()
  24. {
  25. Stop();
  26. }
  27. //Base Setup ------------------------------------------------------------------
  28. void BaseThreadManager::Setup(int thread_max)
  29. {
  30. Setup(thread_max, thread_max);
  31. }
  32. void BaseThreadManager::Setup(int thread_max, int thread_min)
  33. {
  34. #if LOL_FEATURE_THREADS
  35. m_thread_max = thread_max;
  36. m_thread_min = thread_min;
  37. #endif //LOL_FEATURE_THREADS
  38. }
  39. //Initialize, Ticker::Ref and start the thread --------------------------------
  40. bool BaseThreadManager::Start()
  41. {
  42. #if LOL_FEATURE_THREADS
  43. ASSERT(!!m_thread_max, "Thread count shouldn't be zero");
  44. if (m_threads.count() > 0)
  45. return false;
  46. //Add minimum threads
  47. m_threads.reserve(m_thread_max);
  48. //AddThreads(m_thread_min);
  49. AdjustThreadCount(m_thread_min);
  50. #endif //LOL_FEATURE_THREADS
  51. return true;
  52. }
  53. //Stop the threads ------------------------------------------------------------
  54. bool BaseThreadManager::Stop()
  55. {
  56. #if LOL_FEATURE_THREADS
  57. if (m_threads.count() <= 0)
  58. return false;
  59. //End all threads
  60. //RemoveThreads((int)m_threads.count());
  61. AdjustThreadCount(0);
  62. CleanAddedThread(true);
  63. CleanRemovedThread(true);
  64. #endif //LOL_FEATURE_THREADS
  65. return true;
  66. }
  67. //Work stuff ------------------------------------------------------------------
  68. bool BaseThreadManager::AddThreadWork(ThreadJob* job)
  69. {
  70. return m_jobqueue.try_push(job);
  71. }
  72. //-----------------------------------------------------------------------------
  73. void BaseThreadManager::AdjustThreadCount(int count)
  74. {
  75. #if LOL_FEATURE_THREADS
  76. int actual_count = (int)m_threads.count() - m_thread_removed;
  77. int diff = count - actual_count;
  78. for (int i = 0; i < lol::abs(diff); i++)
  79. {
  80. if (diff > 0)
  81. {
  82. //Spawn worker threads and ...
  83. m_threads << new thread(std::bind(&BaseThreadManager::BaseThreadWork, this, std::placeholders::_1));
  84. m_thread_added++;
  85. m_thread_added_timer.Reset();
  86. }
  87. else
  88. {
  89. //Signal worker threads for completion and ...
  90. m_jobqueue.push(new ThreadJob(ThreadJobType::THREAD_STOP));
  91. m_thread_removed++;
  92. m_thread_removed_timer.Reset();
  93. }
  94. }
  95. #endif //LOL_FEATURE_THREADS
  96. }
  97. //-----------------------------------------------------------------------------
  98. void BaseThreadManager::CleanAddedThread(bool wait)
  99. {
  100. #if LOL_FEATURE_THREADS
  101. //... Wait for their readiness.
  102. while (m_thread_added > 0)
  103. {
  104. thread* inst = nullptr;
  105. bool found = false;
  106. if (wait)
  107. found = !!(inst = m_spawnqueue.pop());
  108. else
  109. found = m_spawnqueue.try_pop(inst);
  110. if (found)
  111. m_thread_added--;
  112. else
  113. break;
  114. }
  115. //Assert if spawning took too much time
  116. //ASSERT(!(m_thread_added > 0 && m_thread_added_timer.Poll() > 5.f));
  117. #endif //LOL_FEATURE_THREADS
  118. }
  119. //-----------------------------------------------------------------------------
  120. void BaseThreadManager::CleanRemovedThread(bool wait)
  121. {
  122. #if LOL_FEATURE_THREADS
  123. //... Wait for them to quit.
  124. while (m_thread_removed > 0)
  125. {
  126. thread* inst = nullptr;
  127. bool found = false;
  128. if (wait)
  129. found = !!(inst = m_donequeue.pop());
  130. else
  131. found = m_donequeue.try_pop(inst);
  132. if (found)
  133. {
  134. m_thread_removed--;
  135. m_threads.remove_swap_item(inst);
  136. delete inst;
  137. }
  138. else
  139. break;
  140. }
  141. //Assert if stopping took too much time
  142. //ASSERT(!(m_thread_removed > 0 && m_thread_removed_timer.Poll() > 5.f));
  143. #endif //LOL_FEATURE_THREADS
  144. }
  145. //-----------------------------------------------------------------------------
  146. int BaseThreadManager::GetDispatchCount()
  147. {
  148. return (int)m_job_dispatch.count();
  149. }
  150. int BaseThreadManager::GetDispatchedCount()
  151. {
  152. return m_job_dispatched;
  153. }
  154. //-----------------------------------------------------------------------------
  155. void BaseThreadManager::DispatchJob(ThreadJob* job)
  156. {
  157. if (job) m_job_dispatch << job;
  158. }
  159. void BaseThreadManager::DispatchJob(array<ThreadJob*> const& jobs)
  160. {
  161. m_job_dispatch += jobs;
  162. }
  163. //-----------------------------------------------------------------------------
  164. bool BaseThreadManager::FetchResult(array<ThreadJob*>& results)
  165. {
  166. ThreadJob* result;
  167. while (m_resultqueue.try_pop(result))
  168. results << result;
  169. return results.count() > 0;
  170. }
  171. //Base thread work function ---------------------------------------------------
  172. void BaseThreadManager::BaseThreadWork(thread* inst)
  173. {
  174. ThreadJob* job = nullptr;
  175. #if LOL_FEATURE_THREADS
  176. //Register that the thread has started
  177. m_statusqueue.push(ThreadStatus::THREAD_STARTED);
  178. m_spawnqueue.push(inst);
  179. for (;;)
  180. #else //LOL_FEATURE_THREADS
  181. while (m_jobqueue.try_pop(job))
  182. #endif
  183. {
  184. #if LOL_FEATURE_THREADS
  185. //Retrieve a job
  186. job = m_jobqueue.pop();
  187. //Stop thread
  188. if (job->GetJobType() == ThreadJobType::THREAD_STOP)
  189. {
  190. delete job;
  191. break;
  192. }
  193. //Or work
  194. else
  195. #endif //LOL_FEATURE_THREADS
  196. if (*job == ThreadJobType::WORK_TODO)
  197. {
  198. #if LOL_FEATURE_THREADS
  199. m_statusqueue.push(ThreadStatus::THREAD_WORKING);
  200. #endif //LOL_FEATURE_THREADS
  201. if (job->DoWork())
  202. job->SetJobType(ThreadJobType::WORK_SUCCEEDED);
  203. else
  204. job->SetJobType(ThreadJobType::WORK_FAILED);
  205. m_resultqueue.push(job);
  206. #if LOL_FEATURE_THREADS
  207. m_statusqueue.push(ThreadStatus::THREAD_IDLE);
  208. #endif //LOL_FEATURE_THREADS
  209. }
  210. }
  211. #if LOL_FEATURE_THREADS
  212. //Register that the thread has stopped
  213. m_statusqueue.push(ThreadStatus::THREAD_STOPPED);
  214. m_donequeue.push(inst);
  215. #endif //LOL_FEATURE_THREADS
  216. }
  217. //-----------------------------------------------------------------------------
  218. void BaseThreadManager::TickGame(float seconds)
  219. {
  220. Entity::TickGame(seconds);
  221. //Start if needed
  222. Start();
  223. //Dispatch work task
  224. while (m_job_dispatch.count() > 0 && AddThreadWork(m_job_dispatch[0]))
  225. {
  226. m_job_dispatch.remove(0);
  227. //Keep track of added jobs
  228. m_job_dispatched++;
  229. }
  230. //Execute one task per frame if thread are not available
  231. #if !defined(LOL_FEATURE_THREADS) || !LOL_FEATURE_THREADS
  232. BaseThreadWork(nullptr);
  233. #endif // !LOL_FEATURE_THREADS
  234. array<ThreadJob*> result;
  235. //Fetch and treat results
  236. if (FetchResult(result))
  237. {
  238. for (int i = 0; i < result.count(); i++)
  239. {
  240. ThreadJob* job = result[i];
  241. TreatResult(job);
  242. //Remove job from count as it has been treated
  243. m_job_dispatched--;
  244. }
  245. }
  246. //Resize thread count if needed
  247. #if LOL_FEATURE_THREADS
  248. ThreadStatus status;
  249. while (m_statusqueue.try_pop(status))
  250. m_thread_active += status == ThreadStatus::THREAD_IDLE ? -1 : +1;
  251. AdjustThreadCount(lol::clamp(m_job_dispatched, m_thread_min, m_thread_max));
  252. CleanAddedThread();
  253. CleanRemovedThread();
  254. #endif //LOL_FEATURE_THREADS
  255. }
  256. } /* namespace lol */