2
0

CmCommandQueue.h 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183
  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 Plays all provided commands. Should only be called from the render thread. To get the
  95. * commands call flushCommands().
  96. *
  97. * @param notifyCallback Callback that will be called if a command that has "notifyOnComplete" flag set.
  98. * The callback will receive "callbackId" of the command.
  99. */
  100. void playback(std::queue<Command>* commands, boost::function<void(UINT32)> notifyCallback);
  101. /**
  102. * @brief Plays all provided commands. Should only be called from the render thread. To get
  103. * the commands call flushCommands().
  104. */
  105. void playback(std::queue<Command>* commands);
  106. /**
  107. * @brief Returns true if no commands are queued.
  108. */
  109. bool isEmpty();
  110. /**
  111. * @brief Allows you to set a breakpoint that will trigger when the specified command is executed.
  112. *
  113. * @note This is helpful when you receive an error on the executing thread and you cannot tell from where was
  114. * the command that caused the error queued from. However you can make a note of the queue and command index
  115. * and set a breakpoint so that it gets triggered next time you run the program. At that point you can know
  116. * exactly which part of code queued the command by examining the stack trace.
  117. *
  118. * @param queueIdx Zero-based index of the queue the command was queued on.
  119. * @param commandIdx Zero-based index of the command.
  120. */
  121. static void addBreakpoint(UINT32 queueIdx, UINT32 commandIdx);
  122. private:
  123. std::queue<Command>* mCommands;
  124. bool mAllowAllThreads;
  125. CM_THREAD_ID_TYPE mMyThreadId;
  126. CM_MUTEX(mCommandBufferMutex);
  127. // Various variables that allow for easier debugging by allowing us to trigger breakpoints
  128. // when a certain command was queued.
  129. #if CM_DEBUG_MODE
  130. struct QueueBreakpoint
  131. {
  132. class HashFunction
  133. {
  134. public:
  135. size_t operator()(const QueueBreakpoint &key) const;
  136. };
  137. class EqualFunction
  138. {
  139. public:
  140. bool operator()(const QueueBreakpoint &a, const QueueBreakpoint &b) const;
  141. };
  142. QueueBreakpoint(UINT32 _queueIdx, UINT32 _commandIdx)
  143. :queueIdx(_queueIdx), commandIdx(_commandIdx)
  144. { }
  145. UINT32 queueIdx;
  146. UINT32 commandIdx;
  147. inline size_t operator()(const QueueBreakpoint& v) const;
  148. };
  149. UINT32 mMaxDebugIdx;
  150. UINT32 mCommandQueueIdx;
  151. static UINT32 MaxCommandQueueIdx;
  152. static std::unordered_set<QueueBreakpoint, QueueBreakpoint::HashFunction, QueueBreakpoint::EqualFunction> SetBreakpoints;
  153. CM_STATIC_MUTEX(CommandQueueBreakpointMutex);
  154. static void breakIfNeeded(UINT32 queueIdx, UINT32 commandIdx);
  155. #endif
  156. };
  157. }