1 //===-- llvm/Support/TaskQueue.h - A TaskQueue implementation ---*- C++ -*-===//
3 // The LLVM Compiler Infrastructure
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
8 //===----------------------------------------------------------------------===//
10 // This file defines a crude C++11 based task queue.
12 //===----------------------------------------------------------------------===//
14 #ifndef LLVM_SUPPORT_TASK_QUEUE_H
15 #define LLVM_SUPPORT_TASK_QUEUE_H
17 #include "llvm/Config/llvm-config.h"
18 #include "llvm/Support/ThreadPool.h"
19 #include "llvm/Support/thread.h"
23 #include <condition_variable>
32 /// TaskQueue executes serialized work on a user-defined Thread Pool. It
33 /// guarantees that if task B is enqueued after task A, task B begins after
34 /// task A completes and there is no overlap between the two.
36 // Because we don't have init capture to use move-only local variables that
37 // are captured into a lambda, we create the promise inside an explicit
38 // callable struct. We want to do as much of the wrapping in the
39 // type-specialized domain (before type erasure) and then erase this into a
41 template <typename Callable> struct Task {
42 using ResultTy = typename std::result_of<Callable()>::type;
43 explicit Task(Callable C, TaskQueue &Parent)
44 : C(std::move(C)), P(std::make_shared<std::promise<ResultTy>>()),
48 void invokeCallbackAndSetPromise(T*) {
52 void invokeCallbackAndSetPromise(void*) {
57 void operator()() noexcept {
58 ResultTy *Dummy = nullptr;
59 invokeCallbackAndSetPromise(Dummy);
60 Parent->completeTask();
64 std::shared_ptr<std::promise<ResultTy>> P;
69 /// Construct a task queue with no work.
70 TaskQueue(ThreadPool &Scheduler) : Scheduler(Scheduler) { (void)Scheduler; }
72 /// Blocking destructor: the queue will wait for all work to complete.
75 assert(Tasks.empty());
78 /// Asynchronous submission of a task to the queue. The returned future can be
79 /// used to wait for the task (and all previous tasks that have not yet
80 /// completed) to finish.
81 template <typename Callable>
82 std::future<typename std::result_of<Callable()>::type> async(Callable &&C) {
83 #if !LLVM_ENABLE_THREADS
85 "TaskQueue requires building with LLVM_ENABLE_THREADS!");
87 Task<Callable> T{std::move(C), *this};
88 using ResultTy = typename std::result_of<Callable()>::type;
89 std::future<ResultTy> F = T.P->get_future();
91 std::lock_guard<std::mutex> Lock(QueueLock);
92 // If there's already a task in flight, just queue this one up. If
93 // there is not a task in flight, bypass the queue and schedule this
96 Tasks.push_back(std::move(T));
98 Scheduler.async(std::move(T));
99 IsTaskInFlight = true;
106 void completeTask() {
107 // We just completed a task. If there are no more tasks in the queue,
108 // update IsTaskInFlight to false and stop doing work. Otherwise
109 // schedule the next task (while not holding the lock).
110 std::function<void()> Continuation;
112 std::lock_guard<std::mutex> Lock(QueueLock);
114 IsTaskInFlight = false;
118 Continuation = std::move(Tasks.front());
121 Scheduler.async(std::move(Continuation));
124 /// The thread pool on which to run the work.
125 ThreadPool &Scheduler;
127 /// State which indicates whether the queue currently is currently processing
129 bool IsTaskInFlight = false;
131 /// Mutex for synchronizing access to the Tasks array.
132 std::mutex QueueLock;
134 /// Tasks waiting for execution in the queue.
135 std::deque<std::function<void()>> Tasks;
139 #endif // LLVM_SUPPORT_TASK_QUEUE_H