CmCommandQueue.h 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188
  1. #pragma once
  2. #include "CmPrerequisites.h"
  3. #include "CmAsyncOp.h"
  4. #include "CmCommonEnums.h"
  5. #include "boost/function.hpp"
  6. #include <functional>
  7. namespace CamelotEngine
  8. {
  9. /**
  10. * @brief Contains a list of commands that can be queued by one thread,
  11. * and executed by another.
  12. */
  13. class CM_EXPORT CommandQueue
  14. {
  15. public:
  16. struct Command
  17. {
  18. #ifdef CM_DEBUG_MODE
  19. Command(boost::function<void(AsyncOp&)> _callback, UINT32 _debugId, bool _notifyWhenComplete = false, UINT32 _callbackId = 0)
  20. :callbackWithReturnValue(_callback), debugId(_debugId), returnsValue(true), notifyWhenComplete(_notifyWhenComplete), callbackId(_callbackId)
  21. { }
  22. Command(boost::function<void()> _callback, UINT32 _debugId, bool _notifyWhenComplete = false, UINT32 _callbackId = 0)
  23. :callback(_callback), debugId(_debugId), returnsValue(false), notifyWhenComplete(_notifyWhenComplete), callbackId(_callbackId)
  24. { }
  25. UINT32 debugId;
  26. #else
  27. Command(boost::function<void(AsyncOp&)> _callback, bool _notifyWhenComplete = false, UINT32 _callbackId = 0)
  28. :callbackWithReturnValue(_callback), returnsValue(true), notifyWhenComplete(_notifyWhenComplete), callbackId(_callbackId)
  29. { }
  30. Command(boost::function<void()> _callback, bool _notifyWhenComplete = false, UINT32 _callbackId = 0)
  31. :callback(_callback), returnsValue(false), notifyWhenComplete(_notifyWhenComplete), callbackId(_callbackId)
  32. { }
  33. #endif
  34. boost::function<void()> callback;
  35. boost::function<void(AsyncOp&)> callbackWithReturnValue;
  36. AsyncOp asyncOp;
  37. bool returnsValue;
  38. UINT32 callbackId;
  39. bool notifyWhenComplete;
  40. };
  41. /**
  42. * @brief Constructor.
  43. *
  44. * @param threadId Identifier for the thread the command queue will be used on.
  45. * @param allowAllThreads Only matters for debug purposes. If false, then the queue
  46. * will throw an exception if accessed outside of the creation thread
  47. * (If in debug mode).
  48. *
  49. * When you want to allow multiple threads to access it, set this to true,
  50. * and also make sure you sync access to the queue properly.
  51. *
  52. */
  53. CommandQueue(CM_THREAD_ID_TYPE threadId, bool allowAllThreads = false);
  54. ~CommandQueue();
  55. CM_THREAD_ID_TYPE getThreadId() const { return mMyThreadId; }
  56. /**
  57. * @brief Queue up a new command to execute. Make sure the provided function has all of its
  58. * parameters properly bound. Last parameter must be unbound and of AsyncOp&amp; type.
  59. * This is used to signal that the command is completed, and also for storing the return
  60. * value.
  61. *
  62. * @note Callback method also needs to call AsyncOp::markAsResolved once it is done
  63. * processing. (If it doesn't it will still be called automatically, but the return
  64. * value will default to nullptr)
  65. *
  66. * @param _notifyWhenComplete (optional) Call the notify method (provided in the call to CommandQueue::playback)
  67. * when the command is complete.
  68. * @param _callbackId (optional) Identifier for the callback so you can then later find it
  69. * if needed.
  70. *
  71. * @return Async operation object you can continuously check until the command completes. After
  72. * it completes AsyncOp::isResolved will return true and return data will be valid (if
  73. * the callback provided any).
  74. */
  75. AsyncOp queueReturn(boost::function<void(AsyncOp&)> commandCallback, bool _notifyWhenComplete = false, UINT32 _callbackId = 0);
  76. /**
  77. * @brief Queue up a new command to execute. Make sure the provided function has all of its
  78. * parameters properly bound. Provided command is not expected to return a value. If you
  79. * wish to return a value from the callback use the other overload of queueCommand which
  80. * accepts AsyncOp parameter.
  81. *
  82. * @param _notifyWhenComplete (optional) Call the notify method (provided in the call to CommandQueue::playback)
  83. * when the command is complete.
  84. * @param _callbackId (optional) Identifier for the callback so you can then later find
  85. * it if needed.
  86. */
  87. void queue(boost::function<void()> commandCallback, bool _notifyWhenComplete = false, UINT32 _callbackId = 0);
  88. /**
  89. * @brief Returns a copy of all queued commands and makes room for new ones. Must be called from the thread
  90. * that created the command queue. Returned commands MUST be passed to playbackCommands.
  91. */
  92. std::queue<Command>* flush();
  93. /**
  94. * @brief Cancels all currently queued commands.
  95. */
  96. void cancelAll();
  97. /**
  98. * @brief Plays all provided commands. Should only be called from the render thread. To get the
  99. * commands call flushCommands().
  100. *
  101. * @param notifyCallback Callback that will be called if a command that has "notifyOnComplete" flag set.
  102. * The callback will receive "callbackId" of the command.
  103. */
  104. void playback(std::queue<Command>* commands, boost::function<void(UINT32)> notifyCallback);
  105. /**
  106. * @brief Plays all provided commands. Should only be called from the render thread. To get
  107. * the commands call flushCommands().
  108. */
  109. void playback(std::queue<Command>* commands);
  110. /**
  111. * @brief Returns true if no commands are queued.
  112. */
  113. bool isEmpty();
  114. /**
  115. * @brief Allows you to set a breakpoint that will trigger when the specified command is executed.
  116. *
  117. * @note This is helpful when you receive an error on the executing thread and you cannot tell from where was
  118. * the command that caused the error queued from. However you can make a note of the queue and command index
  119. * and set a breakpoint so that it gets triggered next time you run the program. At that point you can know
  120. * exactly which part of code queued the command by examining the stack trace.
  121. *
  122. * @param queueIdx Zero-based index of the queue the command was queued on.
  123. * @param commandIdx Zero-based index of the command.
  124. */
  125. static void addBreakpoint(UINT32 queueIdx, UINT32 commandIdx);
  126. private:
  127. std::queue<Command>* mCommands;
  128. bool mAllowAllThreads;
  129. CM_THREAD_ID_TYPE mMyThreadId;
  130. CM_MUTEX(mCommandBufferMutex);
  131. // Various variables that allow for easier debugging by allowing us to trigger breakpoints
  132. // when a certain command was queued.
  133. #if CM_DEBUG_MODE
  134. struct QueueBreakpoint
  135. {
  136. class HashFunction
  137. {
  138. public:
  139. size_t operator()(const QueueBreakpoint &key) const;
  140. };
  141. class EqualFunction
  142. {
  143. public:
  144. bool operator()(const QueueBreakpoint &a, const QueueBreakpoint &b) const;
  145. };
  146. QueueBreakpoint(UINT32 _queueIdx, UINT32 _commandIdx)
  147. :queueIdx(_queueIdx), commandIdx(_commandIdx)
  148. { }
  149. UINT32 queueIdx;
  150. UINT32 commandIdx;
  151. inline size_t operator()(const QueueBreakpoint& v) const;
  152. };
  153. UINT32 mMaxDebugIdx;
  154. UINT32 mCommandQueueIdx;
  155. static UINT32 MaxCommandQueueIdx;
  156. static std::unordered_set<QueueBreakpoint, QueueBreakpoint::HashFunction, QueueBreakpoint::EqualFunction> SetBreakpoints;
  157. CM_STATIC_MUTEX(CommandQueueBreakpointMutex);
  158. static void breakIfNeeded(UINT32 queueIdx, UINT32 commandIdx);
  159. #endif
  160. };
  161. }