|
| 1 | +#pragma once |
| 2 | +#include <queue> |
| 3 | +#include <functional> |
| 4 | +#include <memory> |
| 5 | +#include <thread> |
| 6 | +#include <mutex> |
| 7 | + |
| 8 | +namespace SharedUtil |
| 9 | +{ |
| 10 | + /////////////////////////////////////////////////////////////// |
| 11 | + // |
| 12 | + // CAsyncTaskScheduler class |
| 13 | + // |
| 14 | + // Asynchronously executes tasks in secondary worker threads |
| 15 | + // and returns the result back to the main thread |
| 16 | + // |
| 17 | + /////////////////////////////////////////////////////////////// |
| 18 | + class CAsyncTaskScheduler |
| 19 | + { |
| 20 | + struct SBaseTask |
| 21 | + { |
| 22 | + virtual void Execute() = 0; |
| 23 | + virtual void ProcessResult() = 0; |
| 24 | + }; |
| 25 | + |
| 26 | + template<typename ResultType> |
| 27 | + struct STask : public SBaseTask |
| 28 | + { |
| 29 | + using TaskFunction_t = std::function<ResultType()>; |
| 30 | + using ReadyFunction_t = std::function<void(const ResultType&)>; |
| 31 | + |
| 32 | + TaskFunction_t m_TaskFunction; |
| 33 | + ReadyFunction_t m_ReadyFunction; |
| 34 | + ResultType m_Result; |
| 35 | + |
| 36 | + STask(const TaskFunction_t& taskFunc, const ReadyFunction_t& readyFunc) : m_TaskFunction(taskFunc), m_ReadyFunction(readyFunc) |
| 37 | + {} |
| 38 | + |
| 39 | + void Execute() override |
| 40 | + { |
| 41 | + m_Result = std::move(m_TaskFunction()); |
| 42 | + } |
| 43 | + |
| 44 | + void ProcessResult() override |
| 45 | + { |
| 46 | + m_ReadyFunction(m_Result); |
| 47 | + } |
| 48 | + }; |
| 49 | + |
| 50 | + public: |
| 51 | + // |
| 52 | + // Creates a new async task scheduler |
| 53 | + // with a fixed number of worker threads |
| 54 | + // |
| 55 | + CAsyncTaskScheduler(std::size_t numWorkers); |
| 56 | + |
| 57 | + // |
| 58 | + // Ends all worker threads (waits for the last task to finish) |
| 59 | + // |
| 60 | + ~CAsyncTaskScheduler(); |
| 61 | + |
| 62 | + // |
| 63 | + // Pushes a new task for execution once a worker is free |
| 64 | + // (Template Parameter) ResultType: The type of the result |
| 65 | + // |
| 66 | + // taskFunc: Time-consuming function that is executed on the secondary thread (be aware of thread safety!) |
| 67 | + // readyFunc: Function that is called once the result is ready (called on the main thread) |
| 68 | + // |
| 69 | + template<typename ResultType> |
| 70 | + void PushTask(const std::function<ResultType()>& taskFunc, const std::function<void(const ResultType&)>& readyFunc) |
| 71 | + { |
| 72 | + std::unique_ptr<SBaseTask> pTask{ new STask<ResultType>{ taskFunc, readyFunc } }; |
| 73 | + |
| 74 | + std::lock_guard<std::mutex>{ m_TasksMutex }; |
| 75 | + m_Tasks.push(std::move(pTask)); |
| 76 | + } |
| 77 | + |
| 78 | + // |
| 79 | + // Collects (polls) the results of the finished tasks |
| 80 | + // and invokes its ready-functions on the main thread |
| 81 | + // THIS FUNCTION MUST BE CALLED ON THE MAIN THREAD |
| 82 | + // |
| 83 | + void CollectResults(); |
| 84 | + |
| 85 | + protected: |
| 86 | + void DoWork(); |
| 87 | + |
| 88 | + private: |
| 89 | + std::vector<std::thread> m_Workers; |
| 90 | + bool m_Running = true; |
| 91 | + |
| 92 | + std::queue<std::unique_ptr<SBaseTask>> m_Tasks; |
| 93 | + std::mutex m_TasksMutex; |
| 94 | + |
| 95 | + std::vector<std::unique_ptr<SBaseTask>> m_TaskResults; |
| 96 | + std::mutex m_TaskResultsMutex; |
| 97 | + }; |
| 98 | +} |
0 commit comments