diff options
Diffstat (limited to 'include/lldb/Utility/TaskPool.h')
-rw-r--r-- | include/lldb/Utility/TaskPool.h | 221 |
1 files changed, 221 insertions, 0 deletions
diff --git a/include/lldb/Utility/TaskPool.h b/include/lldb/Utility/TaskPool.h new file mode 100644 index 000000000000..443e2a5853e2 --- /dev/null +++ b/include/lldb/Utility/TaskPool.h @@ -0,0 +1,221 @@ +//===--------------------- TaskPool.h ---------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef utility_TaskPool_h_ +#define utility_TaskPool_h_ + +#if defined(__cplusplus) && defined(_MSC_VER) && (_HAS_EXCEPTIONS == 0) +// Compiling MSVC libraries with _HAS_EXCEPTIONS=0, eliminates most but not all +// calls to __uncaught_exception. Unfortunately, it does seem to eliminate +// the delcaration of __uncaught_excpeiton. Including <eh.h> ensures that it is +// declared. This may not be necessary after MSVC 12. +#include <eh.h> +#endif + +#if defined(_MSC_VER) +// Due to another bug in MSVC 2013, including <future> will generate hundreds of +// warnings in the Concurrency Runtime. This can be removed when we switch to +// MSVC 2015 +#pragma warning(push) +#pragma warning(disable:4062) +#endif + +#include <cassert> +#include <cstdint> +#include <future> +#include <list> +#include <queue> +#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 +// other task on the task pool as they may run on the same thread sequentally. +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); + +private: + TaskPool() = delete; + + template<typename... T> + struct RunTaskImpl; + + 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 +{ +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(); + +private: + 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> +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)...)); + + AddTaskImpl([task_sp]() { (*task_sp)(); }); + + return task_sp->get_future(); +} + +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<> +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 <> +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> +void +TaskRunner<T>::WaitForAllTasks() +{ + while (WaitForNextCompletedTask().valid()); +} + + +#if defined(_MSC_VER) +#pragma warning(pop) +#endif + +#endif // #ifndef utility_TaskPool_h_ |