diff options
Diffstat (limited to 'include/lldb/Utility/TaskPool.h')
-rw-r--r-- | include/lldb/Utility/TaskPool.h | 283 |
1 files changed, 136 insertions, 147 deletions
diff --git a/include/lldb/Utility/TaskPool.h b/include/lldb/Utility/TaskPool.h index 443e2a5853e2..db15b208171b 100644 --- a/include/lldb/Utility/TaskPool.h +++ b/include/lldb/Utility/TaskPool.h @@ -23,7 +23,7 @@ // warnings in the Concurrency Runtime. This can be removed when we switch to // MSVC 2015 #pragma warning(push) -#pragma warning(disable:4062) +#pragma warning(disable : 4062) #endif #include <cassert> @@ -34,185 +34,174 @@ #include <thread> #include <vector> -// Global TaskPool class for running tasks in parallel on a set of worker thread created the first -// time the task pool is used. The TaskPool provide no gurantee about the order the task will be run -// and about what tasks will run in parrallel. None of the task added to the task pool should block -// on something (mutex, future, condition variable) what will be set only by the completion of an +// Global TaskPool class for running tasks in parallel on a set of worker thread +// created the first +// time the task pool is used. The TaskPool provide no guarantee about the order +// the task will be run +// and about what tasks will run in parallel. None of the task added to the task +// pool should block +// on something (mutex, future, condition variable) what will be set only by the +// completion of an // other task on the task pool as they may run on the same thread sequentally. -class TaskPool -{ +class TaskPool { public: - // Add a new task to the task pool and return a std::future belonging to the newly created task. - // The caller of this function has to wait on the future for this task to complete. - template<typename F, typename... Args> - static std::future<typename std::result_of<F(Args...)>::type> - AddTask(F&& f, Args&&... args); - - // Run all of the specified tasks on the task pool and wait until all of them are finished - // before returning. This method is intended to be used for small number tasks where listing - // them as function arguments is acceptable. For running large number of tasks you should use - // AddTask for each task and then call wait() on each returned future. - template<typename... T> - static void - RunTasks(T&&... tasks); + // Add a new task to the task pool and return a std::future belonging to the + // newly created task. + // The caller of this function has to wait on the future for this task to + // complete. + template <typename F, typename... Args> + static std::future<typename std::result_of<F(Args...)>::type> + AddTask(F &&f, Args &&... args); + + // Run all of the specified tasks on the task pool and wait until all of them + // are finished + // before returning. This method is intended to be used for small number tasks + // where listing + // them as function arguments is acceptable. For running large number of tasks + // you should use + // AddTask for each task and then call wait() on each returned future. + template <typename... T> static void RunTasks(T &&... tasks); private: - TaskPool() = delete; + TaskPool() = delete; - template<typename... T> - struct RunTaskImpl; + template <typename... T> struct RunTaskImpl; - static void - AddTaskImpl(std::function<void()>&& task_fn); + static void AddTaskImpl(std::function<void()> &&task_fn); }; -// Wrapper class around the global TaskPool implementation to make it possible to create a set of -// tasks and then wait for the tasks to be completed by the WaitForNextCompletedTask call. This -// class should be used when WaitForNextCompletedTask is needed because this class add no other -// extra functionality to the TaskPool class and it have a very minor performance overhead. -template <typename T> // The return type of the tasks what will be added to this task runner -class TaskRunner -{ +// Wrapper class around the global TaskPool implementation to make it possible +// to create a set of +// tasks and then wait for the tasks to be completed by the +// WaitForNextCompletedTask call. This +// class should be used when WaitForNextCompletedTask is needed because this +// class add no other +// extra functionality to the TaskPool class and it have a very minor +// performance overhead. +template <typename T> // The return type of the tasks what will be added to this + // task runner + class TaskRunner { public: - // Add a task to the task runner what will also add the task to the global TaskPool. The - // function doesn't return the std::future for the task because it will be supplied by the - // WaitForNextCompletedTask after the task is completed. - template<typename F, typename... Args> - void - AddTask(F&& f, Args&&... args); - - // Wait for the next task in this task runner to finish and then return the std::future what - // belongs to the finished task. If there is no task in this task runner (neither pending nor - // comleted) then this function will return an invalid future. Usually this function should be - // called in a loop processing the results of the tasks until it returns an invalid std::future - // what means that all task in this task runner is completed. - std::future<T> - WaitForNextCompletedTask(); - - // Convenience method to wait for all task in this TaskRunner to finish. Do NOT use this class - // just because of this method. Use TaskPool instead and wait for each std::future returned by - // AddTask in a loop. - void - WaitForAllTasks(); + // Add a task to the task runner what will also add the task to the global + // TaskPool. The + // function doesn't return the std::future for the task because it will be + // supplied by the + // WaitForNextCompletedTask after the task is completed. + template <typename F, typename... Args> void AddTask(F &&f, Args &&... args); + + // Wait for the next task in this task runner to finish and then return the + // std::future what + // belongs to the finished task. If there is no task in this task runner + // (neither pending nor + // comleted) then this function will return an invalid future. Usually this + // function should be + // called in a loop processing the results of the tasks until it returns an + // invalid std::future + // what means that all task in this task runner is completed. + std::future<T> WaitForNextCompletedTask(); + + // Convenience method to wait for all task in this TaskRunner to finish. Do + // NOT use this class + // just because of this method. Use TaskPool instead and wait for each + // std::future returned by + // AddTask in a loop. + void WaitForAllTasks(); private: - std::list<std::future<T>> m_ready; - std::list<std::future<T>> m_pending; - std::mutex m_mutex; - std::condition_variable m_cv; + std::list<std::future<T>> m_ready; + std::list<std::future<T>> m_pending; + std::mutex m_mutex; + std::condition_variable m_cv; }; -template<typename F, typename... Args> +template <typename F, typename... Args> std::future<typename std::result_of<F(Args...)>::type> -TaskPool::AddTask(F&& f, Args&&... args) -{ - auto task_sp = std::make_shared<std::packaged_task<typename std::result_of<F(Args...)>::type()>>( - std::bind(std::forward<F>(f), std::forward<Args>(args)...)); +TaskPool::AddTask(F &&f, Args &&... args) { + auto task_sp = std::make_shared< + std::packaged_task<typename std::result_of<F(Args...)>::type()>>( + std::bind(std::forward<F>(f), std::forward<Args>(args)...)); - AddTaskImpl([task_sp]() { (*task_sp)(); }); + AddTaskImpl([task_sp]() { (*task_sp)(); }); - return task_sp->get_future(); + return task_sp->get_future(); } -template<typename... T> -void -TaskPool::RunTasks(T&&... tasks) -{ - RunTaskImpl<T...>::Run(std::forward<T>(tasks)...); +template <typename... T> void TaskPool::RunTasks(T &&... tasks) { + RunTaskImpl<T...>::Run(std::forward<T>(tasks)...); } -template<typename Head, typename... Tail> -struct TaskPool::RunTaskImpl<Head, Tail...> -{ - static void - Run(Head&& h, Tail&&... t) - { - auto f = AddTask(std::forward<Head>(h)); - RunTaskImpl<Tail...>::Run(std::forward<Tail>(t)...); - f.wait(); - } +template <typename Head, typename... Tail> +struct TaskPool::RunTaskImpl<Head, Tail...> { + static void Run(Head &&h, Tail &&... t) { + auto f = AddTask(std::forward<Head>(h)); + RunTaskImpl<Tail...>::Run(std::forward<Tail>(t)...); + f.wait(); + } }; -template<> -struct TaskPool::RunTaskImpl<> -{ - static void - Run() {} +template <> struct TaskPool::RunTaskImpl<> { + static void Run() {} }; template <typename T> -template<typename F, typename... Args> -void -TaskRunner<T>::AddTask(F&& f, Args&&... args) -{ - std::unique_lock<std::mutex> lock(m_mutex); - auto it = m_pending.emplace(m_pending.end()); - *it = std::move(TaskPool::AddTask( - [this, it](F f, Args... args) - { - T&& r = f(std::forward<Args>(args)...); - - std::unique_lock<std::mutex> lock(this->m_mutex); - this->m_ready.splice(this->m_ready.end(), this->m_pending, it); - lock.unlock(); - - this->m_cv.notify_one(); - return r; - }, - std::forward<F>(f), - std::forward<Args>(args)...)); +template <typename F, typename... Args> +void TaskRunner<T>::AddTask(F &&f, Args &&... args) { + std::unique_lock<std::mutex> lock(m_mutex); + auto it = m_pending.emplace(m_pending.end()); + *it = std::move(TaskPool::AddTask( + [this, it](F f, Args... args) { + T &&r = f(std::forward<Args>(args)...); + + std::unique_lock<std::mutex> lock(this->m_mutex); + this->m_ready.splice(this->m_ready.end(), this->m_pending, it); + lock.unlock(); + + this->m_cv.notify_one(); + return r; + }, + std::forward<F>(f), std::forward<Args>(args)...)); } template <> -template<typename F, typename... Args> -void -TaskRunner<void>::AddTask(F&& f, Args&&... args) -{ - std::unique_lock<std::mutex> lock(m_mutex); - auto it = m_pending.emplace(m_pending.end()); - *it = std::move(TaskPool::AddTask( - [this, it](F f, Args... args) - { - f(std::forward<Args>(args)...); - - std::unique_lock<std::mutex> lock(this->m_mutex); - this->m_ready.emplace_back(std::move(*it)); - this->m_pending.erase(it); - lock.unlock(); - - this->m_cv.notify_one(); - }, - std::forward<F>(f), - std::forward<Args>(args)...)); +template <typename F, typename... Args> +void TaskRunner<void>::AddTask(F &&f, Args &&... args) { + std::unique_lock<std::mutex> lock(m_mutex); + auto it = m_pending.emplace(m_pending.end()); + *it = std::move(TaskPool::AddTask( + [this, it](F f, Args... args) { + f(std::forward<Args>(args)...); + + std::unique_lock<std::mutex> lock(this->m_mutex); + this->m_ready.emplace_back(std::move(*it)); + this->m_pending.erase(it); + lock.unlock(); + + this->m_cv.notify_one(); + }, + std::forward<F>(f), std::forward<Args>(args)...)); } -template <typename T> -std::future<T> -TaskRunner<T>::WaitForNextCompletedTask() -{ - std::unique_lock<std::mutex> lock(m_mutex); - if (m_ready.empty() && m_pending.empty()) - return std::future<T>(); // No more tasks - - if (m_ready.empty()) - m_cv.wait(lock, [this](){ return !this->m_ready.empty(); }); - - std::future<T> res = std::move(m_ready.front()); - m_ready.pop_front(); - - lock.unlock(); - res.wait(); - - return std::move(res); -} +template <typename T> std::future<T> TaskRunner<T>::WaitForNextCompletedTask() { + std::unique_lock<std::mutex> lock(m_mutex); + if (m_ready.empty() && m_pending.empty()) + return std::future<T>(); // No more tasks -template <typename T> -void -TaskRunner<T>::WaitForAllTasks() -{ - while (WaitForNextCompletedTask().valid()); + if (m_ready.empty()) + m_cv.wait(lock, [this]() { return !this->m_ready.empty(); }); + + std::future<T> res = std::move(m_ready.front()); + m_ready.pop_front(); + + lock.unlock(); + res.wait(); + + return std::move(res); } +template <typename T> void TaskRunner<T>::WaitForAllTasks() { + while (WaitForNextCompletedTask().valid()) + ; +} #if defined(_MSC_VER) #pragma warning(pop) |