1 | //===----------- ThreadSafeModule.h -- Layer interfaces ---------*- C++ -*-===// |
2 | // |
3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | // See https://llvm.org/LICENSE.txt for license information. |
5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | // |
9 | // Thread safe wrappers and utilities for Module and LLVMContext. |
10 | // |
11 | //===----------------------------------------------------------------------===// |
12 | |
13 | #ifndef LLVM_EXECUTIONENGINE_ORC_THREADSAFEMODULE_H |
14 | #define LLVM_EXECUTIONENGINE_ORC_THREADSAFEMODULE_H |
15 | |
16 | #include "llvm/IR/LLVMContext.h" |
17 | #include "llvm/IR/Module.h" |
18 | #include "llvm/Support/Compiler.h" |
19 | |
20 | #include <functional> |
21 | #include <memory> |
22 | #include <mutex> |
23 | |
24 | namespace llvm { |
25 | namespace orc { |
26 | |
27 | /// An LLVMContext together with an associated mutex that can be used to lock |
28 | /// the context to prevent concurrent access by other threads. |
29 | class ThreadSafeContext { |
30 | private: |
31 | struct State { |
32 | State(std::unique_ptr<LLVMContext> Ctx) : Ctx(std::move(Ctx)) {} |
33 | |
34 | std::unique_ptr<LLVMContext> Ctx; |
35 | std::recursive_mutex Mutex; |
36 | }; |
37 | |
38 | public: |
39 | // RAII based lock for ThreadSafeContext. |
40 | class [[nodiscard]] Lock { |
41 | public: |
42 | Lock(std::shared_ptr<State> S) : S(std::move(S)), L(this->S->Mutex) {} |
43 | |
44 | private: |
45 | std::shared_ptr<State> S; |
46 | std::unique_lock<std::recursive_mutex> L; |
47 | }; |
48 | |
49 | /// Construct a null context. |
50 | ThreadSafeContext() = default; |
51 | |
52 | /// Construct a ThreadSafeContext from the given LLVMContext. |
53 | ThreadSafeContext(std::unique_ptr<LLVMContext> NewCtx) |
54 | : S(std::make_shared<State>(args: std::move(NewCtx))) { |
55 | assert(S->Ctx != nullptr && |
56 | "Can not construct a ThreadSafeContext from a nullptr" ); |
57 | } |
58 | |
59 | /// Returns a pointer to the LLVMContext that was used to construct this |
60 | /// instance, or null if the instance was default constructed. |
61 | LLVMContext *getContext() { return S ? S->Ctx.get() : nullptr; } |
62 | |
63 | /// Returns a pointer to the LLVMContext that was used to construct this |
64 | /// instance, or null if the instance was default constructed. |
65 | const LLVMContext *getContext() const { return S ? S->Ctx.get() : nullptr; } |
66 | |
67 | Lock getLock() const { |
68 | assert(S && "Can not lock an empty ThreadSafeContext" ); |
69 | return Lock(S); |
70 | } |
71 | |
72 | private: |
73 | std::shared_ptr<State> S; |
74 | }; |
75 | |
76 | /// An LLVM Module together with a shared ThreadSafeContext. |
77 | class ThreadSafeModule { |
78 | public: |
79 | /// Default construct a ThreadSafeModule. This results in a null module and |
80 | /// null context. |
81 | ThreadSafeModule() = default; |
82 | |
83 | ThreadSafeModule(ThreadSafeModule &&Other) = default; |
84 | |
85 | ThreadSafeModule &operator=(ThreadSafeModule &&Other) { |
86 | // We have to explicitly define this move operator to copy the fields in |
87 | // reverse order (i.e. module first) to ensure the dependencies are |
88 | // protected: The old module that is being overwritten must be destroyed |
89 | // *before* the context that it depends on. |
90 | // We also need to lock the context to make sure the module tear-down |
91 | // does not overlap any other work on the context. |
92 | if (M) { |
93 | auto L = TSCtx.getLock(); |
94 | M = nullptr; |
95 | } |
96 | M = std::move(Other.M); |
97 | TSCtx = std::move(Other.TSCtx); |
98 | return *this; |
99 | } |
100 | |
101 | /// Construct a ThreadSafeModule from a unique_ptr<Module> and a |
102 | /// unique_ptr<LLVMContext>. This creates a new ThreadSafeContext from the |
103 | /// given context. |
104 | ThreadSafeModule(std::unique_ptr<Module> M, std::unique_ptr<LLVMContext> Ctx) |
105 | : M(std::move(M)), TSCtx(std::move(Ctx)) {} |
106 | |
107 | /// Construct a ThreadSafeModule from a unique_ptr<Module> and an |
108 | /// existing ThreadSafeContext. |
109 | ThreadSafeModule(std::unique_ptr<Module> M, ThreadSafeContext TSCtx) |
110 | : M(std::move(M)), TSCtx(std::move(TSCtx)) {} |
111 | |
112 | ~ThreadSafeModule() { |
113 | // We need to lock the context while we destruct the module. |
114 | if (M) { |
115 | auto L = TSCtx.getLock(); |
116 | M = nullptr; |
117 | } |
118 | } |
119 | |
120 | /// Boolean conversion: This ThreadSafeModule will evaluate to true if it |
121 | /// wraps a non-null module. |
122 | explicit operator bool() const { |
123 | if (M) { |
124 | assert(TSCtx.getContext() && |
125 | "Non-null module must have non-null context" ); |
126 | return true; |
127 | } |
128 | return false; |
129 | } |
130 | |
131 | /// Locks the associated ThreadSafeContext and calls the given function |
132 | /// on the contained Module. |
133 | template <typename Func> decltype(auto) withModuleDo(Func &&F) { |
134 | assert(M && "Can not call on null module" ); |
135 | auto Lock = TSCtx.getLock(); |
136 | return F(*M); |
137 | } |
138 | |
139 | /// Locks the associated ThreadSafeContext and calls the given function |
140 | /// on the contained Module. |
141 | template <typename Func> decltype(auto) withModuleDo(Func &&F) const { |
142 | assert(M && "Can not call on null module" ); |
143 | auto Lock = TSCtx.getLock(); |
144 | return F(*M); |
145 | } |
146 | |
147 | /// Locks the associated ThreadSafeContext and calls the given function, |
148 | /// passing the contained std::unique_ptr<Module>. The given function should |
149 | /// consume the Module. |
150 | template <typename Func> decltype(auto) consumingModuleDo(Func &&F) { |
151 | auto Lock = TSCtx.getLock(); |
152 | return F(std::move(M)); |
153 | } |
154 | |
155 | /// Get a raw pointer to the contained module without locking the context. |
156 | Module *getModuleUnlocked() { return M.get(); } |
157 | |
158 | /// Get a raw pointer to the contained module without locking the context. |
159 | const Module *getModuleUnlocked() const { return M.get(); } |
160 | |
161 | /// Returns the context for this ThreadSafeModule. |
162 | ThreadSafeContext getContext() const { return TSCtx; } |
163 | |
164 | private: |
165 | std::unique_ptr<Module> M; |
166 | ThreadSafeContext TSCtx; |
167 | }; |
168 | |
169 | using GVPredicate = std::function<bool(const GlobalValue &)>; |
170 | using GVModifier = std::function<void(GlobalValue &)>; |
171 | |
172 | /// Clones the given module on to a new context. |
173 | ThreadSafeModule |
174 | cloneToNewContext(const ThreadSafeModule &TSMW, |
175 | GVPredicate ShouldCloneDef = GVPredicate(), |
176 | GVModifier UpdateClonedDefSource = GVModifier()); |
177 | |
178 | } // End namespace orc |
179 | } // End namespace llvm |
180 | |
181 | #endif // LLVM_EXECUTIONENGINE_ORC_THREADSAFEMODULE_H |
182 | |