WwiseExecutionQueue.h 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201
  1. /*******************************************************************************
  2. The content of this file includes portions of the proprietary AUDIOKINETIC Wwise
  3. Technology released in source code form as part of the game integration package.
  4. The content of this file may not be used without valid licenses to the
  5. AUDIOKINETIC Wwise Technology.
  6. Note that the use of the game engine is subject to the Unreal(R) Engine End User
  7. License Agreement at https://www.unrealengine.com/en-US/eula/unreal
  8. License Usage
  9. Licensees holding valid licenses to the AUDIOKINETIC Wwise Technology may use
  10. this file in accordance with the end user license agreement provided with the
  11. software or, alternatively, in accordance with the terms contained
  12. in a written agreement between you and Audiokinetic Inc.
  13. Copyright (c) 2023 Audiokinetic Inc.
  14. *******************************************************************************/
  15. #pragma once
  16. #include "Async/TaskGraphInterfaces.h"
  17. #include "Containers/Queue.h"
  18. #include "Misc/DateTime.h"
  19. #include "Misc/QueuedThreadPool.h"
  20. #include "Wwise/WwiseTask.h"
  21. #include <atomic>
  22. /**
  23. * @brief Asynchronous sequential execution queue.
  24. *
  25. * The goal of the Execution Queue is to call asynchronous operations sequentially. In effect,
  26. * this creates the equivalent of a GCD's Dispatch Queue, where operations are known to be executed
  27. * one after the other, but asynchronously.
  28. */
  29. struct WWISECONCURRENCY_API FWwiseExecutionQueue
  30. {
  31. const TCHAR* const DebugName;
  32. const EWwiseTaskPriority TaskPriority;
  33. using FBasicFunction = TUniqueFunction<void()>;
  34. #define WWISE_EQ_NAME(name) TEXT(name) TEXT(" Execution Queue worker")
  35. /**
  36. * @brief Starts a new Execution Queue running at a particular thread priority.
  37. * @param InDebugName Name for this execution queue's task. Can use WWISE_EQ_NAME to create a standardized name.
  38. * @param InTaskPriority The priority for the task.
  39. */
  40. FWwiseExecutionQueue(const TCHAR* InDebugName, EWwiseTaskPriority InTaskPriority = EWwiseTaskPriority::Default);
  41. /**
  42. * @brief Destructor for Execution Queue.
  43. *
  44. * This will lock the calling thread until the Execution Queue is actually closed.
  45. *
  46. * If the Execution Queue can be deleted while executing its own Async operation, you need to call CloseAndDelete() instead
  47. * of deleting the Execution Queue yourself.
  48. */
  49. ~FWwiseExecutionQueue();
  50. /**
  51. * @brief Calls a function asynchronously in the Execution Queue.
  52. * @param InDebugName Name of the async task. Can use UE_SOURCE_LOCATION for a standardized name.
  53. * @param InFunction The function to be called.
  54. *
  55. * Async usually calls the passed function asynchronously. However, in deletion instances or exiting instances, this
  56. * will be called synchronously. If you absolutely need this call to be done asynchronously (it might not be called),
  57. * you should call AsyncAlways instead.
  58. */
  59. void Async(const TCHAR* InDebugName, FBasicFunction&& InFunction);
  60. /**
  61. * @brief Calls a function asynchronously in the Execution Queue. If no Execution Queue is available, it will be called
  62. * on any Task Graph thread.
  63. * @param InDebugName Name of the async task. Can use UE_SOURCE_LOCATION for a standardized name.
  64. * @param InFunction The function to be called.
  65. *
  66. * Execute the function asynchronously, or on the Task Graph, but never synchronously.
  67. */
  68. void AsyncAlways(const TCHAR* InDebugName, FBasicFunction&& InFunction);
  69. /**
  70. * @brief Calls a function asynchronously in the Execution Queue, and then wait for it to be called.
  71. * @param InDebugName Name of the async task. Can use UE_SOURCE_LOCATION for a standardized name.
  72. * @param InFunction The function to be called.
  73. */
  74. void AsyncWait(const TCHAR* InDebugName, FBasicFunction&& InFunction);
  75. /**
  76. * @brief Permanently closes the Execution Queue, ensuring every Async operations are done processing before closing.
  77. *
  78. * This gets called by the destructor. This will lock the calling thread until the Execution Queue is actually closed.
  79. */
  80. void Close();
  81. /**
  82. * @brief Permanently closes the Execution Queue once it's done processing asynchronously. Then, deletes the Execution Queue pointer.
  83. *
  84. * This is the way to destroy an Execution Queue whose destruction can be achieved through one of its own function. For example,
  85. * an object that async a structure cleanup, and then removes itself once it's possible to delete it.
  86. */
  87. void CloseAndDelete();
  88. bool IsBeingClosed() const;
  89. bool IsClosed() const;
  90. bool IsRunningInThisThread() const;
  91. struct FOpQueueItem
  92. {
  93. #if ENABLE_NAMED_EVENTS
  94. FOpQueueItem() : DebugName(nullptr), Function([]{}) {}
  95. FOpQueueItem(const TCHAR* InDebugName, FBasicFunction&& InFunction) :
  96. DebugName(InDebugName),
  97. Function(MoveTemp(InFunction))
  98. {}
  99. FOpQueueItem(FOpQueueItem&& Rhs) :
  100. DebugName(Rhs.DebugName),
  101. Function(MoveTemp(Rhs.Function))
  102. {
  103. }
  104. #else
  105. FOpQueueItem() : Function([]{}) {}
  106. FOpQueueItem(const TCHAR*, FBasicFunction&& InFunction) :
  107. Function(MoveTemp(InFunction))
  108. {}
  109. FOpQueueItem(FOpQueueItem&& Rhs) :
  110. Function(MoveTemp(Rhs.Function))
  111. {
  112. }
  113. #endif
  114. FOpQueueItem& operator=(const FOpQueueItem& Rhs)
  115. {
  116. #if ENABLE_NAMED_EVENTS
  117. check(Rhs.DebugName == nullptr);
  118. DebugName = nullptr;
  119. #endif
  120. Function = []{};
  121. return *this;
  122. }
  123. #if ENABLE_NAMED_EVENTS
  124. const TCHAR* DebugName;
  125. #endif
  126. FBasicFunction Function;
  127. };
  128. private:
  129. class ExecutionQueuePoolTask;
  130. enum class EWorkerState
  131. {
  132. Stopped, ///< Idle
  133. Running, ///< There is a thread owner executing operations
  134. AddOp, ///< While a thread is running, a producer is adding operations
  135. Closing, ///< While a thread is running, the last producer is asking to permanently close the Execution Queue
  136. Closed ///< Execution Queue is permanently closed. It can be deleted.
  137. };
  138. std::atomic<EWorkerState> WorkerState{ EWorkerState::Stopped };
  139. bool bDeleteOnceClosed{ false };
  140. using FOpQueue = TQueue<FOpQueueItem, EQueueMode::Mpsc>;
  141. FOpQueue OpQueue;
  142. struct TLS;
  143. void StartWorkerIfNeeded();
  144. void Work();
  145. bool KeepWorking();
  146. void ProcessWork();
  147. bool TrySetStoppedWorkerToRunning();
  148. bool TrySetRunningWorkerToStopped();
  149. bool TrySetRunningWorkerToAddOp();
  150. bool TrySetAddOpWorkerToRunning();
  151. bool TrySetRunningWorkerToClosing();
  152. bool TrySetClosingWorkerToClosed();
  153. static const TCHAR* StateName(EWorkerState State);
  154. FORCEINLINE bool TryStateUpdate(EWorkerState NeededState, EWorkerState WantedState);
  155. private:
  156. FWwiseExecutionQueue(const FWwiseExecutionQueue& Rhs) = delete;
  157. FWwiseExecutionQueue(FWwiseExecutionQueue&& Rhs) = delete;
  158. FWwiseExecutionQueue& operator=(const FWwiseExecutionQueue& Rhs) = delete;
  159. FWwiseExecutionQueue& operator=(FWwiseExecutionQueue&& Rhs) = delete;
  160. public:
  161. struct WWISECONCURRENCY_API Test
  162. {
  163. #if defined(WITH_LOW_LEVEL_TESTS) && WITH_LOW_LEVEL_TESTS || defined(WITH_AUTOMATION_TESTS) || (WITH_DEV_AUTOMATION_TESTS || WITH_PERF_AUTOMATION_TESTS)
  164. #define WWISE_EXECUTIONQUEUE_TEST_CONST
  165. #else
  166. #define WWISE_EXECUTIONQUEUE_TEST_CONST const
  167. #endif
  168. static const bool bExtremelyVerbose;
  169. static WWISE_EXECUTIONQUEUE_TEST_CONST bool bMockEngineDeletion;
  170. static WWISE_EXECUTIONQUEUE_TEST_CONST bool bMockEngineDeleted;
  171. static WWISE_EXECUTIONQUEUE_TEST_CONST bool bMockSleepOnStateUpdate;
  172. static WWISE_EXECUTIONQUEUE_TEST_CONST bool bReduceLogVerbosity;
  173. static bool IsExtremelyVerbose(){ return bExtremelyVerbose && !bReduceLogVerbosity; }
  174. };
  175. };