BsCoreThread.h 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218
  1. #pragma once
  2. #include "BsCorePrerequisites.h"
  3. #include "BsModule.h"
  4. #include "BsCommandQueue.h"
  5. #include "BsCoreThreadAccessor.h"
  6. #include "BsThreadPool.h"
  7. namespace BansheeEngine
  8. {
  9. /** @addtogroup CoreThread
  10. * @{
  11. */
  12. /** @cond INTERNAL */
  13. /**
  14. * Manager for the core thread. Takes care of starting, running, queuing commands and shutting down the core thread.
  15. *
  16. * @note
  17. * How threading works:
  18. * - This class contains a queue which is filled by commands from other threads via queueCommand() and queueReturnCommand()
  19. * - Commands are executed on the core thread as soon as they are queued (if core thread is not busy with previous commands)
  20. * - Core thread accessors are helpers for queuing commands. They perform better than queuing each command directly
  21. * using queueCommand() or queueReturnCommand().
  22. * - Accessors contain a command queue of their own, and queuing commands in them will not automatically start
  23. * executing the commands like with queueCommand or queueReturnCommand. Instead you must manually call
  24. * submitAccessors() when you are ready to send their commands to the core thread. Sending commands "in bulk" like
  25. * this is what makes them faster than directly queuing commands.
  26. * - Synced accessor is a special type of accessor which may be accessed from any thread. Its commands are always
  27. * executed after all other non-synced accessors. It is primarily useful when multiple threads are managing the same
  28. * resource and you must ensure proper order of operations. You should use normal accessors whenever possible as
  29. * synced accessors involve potentially slow synchronization operations.
  30. */
  31. class BS_CORE_EXPORT CoreThread : public Module<CoreThread>
  32. {
  33. /** Contains data about an accessor for a specific thread. */
  34. struct AccessorContainer
  35. {
  36. CoreAccessorPtr accessor;
  37. bool isMain;
  38. };
  39. /** Wrapper for the thread-local variable because MSVC can't deal with a thread-local variable marked with dllimport or dllexport,
  40. * and we cannot use per-member dllimport/dllexport specifiers because Module's members will then not be exported and its static
  41. * members will not have external linkage. */
  42. struct AccessorData
  43. {
  44. static BS_THREADLOCAL AccessorContainer* current;
  45. };
  46. public:
  47. CoreThread();
  48. ~CoreThread();
  49. /** Returns the id of the core thread. */
  50. BS_THREAD_ID_TYPE getCoreThreadId() { return mCoreThreadId; }
  51. /**
  52. * Creates or retrieves an accessor that you can use for executing commands on the core thread from a non-core thread.
  53. * The accessor will be bound to the thread you call this method on.
  54. *
  55. * @note
  56. * Accessors contain their own command queue and their commands will only start to get executed once that queue is
  57. * submitted to the core thread via submitAccessors() method.
  58. */
  59. CoreAccessorPtr getAccessor();
  60. /**
  61. * Retrieves an accessor that you can use for executing commands on the core thread from a non-core thread. There is
  62. * only one synchronized accessor and you may access it from any thread you wish. Note however that it is much more
  63. * efficient to retrieve a separate non-synchronized accessor for each thread you will be using it on.
  64. *
  65. * @note
  66. * Accessors contain their own command queue and their commands will only start to get executed once that queue
  67. * is submitted to the core thread via submitAccessors() method.
  68. * @note
  69. * Synced accessor commands are sent after all non-synced accessor commands are sent.
  70. */
  71. SyncedCoreAccessor& getSyncedAccessor();
  72. /** Queues all the accessor commands and starts executing them on the core thread. */
  73. void submitAccessors(bool blockUntilComplete = false);
  74. /**
  75. * Queues a new command that will be added to the global command queue. You are allowed to call this from any thread,
  76. * however be aware that it involves possibly slow synchronization primitives, so limit your usage.
  77. *
  78. * @param[in] blockUntilComplete If true the thread will be blocked until the command executes. Be aware that there
  79. * may be many commands queued before it and they all need to be executed in order
  80. * before the current command is reached, which might take a long time.
  81. *
  82. * @see CommandQueue::queueReturn()
  83. */
  84. AsyncOp queueReturnCommand(std::function<void(AsyncOp&)> commandCallback, bool blockUntilComplete = false);
  85. /**
  86. * Queues a new command that will be added to the global command queue.You are allowed to call this from any thread,
  87. * however be aware that it involves possibly slow synchronization primitives, so limit your usage.
  88. *
  89. * @param[in] blockUntilComplete If true the thread will be blocked until the command executes. Be aware that there
  90. * may be many commands queued before it and they all need to be executed in order
  91. * before the current command is reached, which might take a long time.
  92. *
  93. * @see CommandQueue::queue()
  94. */
  95. void queueCommand(std::function<void()> commandCallback, bool blockUntilComplete = false);
  96. /**
  97. * Called once every frame.
  98. *
  99. * @note Must be called before sim thread schedules any core thread operations for the frame.
  100. */
  101. void update();
  102. /**
  103. * Returns a frame allocator that should be used for allocating temporary data being passed to the core thread. As the
  104. * name implies the data only lasts one frame, so you need to be careful not to use it for longer than that.
  105. *
  106. * @note Sim thread only.
  107. */
  108. FrameAlloc* getFrameAlloc() const;
  109. private:
  110. static const int NUM_FRAME_ALLOCS = 2;
  111. /**
  112. * Double buffered frame allocators. Means sim thread cannot be more than 1 frame ahead of core thread (If that changes
  113. * you should be able to easily add more).
  114. */
  115. FrameAlloc* mFrameAllocs[NUM_FRAME_ALLOCS];
  116. UINT32 mActiveFrameAlloc;
  117. static AccessorData mAccessor;
  118. Vector<AccessorContainer*> mAccessors;
  119. volatile bool mCoreThreadShutdown;
  120. HThread mCoreThread;
  121. bool mCoreThreadStarted;
  122. BS_THREAD_ID_TYPE mSimThreadId;
  123. BS_THREAD_ID_TYPE mCoreThreadId;
  124. BS_MUTEX(mCommandQueueMutex)
  125. BS_MUTEX(mAccessorMutex)
  126. BS_THREAD_SYNCHRONISER(mCommandReadyCondition)
  127. BS_MUTEX(mCommandNotifyMutex)
  128. BS_THREAD_SYNCHRONISER(mCommandCompleteCondition)
  129. BS_MUTEX(mThreadStartedMutex)
  130. BS_THREAD_SYNCHRONISER(mCoreThreadStartedCondition)
  131. CommandQueue<CommandQueueSync>* mCommandQueue;
  132. UINT32 mMaxCommandNotifyId; /**< ID that will be assigned to the next command with a notifier callback. */
  133. Vector<UINT32> mCommandsCompleted; /**< Completed commands that have notifier callbacks set up */
  134. SyncedCoreAccessor* mSyncedCoreAccessor;
  135. /** Starts the core thread worker method. Should only be called once. */
  136. void initCoreThread();
  137. /** Main worker method of the core thread. Called once thread is started. */
  138. void runCoreThread();
  139. /** Shutdowns the core thread. It will complete all ready commands before shutdown. */
  140. void shutdownCoreThread();
  141. /**
  142. * Blocks the calling thread until the command with the specified ID completes. Make sure that the specified ID
  143. * actually exists, otherwise this will block forever.
  144. */
  145. void blockUntilCommandCompleted(UINT32 commandId);
  146. /**
  147. * Callback called by the command list when a specific command finishes executing. This is only called on commands that
  148. * have a special notify on complete flag set.
  149. *
  150. * @param[in] commandId Identifier for the command.
  151. */
  152. void commandCompletedNotify(UINT32 commandId);
  153. };
  154. /**
  155. * Returns the core thread manager used for dealing with the core thread from external threads.
  156. *
  157. * @see CoreThread
  158. */
  159. BS_CORE_EXPORT CoreThread& gCoreThread();
  160. /** Throws an exception if current thread isn't the core thread. */
  161. BS_CORE_EXPORT void throwIfNotCoreThread();
  162. /** Throws an exception if current thread is the core thread. */
  163. BS_CORE_EXPORT void throwIfCoreThread();
  164. #if BS_DEBUG_MODE
  165. #define THROW_IF_NOT_CORE_THREAD throwIfNotCoreThread();
  166. #define THROW_IF_CORE_THREAD throwIfCoreThread();
  167. #else
  168. #define THROW_IF_NOT_CORE_THREAD
  169. #define THROW_IF_CORE_THREAD
  170. #endif
  171. /** @endcond */
  172. /**
  173. * Creates or retrieves an accessor that you can use for executing commands on the core thread from a non-core thread.
  174. * The accessor will be bound to the thread you call this method on.
  175. */
  176. BS_CORE_EXPORT CoreThreadAccessor<CommandQueueNoSync>& gCoreAccessor();
  177. /**
  178. * Retrieves an accessor that you can use for executing commands on the core thread from a non-core thread. There is
  179. * only one synchronized accessor and you may access it from any thread you wish. Note however that it is much more
  180. * efficient to retrieve a separate non-synchronized accessor for each thread you will be using it on.
  181. */
  182. BS_CORE_EXPORT CoreThreadAccessor<CommandQueueSync>& gSyncedCoreAccessor();
  183. /** @} */
  184. }