//===----------- ThreadSafeModule.h -- Layer interfaces ---------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // Thread safe wrappers and utilities for Module and LLVMContext. // //===----------------------------------------------------------------------===// #ifndef LLVM_EXECUTIONENGINE_ORC_THREADSAFEMODULE_H #define LLVM_EXECUTIONENGINE_ORC_THREADSAFEMODULE_H #include "llvm/IR/LLVMContext.h" #include "llvm/IR/Module.h" #include "llvm/Support/Compiler.h" #include #include #include namespace llvm { namespace orc { /// An LLVMContext together with an associated mutex that can be used to lock /// the context to prevent concurrent access by other threads. class ThreadSafeContext { private: struct State { State(std::unique_ptr Ctx) : Ctx(std::move(Ctx)) {} std::unique_ptr Ctx; std::recursive_mutex Mutex; }; public: // RAII based lock for ThreadSafeContext. class [[nodiscard]] Lock { public: Lock(std::shared_ptr S) : S(std::move(S)), L(this->S->Mutex) {} private: std::shared_ptr S; std::unique_lock L; }; /// Construct a null context. ThreadSafeContext() = default; /// Construct a ThreadSafeContext from the given LLVMContext. ThreadSafeContext(std::unique_ptr NewCtx) : S(std::make_shared(std::move(NewCtx))) { assert(S->Ctx != nullptr && "Can not construct a ThreadSafeContext from a nullptr"); } /// Returns a pointer to the LLVMContext that was used to construct this /// instance, or null if the instance was default constructed. LLVMContext *getContext() { return S ? S->Ctx.get() : nullptr; } /// Returns a pointer to the LLVMContext that was used to construct this /// instance, or null if the instance was default constructed. const LLVMContext *getContext() const { return S ? S->Ctx.get() : nullptr; } Lock getLock() const { assert(S && "Can not lock an empty ThreadSafeContext"); return Lock(S); } private: std::shared_ptr S; }; /// An LLVM Module together with a shared ThreadSafeContext. class ThreadSafeModule { public: /// Default construct a ThreadSafeModule. This results in a null module and /// null context. ThreadSafeModule() = default; ThreadSafeModule(ThreadSafeModule &&Other) = default; ThreadSafeModule &operator=(ThreadSafeModule &&Other) { // We have to explicitly define this move operator to copy the fields in // reverse order (i.e. module first) to ensure the dependencies are // protected: The old module that is being overwritten must be destroyed // *before* the context that it depends on. // We also need to lock the context to make sure the module tear-down // does not overlap any other work on the context. if (M) { auto L = TSCtx.getLock(); M = nullptr; } M = std::move(Other.M); TSCtx = std::move(Other.TSCtx); return *this; } /// Construct a ThreadSafeModule from a unique_ptr and a /// unique_ptr. This creates a new ThreadSafeContext from the /// given context. ThreadSafeModule(std::unique_ptr M, std::unique_ptr Ctx) : M(std::move(M)), TSCtx(std::move(Ctx)) {} /// Construct a ThreadSafeModule from a unique_ptr and an /// existing ThreadSafeContext. ThreadSafeModule(std::unique_ptr M, ThreadSafeContext TSCtx) : M(std::move(M)), TSCtx(std::move(TSCtx)) {} ~ThreadSafeModule() { // We need to lock the context while we destruct the module. if (M) { auto L = TSCtx.getLock(); M = nullptr; } } /// Boolean conversion: This ThreadSafeModule will evaluate to true if it /// wraps a non-null module. explicit operator bool() const { if (M) { assert(TSCtx.getContext() && "Non-null module must have non-null context"); return true; } return false; } /// Locks the associated ThreadSafeContext and calls the given function /// on the contained Module. template decltype(auto) withModuleDo(Func &&F) { assert(M && "Can not call on null module"); auto Lock = TSCtx.getLock(); return F(*M); } /// Locks the associated ThreadSafeContext and calls the given function /// on the contained Module. template decltype(auto) withModuleDo(Func &&F) const { assert(M && "Can not call on null module"); auto Lock = TSCtx.getLock(); return F(*M); } /// Locks the associated ThreadSafeContext and calls the given function, /// passing the contained std::unique_ptr. The given function should /// consume the Module. template decltype(auto) consumingModuleDo(Func &&F) { auto Lock = TSCtx.getLock(); return F(std::move(M)); } /// Get a raw pointer to the contained module without locking the context. Module *getModuleUnlocked() { return M.get(); } /// Get a raw pointer to the contained module without locking the context. const Module *getModuleUnlocked() const { return M.get(); } /// Returns the context for this ThreadSafeModule. ThreadSafeContext getContext() const { return TSCtx; } private: std::unique_ptr M; ThreadSafeContext TSCtx; }; using GVPredicate = std::function; using GVModifier = std::function; /// Clones the given module on to a new context. ThreadSafeModule cloneToNewContext(const ThreadSafeModule &TSMW, GVPredicate ShouldCloneDef = GVPredicate(), GVModifier UpdateClonedDefSource = GVModifier()); } // End namespace orc } // End namespace llvm #endif // LLVM_EXECUTIONENGINE_ORC_THREADSAFEMODULE_H