Proteus
Programmable JIT compilation and optimization for C/C++ using LLVM
Loading...
Searching...
No Matches
CompilerAsync.h
Go to the documentation of this file.
1#ifndef PROTEUS_ASYNC_COMPILER_H
2#define PROTEUS_ASYNC_COMPILER_H
3
9
10#include <condition_variable>
11#include <deque>
12#include <thread>
13
14namespace proteus {
15
16using namespace llvm;
17
19public:
20 explicit CompilationResult() : IsReadyFlag{false} {}
21
24
25 CompilationResult(CompilationResult &&) noexcept = delete;
26 CompilationResult &operator=(CompilationResult &&) noexcept = delete;
27
28 bool isReady() { return IsReadyFlag; }
29
30 void set(std::unique_ptr<MemoryBuffer> ObjBuf) {
31 ResultObjBuf = std::move(ObjBuf);
32 IsReadyFlag = true;
33 }
34
35 void wait() {
36 // Busy wait until it's ready.
37 while (!IsReadyFlag) {
38 std::this_thread::yield();
39 }
40 }
41
42 std::unique_ptr<MemoryBuffer> take() { return std::move(ResultObjBuf); }
43
44private:
45 std::atomic<bool> IsReadyFlag;
46 std::unique_ptr<MemoryBuffer> ResultObjBuf;
47};
48
50public:
52 std::unique_lock Lock{Mutex};
53 // Get HashValue before the move of CT.
54 HashT HashValue = CT.getHashValue();
55 Worklist.emplace_back(std::move(CT));
56 CompilationResultMap.emplace(HashValue,
57 std::make_unique<CompilationResult>());
58 CondVar.notify_one();
59 }
60
61 void run() {
62 TimeTraceThreadRAII TimeTraceThread;
63 [[maybe_unused]] int Count = 0;
64 while (Active) {
65 std::unique_lock Lock(Mutex);
66 CondVar.wait(Lock, [this] { return !Worklist.empty() || !Active; });
67 if (!Active)
68 break;
69 if (Worklist.empty())
70 reportFatalError("Expected non-empty Worklist");
71 CompilationTask CT = std::move(Worklist.front());
72 Worklist.pop_front();
73 Lock.unlock();
74
75 Count++;
76 std::unique_ptr<MemoryBuffer> ObjBuf = CT.compile();
77
78 // Ensure threads are joined before static objects are destroyed during
79 // program exit. This static is initialized after COMGR's own statics
80 // (triggered by compile() above), so C++ reverse destruction order
81 // guarantees ~ShutdownGuard runs first.
82 static ShutdownGuard Guard{this};
83
84 Lock.lock();
85 CompilationResultMap.at(CT.getHashValue())->set(std::move(ObjBuf));
86 Lock.unlock();
87 }
88
89 PROTEUS_DBG(Logger::logs("proteus")
90 << "Thread exiting! Compiled " + std::to_string(Count) + "\n");
91 }
92
94 {
95 std::unique_lock Lock{Mutex};
96 // Return if already inactive.
97 if (!Active)
98 return;
99
100 Active = false;
101 }
102 CondVar.notify_all();
103
104 for (auto &Thread : Threads)
105 if (Thread.joinable())
106 Thread.join();
107
108 Threads.clear();
109 }
110
111 bool isCompilationPending(HashT HashValue) {
112 std::unique_lock Lock{Mutex};
113 return !(CompilationResultMap.find(HashValue) ==
114 CompilationResultMap.end());
115 }
116
117 std::unique_ptr<MemoryBuffer> takeCompilationResult(HashT HashValue,
118 bool BlockingWait) {
120 std::unique_lock Lock{Mutex};
121 auto It = CompilationResultMap.find(HashValue);
122 if (It == CompilationResultMap.end())
123 return nullptr;
124
125 std::unique_ptr<CompilationResult> &CRes = It->second;
126
127 if (BlockingWait) {
128 // Release the lock while waiting for the result.
129 Lock.unlock();
130 CRes->wait();
131 // Reacquire the lock for synchronized access, the result is ready.
132 Lock.lock();
133 } else {
134 if (!CRes->isReady())
135 return nullptr;
136 }
137
138 // If compilation result is ready, take ownership of the buffer, erase it
139 // from the compilation results map and move the buffer to the caller.
140 std::unique_ptr<MemoryBuffer> ObjBuf = CRes->take();
141 // Use the HashValue key as the iterator may have been invalidated by
142 // insert/emplace from another thread.
143 CompilationResultMap.erase(HashValue);
144 Lock.unlock();
145 return ObjBuf;
146 }
147
148 CompilerAsync(int NumThreads) {
149 Active = true;
150 for (int I = 0; I < NumThreads; ++I)
151 Threads.emplace_back(&CompilerAsync::run, this);
152 }
153
155
156private:
157 // Joins threads before static objects are destroyed at exit (e.g.,
158 // COMGR/HIPRTC). Must be constructed after the first compile()) so that
159 // reverse destruction order ensures this runs first.
160 struct ShutdownGuard {
161 CompilerAsync *CA;
162 ~ShutdownGuard() {
163 if (CA)
164 CA->joinAllThreads();
165 }
166 };
167
168 bool Active;
169 std::mutex Mutex;
170 std::condition_variable CondVar;
171 std::unordered_map<HashT, std::unique_ptr<CompilationResult>>
172 CompilationResultMap;
173 std::deque<CompilationTask> Worklist;
174 std::vector<std::thread> Threads;
175};
176
177} // namespace proteus
178
179#endif
#define PROTEUS_DBG(x)
Definition Debug.h:9
#define TIMESCOPE(...)
Definition TimeTracing.h:66
Definition CompilerAsync.h:18
CompilationResult(CompilationResult &&) noexcept=delete
void set(std::unique_ptr< MemoryBuffer > ObjBuf)
Definition CompilerAsync.h:30
bool isReady()
Definition CompilerAsync.h:28
CompilationResult(const CompilationResult &)=delete
CompilationResult & operator=(const CompilationResult &)=delete
std::unique_ptr< MemoryBuffer > take()
Definition CompilerAsync.h:42
void wait()
Definition CompilerAsync.h:35
CompilationResult()
Definition CompilerAsync.h:20
Definition CompilationTask.h:19
HashT getHashValue() const
Definition CompilationTask.h:111
std::unique_ptr< MemoryBuffer > compile()
Definition CompilationTask.h:113
Definition CompilerAsync.h:49
bool isCompilationPending(HashT HashValue)
Definition CompilerAsync.h:111
CompilerAsync(int NumThreads)
Definition CompilerAsync.h:148
void compile(CompilationTask &&CT)
Definition CompilerAsync.h:51
std::unique_ptr< MemoryBuffer > takeCompilationResult(HashT HashValue, bool BlockingWait)
Definition CompilerAsync.h:117
void joinAllThreads()
Definition CompilerAsync.h:93
~CompilerAsync()
Definition CompilerAsync.h:154
void run()
Definition CompilerAsync.h:61
Definition Hashing.h:22
static llvm::raw_ostream & logs(const std::string &Name)
Definition Logger.h:19
Definition TimeTracingInit.h:29
Definition CompiledLibrary.h:7
Definition MemoryCache.h:27
void reportFatalError(const llvm::Twine &Reason, const char *FILE, unsigned Line)
Definition Error.cpp:14