ThreadSafeModule.h revision 360784
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_THREADSAFEMODULEWRAPPER_H
14#define LLVM_EXECUTIONENGINE_ORC_THREADSAFEMODULEWRAPPER_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
24namespace llvm {
25namespace 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.
29class ThreadSafeContext {
30private:
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
38public:
39  // RAII based lock for ThreadSafeContext.
40  class LLVM_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>(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
72private:
73  std::shared_ptr<State> S;
74};
75
76/// An LLVM Module together with a shared ThreadSafeContext.
77class ThreadSafeModule {
78public:
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>
134  auto withModuleDo(Func &&F) -> decltype(F(std::declval<Module &>())) {
135    assert(M && "Can not call on null module");
136    auto Lock = TSCtx.getLock();
137    return F(*M);
138  }
139
140  /// Locks the associated ThreadSafeContext and calls the given function
141  /// on the contained Module.
142  template <typename Func>
143  auto withModuleDo(Func &&F) const
144      -> decltype(F(std::declval<const Module &>())) {
145    auto Lock = TSCtx.getLock();
146    return F(*M);
147  }
148
149  /// Get a raw pointer to the contained module without locking the context.
150  Module *getModuleUnlocked() { return M.get(); }
151
152  /// Get a raw pointer to the contained module without locking the context.
153  const Module *getModuleUnlocked() const { return M.get(); }
154
155  /// Returns the context for this ThreadSafeModule.
156  ThreadSafeContext getContext() const { return TSCtx; }
157
158private:
159  std::unique_ptr<Module> M;
160  ThreadSafeContext TSCtx;
161};
162
163using GVPredicate = std::function<bool(const GlobalValue &)>;
164using GVModifier = std::function<void(GlobalValue &)>;
165
166/// Clones the given module on to a new context.
167ThreadSafeModule
168cloneToNewContext(ThreadSafeModule &TSMW,
169                  GVPredicate ShouldCloneDef = GVPredicate(),
170                  GVModifier UpdateClonedDefSource = GVModifier());
171
172} // End namespace orc
173} // End namespace llvm
174
175#endif // LLVM_EXECUTIONENGINE_ORC_THREADSAFEMODULEWRAPPER_H
176