CmCoreThread.cpp 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253
  1. #include "CmCoreThread.h"
  2. namespace CamelotFramework
  3. {
  4. CoreThread::CoreThread()
  5. : mCoreThreadFunc(nullptr)
  6. , mCoreThreadStarted(false)
  7. , mCoreThreadShutdown(false)
  8. , mCommandQueue(nullptr)
  9. , mMaxCommandNotifyId(0)
  10. , mSyncedCoreAccessor(nullptr)
  11. {
  12. mCoreThreadId = CM_THREAD_CURRENT_ID;
  13. mCommandQueue = cm_new<CommandQueue<CommandQueueSync>>(CM_THREAD_CURRENT_ID, true);
  14. initCoreThread();
  15. }
  16. CoreThread::~CoreThread()
  17. {
  18. // TODO - What if something gets queued between the queued call to destroy_internal and this!?
  19. shutdownCoreThread();
  20. if(mCommandQueue != nullptr)
  21. {
  22. cm_delete(mCommandQueue);
  23. mCommandQueue = nullptr;
  24. }
  25. }
  26. void CoreThread::initCoreThread()
  27. {
  28. #if !CM_FORCE_SINGLETHREADED_RENDERING
  29. mCoreThreadFunc = cm_new<CoreThreadWorkerFunc>(this);
  30. #if CM_THREAD_SUPPORT
  31. CM_THREAD_CREATE(t, *mCoreThreadFunc);
  32. mCoreThread = t;
  33. CM_LOCK_MUTEX_NAMED(mCoreThreadStartMutex, lock);
  34. while(!mCoreThreadStarted)
  35. CM_THREAD_WAIT(mCoreThreadStartCondition, mCoreThreadStartMutex, lock);
  36. #else
  37. CM_EXCEPT(InternalErrorException, "Attempting to start a core thread but Camelot isn't compiled with thread support.");
  38. #endif
  39. #endif
  40. }
  41. void CoreThread::runCoreThread()
  42. {
  43. #if !CM_FORCE_SINGLETHREADED_RENDERING
  44. mCoreThreadId = CM_THREAD_CURRENT_ID;
  45. mSyncedCoreAccessor = cm_new<CoreThreadAccessor<CommandQueueSync>>(CM_THREAD_CURRENT_ID);
  46. {
  47. CM_LOCK_MUTEX(mCoreThreadStartMutex);
  48. mCoreThreadStarted = true;
  49. }
  50. CM_THREAD_NOTIFY_ALL(mCoreThreadStartCondition)
  51. while(true)
  52. {
  53. // Wait until we get some ready commands
  54. Queue<QueuedCommand>::type* commands = nullptr;
  55. {
  56. CM_LOCK_MUTEX_NAMED(mCommandQueueMutex, lock)
  57. while(mCommandQueue->isEmpty())
  58. {
  59. if(mCoreThreadShutdown)
  60. return;
  61. CM_THREAD_WAIT(mCommandReadyCondition, mCommandQueueMutex, lock);
  62. }
  63. commands = mCommandQueue->flush();
  64. }
  65. // Play commands
  66. mCommandQueue->playback(commands, boost::bind(&CoreThread::commandCompletedNotify, this, _1));
  67. }
  68. cm_delete(mSyncedCoreAccessor);
  69. #endif
  70. }
  71. void CoreThread::shutdownCoreThread()
  72. {
  73. #if !CM_FORCE_SINGLETHREADED_RENDERING
  74. {
  75. CM_LOCK_MUTEX(mCommandQueueMutex);
  76. mCoreThreadShutdown = true;
  77. }
  78. // Wake all threads. They will quit after they see the shutdown flag
  79. CM_THREAD_NOTIFY_ALL(mCommandReadyCondition);
  80. mCoreThread->join();
  81. CM_THREAD_DESTROY(mCoreThread);
  82. mCoreThread = nullptr;
  83. mCoreThreadId = CM_THREAD_CURRENT_ID;
  84. if(mCoreThreadFunc != nullptr)
  85. {
  86. cm_delete(mCoreThreadFunc);
  87. mCoreThreadFunc = nullptr;
  88. }
  89. #endif
  90. mCoreThreadStarted = false;
  91. }
  92. CoreAccessorPtr CoreThread::createAccessor()
  93. {
  94. return cm_shared_ptr<CoreThreadAccessor<CommandQueueNoSync>>(CM_THREAD_CURRENT_ID);
  95. }
  96. SyncedCoreAccessor& CoreThread::getSyncedAccessor()
  97. {
  98. return *mSyncedCoreAccessor;
  99. }
  100. AsyncOp CoreThread::queueReturnCommand(boost::function<void(AsyncOp&)> commandCallback, bool blockUntilComplete)
  101. {
  102. AsyncOp op;
  103. if(CM_THREAD_CURRENT_ID == getCoreThreadId())
  104. {
  105. commandCallback(op); // Execute immediately
  106. return op;
  107. }
  108. UINT32 commandId = -1;
  109. {
  110. CM_LOCK_MUTEX(mCommandQueueMutex);
  111. if(blockUntilComplete)
  112. {
  113. commandId = mMaxCommandNotifyId++;
  114. op = mCommandQueue->queueReturn(commandCallback, true, commandId);
  115. }
  116. else
  117. op = mCommandQueue->queueReturn(commandCallback);
  118. }
  119. CM_THREAD_NOTIFY_ALL(mCommandReadyCondition);
  120. if(blockUntilComplete)
  121. blockUntilCommandCompleted(commandId);
  122. return op;
  123. }
  124. void CoreThread::queueCommand(boost::function<void()> commandCallback, bool blockUntilComplete)
  125. {
  126. if(CM_THREAD_CURRENT_ID == getCoreThreadId())
  127. {
  128. commandCallback(); // Execute immediately
  129. return;
  130. }
  131. UINT32 commandId = -1;
  132. {
  133. CM_LOCK_MUTEX(mCommandQueueMutex);
  134. if(blockUntilComplete)
  135. {
  136. commandId = mMaxCommandNotifyId++;
  137. mCommandQueue->queue(commandCallback, true, commandId);
  138. }
  139. else
  140. mCommandQueue->queue(commandCallback);
  141. }
  142. CM_THREAD_NOTIFY_ALL(mCommandReadyCondition);
  143. if(blockUntilComplete)
  144. blockUntilCommandCompleted(commandId);
  145. }
  146. void CoreThread::blockUntilCommandCompleted(UINT32 commandId)
  147. {
  148. #if !CM_FORCE_SINGLETHREADED_RENDERING
  149. CM_LOCK_MUTEX_NAMED(mCommandNotifyMutex, lock);
  150. while(true)
  151. {
  152. // TODO - This might be causing a deadlock in Release mode. I'm thinking because mCommandsCompleted isn't marked as volatile.
  153. // Check if our command id is in the completed list
  154. auto iter = mCommandsCompleted.begin();
  155. for(; iter != mCommandsCompleted.end(); ++iter)
  156. {
  157. if(*iter == commandId)
  158. break;
  159. }
  160. if(iter != mCommandsCompleted.end())
  161. {
  162. mCommandsCompleted.erase(iter);
  163. break;
  164. }
  165. CM_THREAD_WAIT(mCommandCompleteCondition, mCommandNotifyMutex, lock);
  166. }
  167. #endif
  168. }
  169. void CoreThread::commandCompletedNotify(UINT32 commandId)
  170. {
  171. {
  172. CM_LOCK_MUTEX(mCommandNotifyMutex);
  173. mCommandsCompleted.push_back(commandId);
  174. }
  175. CM_THREAD_NOTIFY_ALL(mCommandCompleteCondition);
  176. }
  177. CoreThread& gCoreThread()
  178. {
  179. return CoreThread::instance();
  180. }
  181. void throwIfNotCoreThread()
  182. {
  183. #if !CM_FORCE_SINGLETHREADED_RENDERING
  184. if(CM_THREAD_CURRENT_ID != CoreThread::instance().getCoreThreadId())
  185. CM_EXCEPT(InternalErrorException, "This method can only be accessed from the core thread.");
  186. #endif
  187. }
  188. /************************************************************************/
  189. /* THREAD WORKER */
  190. /************************************************************************/
  191. CoreThread::CoreThreadWorkerFunc::CoreThreadWorkerFunc(CoreThread* owner)
  192. :mOwner(owner)
  193. {
  194. assert(mOwner != nullptr);
  195. }
  196. void CoreThread::CoreThreadWorkerFunc::operator()()
  197. {
  198. mOwner->runCoreThread();
  199. }
  200. }