Files
archived-llvm/include/llvm/ExecutionEngine/Orc/ThreadSafeModule.h
Lang Hames 3a5e132bb3 [ORC] Lock ThreadSafeContext during Module destructing in ThreadSafeModule.
Failure to lock the context can lead to data races if other threads are
operating on other ThreadSafeModules that share the same context.

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@343261 91177308-0d34-0410-b5e6-96231b3b80d8
2018-09-27 20:36:08 +00:00

154 lines
4.6 KiB
C++

//===----------- ThreadSafeModule.h -- Layer interfaces ---------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// Thread safe wrappers and utilities for Module and LLVMContext.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_EXECUTIONENGINE_ORC_THREADSAFEMODULEWRAPPER_H
#define LLVM_EXECUTIONENGINE_ORC_THREADSAFEMODULEWRAPPER_H
#include "llvm/IR/LLVMContext.h"
#include "llvm/IR/Module.h"
#include "llvm/Support/Compiler.h"
#include <functional>
#include <memory>
#include <mutex>
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<LLVMContext> Ctx)
: Ctx(std::move(Ctx)) {}
std::unique_ptr<LLVMContext> Ctx;
std::recursive_mutex Mutex;
};
public:
// RAII based lock for ThreadSafeContext.
class LLVM_NODISCARD Lock {
private:
using UnderlyingLock = std::lock_guard<std::recursive_mutex>;
public:
Lock(std::shared_ptr<State> S)
: S(std::move(S)),
L(llvm::make_unique<UnderlyingLock>(this->S->Mutex)) {}
private:
std::shared_ptr<State> S;
std::unique_ptr<UnderlyingLock> L;
};
/// Construct a null context.
ThreadSafeContext() = default;
/// Construct a ThreadSafeContext from the given LLVMContext.
ThreadSafeContext(std::unique_ptr<LLVMContext> NewCtx)
: S(std::make_shared<State>(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;
}
Lock getLock() {
assert(S && "Can not lock an empty ThreadSafeContext");
return Lock(S);
}
private:
std::shared_ptr<State> 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.
M = std::move(Other.M);
TSCtx = std::move(Other.TSCtx);
return *this;
}
/// Construct a ThreadSafeModule from a unique_ptr<Module> and a
/// unique_ptr<LLVMContext>. This creates a new ThreadSafeContext from the
/// given context.
ThreadSafeModule(std::unique_ptr<Module> M, std::unique_ptr<LLVMContext> Ctx)
: M(std::move(M)), TSCtx(std::move(Ctx)) {}
/// Construct a ThreadSafeModule from a unique_ptr<Module> and an
/// existing ThreadSafeContext.
ThreadSafeModule(std::unique_ptr<Module> 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 = getContextLock();
M = nullptr;
}
}
/// Get the module wrapped by this ThreadSafeModule.
Module* getModule() { return M.get(); }
/// Take out a lock on the ThreadSafeContext for this module.
ThreadSafeContext::Lock getContextLock() { return TSCtx.getLock(); }
/// Boolean conversion: This ThreadSafeModule will evaluate to true if it
/// wraps a non-null module.
explicit operator bool() {
if (M) {
assert(TSCtx.getContext() && "Non-null module must have non-null context");
return true;
}
return false;
}
private:
std::unique_ptr<Module> M;
ThreadSafeContext TSCtx;
};
using GVPredicate = std::function<bool(const GlobalValue&)>;
using GVModifier = std::function<void(GlobalValue&)>;
/// Clones the given module on to a new context.
ThreadSafeModule
cloneToNewContext(ThreadSafeModule &TSMW,
GVPredicate ShouldCloneDef = GVPredicate(),
GVModifier UpdateClonedDefSource = GVModifier());
} // End namespace orc
} // End namespace llvm
#endif // LLVM_EXECUTIONENGINE_ORC_THREADSAFEMODULEWRAPPER_H