CmCoreThread.cpp 5.9 KB

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