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