CmCoreThread.cpp 6.0 KB

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