[llvm] 1b1f1c7 - Re-re-apply 5acd47169884, Add a shared-memory based orc::MemoryMapper...
Lang Hames via llvm-commits
llvm-commits at lists.llvm.org
Tue Jul 19 15:35:40 PDT 2022
Author: Anubhab Ghosh
Date: 2022-07-19T15:35:33-07:00
New Revision: 1b1f1c778695442c2683813ec7d4b557bb279e26
URL: https://github.com/llvm/llvm-project/commit/1b1f1c778695442c2683813ec7d4b557bb279e26
DIFF: https://github.com/llvm/llvm-project/commit/1b1f1c778695442c2683813ec7d4b557bb279e26.diff
LOG: Re-re-apply 5acd47169884, Add a shared-memory based orc::MemoryMapper...
...with more fixes.
The original patch was reverted in 3e9cc543f22 due to bot failures caused by
a missing dependence on librt. That issue was fixed in 32d8d23cd0, but that
commit also broke sanitizer bots due to a bug in SimplePackedSerialization:
empty ArrayRef<char>s triggered a zero-byte memcpy from a null source. The
ArrayRef<char> serialization issue was fixed in 67220c2ad7, and this patch has
also been updated with a new custom SharedMemorySegFinalizeRequest message that
should avoid serializing empty ArrayRefs in the first place.
https://reviews.llvm.org/D128544
Added:
llvm/include/llvm/ExecutionEngine/Orc/TargetProcess/ExecutorSharedMemoryMapperService.h
llvm/lib/ExecutionEngine/Orc/TargetProcess/ExecutorSharedMemoryMapperService.cpp
llvm/unittests/ExecutionEngine/Orc/SharedMemoryMapperTest.cpp
Modified:
llvm/include/llvm/ExecutionEngine/Orc/MemoryMapper.h
llvm/include/llvm/ExecutionEngine/Orc/Shared/OrcRTBridge.h
llvm/include/llvm/ExecutionEngine/Orc/Shared/TargetProcessControlTypes.h
llvm/lib/ExecutionEngine/Orc/CMakeLists.txt
llvm/lib/ExecutionEngine/Orc/MemoryMapper.cpp
llvm/lib/ExecutionEngine/Orc/Shared/OrcRTBridge.cpp
llvm/lib/ExecutionEngine/Orc/TargetProcess/CMakeLists.txt
llvm/unittests/ExecutionEngine/Orc/CMakeLists.txt
Removed:
################################################################################
diff --git a/llvm/include/llvm/ExecutionEngine/Orc/MemoryMapper.h b/llvm/include/llvm/ExecutionEngine/Orc/MemoryMapper.h
index d023bfbdb5b6f..ba27bceb89884 100644
--- a/llvm/include/llvm/ExecutionEngine/Orc/MemoryMapper.h
+++ b/llvm/include/llvm/ExecutionEngine/Orc/MemoryMapper.h
@@ -109,6 +109,47 @@ class InProcessMemoryMapper final : public MemoryMapper {
AllocationMap Allocations;
};
+class SharedMemoryMapper final : public MemoryMapper {
+public:
+ struct SymbolAddrs {
+ ExecutorAddr Instance;
+ ExecutorAddr Reserve;
+ ExecutorAddr Initialize;
+ ExecutorAddr Deinitialize;
+ ExecutorAddr Release;
+ };
+
+ SharedMemoryMapper(ExecutorProcessControl &EPC, SymbolAddrs SAs)
+ : EPC(EPC), SAs(SAs) {}
+
+ void reserve(size_t NumBytes, OnReservedFunction OnReserved) override;
+
+ char *prepare(ExecutorAddr Addr, size_t ContentSize) override;
+
+ void initialize(AllocInfo &AI, OnInitializedFunction OnInitialized) override;
+
+ void deinitialize(ArrayRef<ExecutorAddr> Allocations,
+ OnDeinitializedFunction OnDeInitialized) override;
+
+ void release(ArrayRef<ExecutorAddr> Reservations,
+ OnReleasedFunction OnRelease) override;
+
+ ~SharedMemoryMapper() override;
+
+private:
+ struct Reservation {
+ void *LocalAddr;
+ size_t Size;
+ };
+
+ ExecutorProcessControl &EPC;
+ SymbolAddrs SAs;
+
+ std::mutex Mutex;
+
+ std::map<ExecutorAddr, Reservation> Reservations;
+};
+
} // namespace orc
} // end namespace llvm
diff --git a/llvm/include/llvm/ExecutionEngine/Orc/Shared/OrcRTBridge.h b/llvm/include/llvm/ExecutionEngine/Orc/Shared/OrcRTBridge.h
index 96166ac20b2e3..2aedf1e44ad8b 100644
--- a/llvm/include/llvm/ExecutionEngine/Orc/Shared/OrcRTBridge.h
+++ b/llvm/include/llvm/ExecutionEngine/Orc/Shared/OrcRTBridge.h
@@ -31,6 +31,12 @@ extern const char *SimpleExecutorMemoryManagerReserveWrapperName;
extern const char *SimpleExecutorMemoryManagerFinalizeWrapperName;
extern const char *SimpleExecutorMemoryManagerDeallocateWrapperName;
+extern const char *ExecutorSharedMemoryMapperServiceInstanceName;
+extern const char *ExecutorSharedMemoryMapperServiceReserveWrapperName;
+extern const char *ExecutorSharedMemoryMapperServiceInitializeWrapperName;
+extern const char *ExecutorSharedMemoryMapperServiceDeinitializeWrapperName;
+extern const char *ExecutorSharedMemoryMapperServiceReleaseWrapperName;
+
extern const char *MemoryWriteUInt8sWrapperName;
extern const char *MemoryWriteUInt16sWrapperName;
extern const char *MemoryWriteUInt32sWrapperName;
@@ -58,6 +64,21 @@ using SPSSimpleExecutorMemoryManagerFinalizeSignature =
using SPSSimpleExecutorMemoryManagerDeallocateSignature = shared::SPSError(
shared::SPSExecutorAddr, shared::SPSSequence<shared::SPSExecutorAddr>);
+// ExecutorSharedMemoryMapperService
+using SPSExecutorSharedMemoryMapperServiceReserveSignature =
+ shared::SPSExpected<
+ shared::SPSTuple<shared::SPSExecutorAddr, shared::SPSString>>(
+ shared::SPSExecutorAddr, uint64_t);
+using SPSExecutorSharedMemoryMapperServiceInitializeSignature =
+ shared::SPSExpected<shared::SPSExecutorAddr>(
+ shared::SPSExecutorAddr, shared::SPSExecutorAddr,
+ shared::SPSSharedMemoryFinalizeRequest);
+using SPSExecutorSharedMemoryMapperServiceDeinitializeSignature =
+ shared::SPSError(shared::SPSExecutorAddr,
+ shared::SPSSequence<shared::SPSExecutorAddr>);
+using SPSExecutorSharedMemoryMapperServiceReleaseSignature = shared::SPSError(
+ shared::SPSExecutorAddr, shared::SPSSequence<shared::SPSExecutorAddr>);
+
using SPSRunAsMainSignature = int64_t(shared::SPSExecutorAddr,
shared::SPSSequence<shared::SPSString>);
diff --git a/llvm/include/llvm/ExecutionEngine/Orc/Shared/TargetProcessControlTypes.h b/llvm/include/llvm/ExecutionEngine/Orc/Shared/TargetProcessControlTypes.h
index d596a89a50b63..502c7c1f70694 100644
--- a/llvm/include/llvm/ExecutionEngine/Orc/Shared/TargetProcessControlTypes.h
+++ b/llvm/include/llvm/ExecutionEngine/Orc/Shared/TargetProcessControlTypes.h
@@ -82,6 +82,17 @@ struct FinalizeRequest {
shared::AllocActions Actions;
};
+struct SharedMemorySegFinalizeRequest {
+ WireProtectionFlags Prot;
+ ExecutorAddr Addr;
+ uint64_t Size;
+};
+
+struct SharedMemoryFinalizeRequest {
+ std::vector<SharedMemorySegFinalizeRequest> Segments;
+ shared::AllocActions Actions;
+};
+
template <typename T> struct UIntWrite {
UIntWrite() = default;
UIntWrite(ExecutorAddr Addr, T Value) : Addr(Addr), Value(Value) {}
@@ -131,6 +142,13 @@ using SPSSegFinalizeRequest =
using SPSFinalizeRequest = SPSTuple<SPSSequence<SPSSegFinalizeRequest>,
SPSSequence<SPSAllocActionCallPair>>;
+using SPSSharedMemorySegFinalizeRequest =
+ SPSTuple<SPSMemoryProtectionFlags, SPSExecutorAddr, uint64_t>;
+
+using SPSSharedMemoryFinalizeRequest =
+ SPSTuple<SPSSequence<SPSSharedMemorySegFinalizeRequest>,
+ SPSSequence<SPSAllocActionCallPair>>;
+
template <typename T>
using SPSMemoryAccessUIntWrite = SPSTuple<SPSExecutorAddr, T>;
@@ -204,6 +222,48 @@ class SPSSerializationTraits<SPSFinalizeRequest, tpctypes::FinalizeRequest> {
}
};
+template <>
+class SPSSerializationTraits<SPSSharedMemorySegFinalizeRequest,
+ tpctypes::SharedMemorySegFinalizeRequest> {
+ using SFRAL = SPSSharedMemorySegFinalizeRequest::AsArgList;
+
+public:
+ static size_t size(const tpctypes::SharedMemorySegFinalizeRequest &SFR) {
+ return SFRAL::size(SFR.Prot, SFR.Addr, SFR.Size);
+ }
+
+ static bool serialize(SPSOutputBuffer &OB,
+ const tpctypes::SharedMemorySegFinalizeRequest &SFR) {
+ return SFRAL::serialize(OB, SFR.Prot, SFR.Addr, SFR.Size);
+ }
+
+ static bool deserialize(SPSInputBuffer &IB,
+ tpctypes::SharedMemorySegFinalizeRequest &SFR) {
+ return SFRAL::deserialize(IB, SFR.Prot, SFR.Addr, SFR.Size);
+ }
+};
+
+template <>
+class SPSSerializationTraits<SPSSharedMemoryFinalizeRequest,
+ tpctypes::SharedMemoryFinalizeRequest> {
+ using FRAL = SPSSharedMemoryFinalizeRequest::AsArgList;
+
+public:
+ static size_t size(const tpctypes::SharedMemoryFinalizeRequest &FR) {
+ return FRAL::size(FR.Segments, FR.Actions);
+ }
+
+ static bool serialize(SPSOutputBuffer &OB,
+ const tpctypes::SharedMemoryFinalizeRequest &FR) {
+ return FRAL::serialize(OB, FR.Segments, FR.Actions);
+ }
+
+ static bool deserialize(SPSInputBuffer &IB,
+ tpctypes::SharedMemoryFinalizeRequest &FR) {
+ return FRAL::deserialize(IB, FR.Segments, FR.Actions);
+ }
+};
+
template <typename T>
class SPSSerializationTraits<SPSMemoryAccessUIntWrite<T>,
tpctypes::UIntWrite<T>> {
@@ -244,7 +304,6 @@ class SPSSerializationTraits<SPSMemoryAccessBufferWrite,
}
};
-
} // end namespace shared
} // end namespace orc
} // end namespace llvm
diff --git a/llvm/include/llvm/ExecutionEngine/Orc/TargetProcess/ExecutorSharedMemoryMapperService.h b/llvm/include/llvm/ExecutionEngine/Orc/TargetProcess/ExecutorSharedMemoryMapperService.h
new file mode 100644
index 0000000000000..69d8cf5d29806
--- /dev/null
+++ b/llvm/include/llvm/ExecutionEngine/Orc/TargetProcess/ExecutorSharedMemoryMapperService.h
@@ -0,0 +1,78 @@
+//===----------- ExecutorSharedMemoryMapperService.h ------------*- 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_EXECUTIONENGINE_ORC_TARGETPROCESS_EXECUTORSHAREDMEMORYMAPPERSERVICE
+#define LLVM_EXECUTIONENGINE_ORC_TARGETPROCESS_EXECUTORSHAREDMEMORYMAPPERSERVICE
+
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/ExecutionEngine/Orc/Shared/TargetProcessControlTypes.h"
+#include "llvm/ExecutionEngine/Orc/TargetProcess/ExecutorBootstrapService.h"
+
+#include <atomic>
+#include <mutex>
+
+#if defined(_WIN32)
+#include <windows.h>
+#endif
+
+namespace llvm {
+namespace orc {
+namespace rt_bootstrap {
+
+class ExecutorSharedMemoryMapperService final
+ : public ExecutorBootstrapService {
+public:
+ ~ExecutorSharedMemoryMapperService(){};
+
+ Expected<std::pair<ExecutorAddr, std::string>> reserve(uint64_t Size);
+ Expected<ExecutorAddr> initialize(ExecutorAddr Reservation,
+ tpctypes::SharedMemoryFinalizeRequest &FR);
+
+ Error deinitialize(const std::vector<ExecutorAddr> &Bases);
+ Error release(const std::vector<ExecutorAddr> &Bases);
+
+ Error shutdown() override;
+ void addBootstrapSymbols(StringMap<ExecutorAddr> &M) override;
+
+private:
+ struct Allocation {
+ std::vector<shared::WrapperFunctionCall> DeinitializationActions;
+ };
+ using AllocationMap = DenseMap<ExecutorAddr, Allocation>;
+
+ struct Reservation {
+ size_t Size;
+ std::vector<ExecutorAddr> Allocations;
+#if defined(_WIN32)
+ HANDLE SharedMemoryFile;
+#endif
+ };
+ using ReservationMap = DenseMap<void *, Reservation>;
+
+ static llvm::orc::shared::CWrapperFunctionResult
+ reserveWrapper(const char *ArgData, size_t ArgSize);
+
+ static llvm::orc::shared::CWrapperFunctionResult
+ initializeWrapper(const char *ArgData, size_t ArgSize);
+
+ static llvm::orc::shared::CWrapperFunctionResult
+ deinitializeWrapper(const char *ArgData, size_t ArgSize);
+
+ static llvm::orc::shared::CWrapperFunctionResult
+ releaseWrapper(const char *ArgData, size_t ArgSize);
+
+ std::atomic<int> SharedMemoryCount{0};
+ std::mutex Mutex;
+ ReservationMap Reservations;
+ AllocationMap Allocations;
+};
+
+} // namespace rt_bootstrap
+} // namespace orc
+} // namespace llvm
+#endif // LLVM_EXECUTIONENGINE_ORC_TARGETPROCESS_EXECUTORSHAREDMEMORYMAPPERSERVICE
diff --git a/llvm/lib/ExecutionEngine/Orc/CMakeLists.txt b/llvm/lib/ExecutionEngine/Orc/CMakeLists.txt
index 1c9b9e8a1b31e..130e542f246e6 100644
--- a/llvm/lib/ExecutionEngine/Orc/CMakeLists.txt
+++ b/llvm/lib/ExecutionEngine/Orc/CMakeLists.txt
@@ -1,3 +1,7 @@
+if( CMAKE_HOST_UNIX AND HAVE_LIBRT )
+ set(rt_lib rt)
+endif()
+
add_llvm_component_library(LLVMOrcJIT
CompileOnDemandLayer.cpp
CompileUtils.cpp
@@ -45,6 +49,7 @@ add_llvm_component_library(LLVMOrcJIT
LINK_LIBS
${LLVM_PTHREAD_LIB}
+ ${rt_lib}
LINK_COMPONENTS
Core
diff --git a/llvm/lib/ExecutionEngine/Orc/MemoryMapper.cpp b/llvm/lib/ExecutionEngine/Orc/MemoryMapper.cpp
index 8b3fbd7117e2a..b38de20ec5400 100644
--- a/llvm/lib/ExecutionEngine/Orc/MemoryMapper.cpp
+++ b/llvm/lib/ExecutionEngine/Orc/MemoryMapper.cpp
@@ -8,6 +8,17 @@
#include "llvm/ExecutionEngine/Orc/MemoryMapper.h"
+#include "llvm/ExecutionEngine/Orc/Shared/OrcRTBridge.h"
+#include "llvm/Support/WindowsError.h"
+
+#if defined(LLVM_ON_UNIX)
+#include <fcntl.h>
+#include <sys/mman.h>
+#include <unistd.h>
+#elif defined(_WIN32)
+#include <windows.h>
+#endif
+
namespace llvm {
namespace orc {
@@ -147,6 +158,225 @@ InProcessMemoryMapper::~InProcessMemoryMapper() {
cantFail(F.get());
}
+// SharedMemoryMapper
+
+void SharedMemoryMapper::reserve(size_t NumBytes,
+ OnReservedFunction OnReserved) {
+#if defined(LLVM_ON_UNIX) || defined(_WIN32)
+
+ EPC.callSPSWrapperAsync<
+ rt::SPSExecutorSharedMemoryMapperServiceReserveSignature>(
+ SAs.Reserve,
+ [this, NumBytes, OnReserved = std::move(OnReserved)](
+ Error SerializationErr,
+ Expected<std::pair<ExecutorAddr, std::string>> Result) mutable {
+ if (SerializationErr) {
+ cantFail(Result.takeError());
+ return OnReserved(std::move(SerializationErr));
+ }
+
+ if (!Result)
+ return OnReserved(Result.takeError());
+
+ ExecutorAddr RemoteAddr;
+ std::string SharedMemoryName;
+ std::tie(RemoteAddr, SharedMemoryName) = std::move(*Result);
+
+ void *LocalAddr = nullptr;
+
+#if defined(LLVM_ON_UNIX)
+
+ int SharedMemoryFile = shm_open(SharedMemoryName.c_str(), O_RDWR, 0700);
+ if (SharedMemoryFile < 0) {
+ return OnReserved(errorCodeToError(
+ std::error_code(errno, std::generic_category())));
+ }
+
+ // this prevents other processes from accessing it by name
+ shm_unlink(SharedMemoryName.c_str());
+
+ LocalAddr = mmap(nullptr, NumBytes, PROT_READ | PROT_WRITE, MAP_SHARED,
+ SharedMemoryFile, 0);
+ if (LocalAddr == MAP_FAILED) {
+ return OnReserved(errorCodeToError(
+ std::error_code(errno, std::generic_category())));
+ }
+
+ close(SharedMemoryFile);
+
+#elif defined(_WIN32)
+
+ std::wstring WideSharedMemoryName(SharedMemoryName.begin(),
+ SharedMemoryName.end());
+ HANDLE SharedMemoryFile = OpenFileMappingW(
+ FILE_MAP_ALL_ACCESS, FALSE, WideSharedMemoryName.c_str());
+ if (!SharedMemoryFile)
+ return OnReserved(errorCodeToError(mapWindowsError(GetLastError())));
+
+ LocalAddr =
+ MapViewOfFile(SharedMemoryFile, FILE_MAP_ALL_ACCESS, 0, 0, 0);
+ if (!LocalAddr) {
+ CloseHandle(SharedMemoryFile);
+ return OnReserved(errorCodeToError(mapWindowsError(GetLastError())));
+ }
+
+ CloseHandle(SharedMemoryFile);
+
+#endif
+ {
+ std::lock_guard<std::mutex> Lock(Mutex);
+ Reservations.insert({RemoteAddr, {LocalAddr, NumBytes}});
+ }
+
+ OnReserved(ExecutorAddrRange(RemoteAddr, NumBytes));
+ },
+ SAs.Instance, static_cast<uint64_t>(NumBytes));
+
+#else
+ OnReserved(make_error<StringError>(
+ "SharedMemoryMapper is not supported on this platform yet",
+ inconvertibleErrorCode()));
+#endif
+}
+
+char *SharedMemoryMapper::prepare(ExecutorAddr Addr, size_t ContentSize) {
+ auto R = Reservations.upper_bound(Addr);
+ assert(R != Reservations.begin() && "Attempt to prepare unknown range");
+ R--;
+
+ ExecutorAddrDiff Offset = Addr - R->first;
+
+ return static_cast<char *>(R->second.LocalAddr) + Offset;
+}
+
+void SharedMemoryMapper::initialize(MemoryMapper::AllocInfo &AI,
+ OnInitializedFunction OnInitialized) {
+ auto Reservation = Reservations.find(AI.MappingBase);
+ assert(Reservation != Reservations.end() &&
+ "Attempt to initialize unreserved range");
+
+ tpctypes::SharedMemoryFinalizeRequest FR;
+
+ AI.Actions.swap(FR.Actions);
+
+ FR.Segments.reserve(AI.Segments.size());
+
+ for (auto Segment : AI.Segments) {
+ char *Base =
+ static_cast<char *>(Reservation->second.LocalAddr) + Segment.Offset;
+ std::memset(Base + Segment.ContentSize, 0, Segment.ZeroFillSize);
+
+ tpctypes::SharedMemorySegFinalizeRequest SegReq;
+ SegReq.Prot = tpctypes::toWireProtectionFlags(
+ static_cast<sys::Memory::ProtectionFlags>(Segment.Prot));
+ SegReq.Addr = AI.MappingBase + Segment.Offset;
+ SegReq.Size = Segment.ContentSize + Segment.ZeroFillSize;
+
+ FR.Segments.push_back(SegReq);
+ }
+
+ EPC.callSPSWrapperAsync<
+ rt::SPSExecutorSharedMemoryMapperServiceInitializeSignature>(
+ SAs.Initialize,
+ [OnInitialized = std::move(OnInitialized)](
+ Error SerializationErr, Expected<ExecutorAddr> Result) mutable {
+ if (SerializationErr) {
+ cantFail(Result.takeError());
+ return OnInitialized(std::move(SerializationErr));
+ }
+
+ OnInitialized(std::move(Result));
+ },
+ SAs.Instance, AI.MappingBase, std::move(FR));
+}
+
+void SharedMemoryMapper::deinitialize(
+ ArrayRef<ExecutorAddr> Allocations,
+ MemoryMapper::OnDeinitializedFunction OnDeinitialized) {
+ EPC.callSPSWrapperAsync<
+ rt::SPSExecutorSharedMemoryMapperServiceDeinitializeSignature>(
+ SAs.Deinitialize,
+ [OnDeinitialized = std::move(OnDeinitialized)](Error SerializationErr,
+ Error Result) mutable {
+ if (SerializationErr) {
+ cantFail(std::move(Result));
+ return OnDeinitialized(std::move(SerializationErr));
+ }
+
+ OnDeinitialized(std::move(Result));
+ },
+ SAs.Instance, Allocations);
+}
+
+void SharedMemoryMapper::release(ArrayRef<ExecutorAddr> Bases,
+ OnReleasedFunction OnReleased) {
+#if defined(LLVM_ON_UNIX) || defined(_WIN32)
+ Error Err = Error::success();
+
+ {
+ std::lock_guard<std::mutex> Lock(Mutex);
+
+ for (auto Base : Bases) {
+
+#if defined(LLVM_ON_UNIX)
+
+ if (munmap(Reservations[Base].LocalAddr, Reservations[Base].Size) != 0)
+ Err = joinErrors(std::move(Err), errorCodeToError(std::error_code(
+ errno, std::generic_category())));
+
+#elif defined(_WIN32)
+
+ if (!UnmapViewOfFile(Reservations[Base].LocalAddr))
+ joinErrors(std::move(Err),
+ errorCodeToError(mapWindowsError(GetLastError())));
+
+#endif
+
+ Reservations.erase(Base);
+ }
+ }
+
+ EPC.callSPSWrapperAsync<
+ rt::SPSExecutorSharedMemoryMapperServiceReleaseSignature>(
+ SAs.Release,
+ [OnReleased = std::move(OnReleased),
+ Err = std::move(Err)](Error SerializationErr, Error Result) mutable {
+ if (SerializationErr) {
+ cantFail(std::move(Result));
+ return OnReleased(
+ joinErrors(std::move(Err), std::move(SerializationErr)));
+ }
+
+ return OnReleased(joinErrors(std::move(Err), std::move(Result)));
+ },
+ SAs.Instance, Bases);
+#else
+ OnReleased(make_error<StringError>(
+ "SharedMemoryMapper is not supported on this platform yet",
+ inconvertibleErrorCode()));
+#endif
+}
+
+SharedMemoryMapper::~SharedMemoryMapper() {
+ std::vector<ExecutorAddr> ReservationAddrs;
+ if (!Reservations.empty()) {
+ std::lock_guard<std::mutex> Lock(Mutex);
+ {
+ ReservationAddrs.reserve(Reservations.size());
+ for (const auto &R : Reservations) {
+ ReservationAddrs.push_back(R.first);
+ }
+ }
+ }
+
+ std::promise<MSVCPError> P;
+ auto F = P.get_future();
+ release(ReservationAddrs, [&](Error Err) { P.set_value(std::move(Err)); });
+ // FIXME: Release can actually fail. The error should be propagated.
+ // Meanwhile, a better option is to explicitly call release().
+ cantFail(F.get());
+}
+
} // namespace orc
} // namespace llvm
diff --git a/llvm/lib/ExecutionEngine/Orc/Shared/OrcRTBridge.cpp b/llvm/lib/ExecutionEngine/Orc/Shared/OrcRTBridge.cpp
index 5eae33121eb9c..dfdd846c46a75 100644
--- a/llvm/lib/ExecutionEngine/Orc/Shared/OrcRTBridge.cpp
+++ b/llvm/lib/ExecutionEngine/Orc/Shared/OrcRTBridge.cpp
@@ -18,6 +18,7 @@ const char *SimpleExecutorDylibManagerOpenWrapperName =
"__llvm_orc_SimpleExecutorDylibManager_open_wrapper";
const char *SimpleExecutorDylibManagerLookupWrapperName =
"__llvm_orc_SimpleExecutorDylibManager_lookup_wrapper";
+
const char *SimpleExecutorMemoryManagerInstanceName =
"__llvm_orc_SimpleExecutorMemoryManager_Instance";
const char *SimpleExecutorMemoryManagerReserveWrapperName =
@@ -26,6 +27,18 @@ const char *SimpleExecutorMemoryManagerFinalizeWrapperName =
"__llvm_orc_SimpleExecutorMemoryManager_finalize_wrapper";
const char *SimpleExecutorMemoryManagerDeallocateWrapperName =
"__llvm_orc_SimpleExecutorMemoryManager_deallocate_wrapper";
+
+const char *ExecutorSharedMemoryMapperServiceInstanceName =
+ "__llvm_orc_ExecutorSharedMemoryMapperService_Instance";
+const char *ExecutorSharedMemoryMapperServiceReserveWrapperName =
+ "__llvm_orc_ExecutorSharedMemoryMapperService_Reserve";
+const char *ExecutorSharedMemoryMapperServiceInitializeWrapperName =
+ "__llvm_orc_ExecutorSharedMemoryMapperService_Initialize";
+const char *ExecutorSharedMemoryMapperServiceDeinitializeWrapperName =
+ "__llvm_orc_ExecutorSharedMemoryMapperService_Deinitialize";
+const char *ExecutorSharedMemoryMapperServiceReleaseWrapperName =
+ "__llvm_orc_ExecutorSharedMemoryMapperService_Release";
+
const char *MemoryWriteUInt8sWrapperName =
"__llvm_orc_bootstrap_mem_write_uint8s_wrapper";
const char *MemoryWriteUInt16sWrapperName =
@@ -36,10 +49,12 @@ const char *MemoryWriteUInt64sWrapperName =
"__llvm_orc_bootstrap_mem_write_uint64s_wrapper";
const char *MemoryWriteBuffersWrapperName =
"__llvm_orc_bootstrap_mem_write_buffers_wrapper";
+
const char *RegisterEHFrameSectionWrapperName =
"__llvm_orc_bootstrap_register_ehframe_section_wrapper";
const char *DeregisterEHFrameSectionWrapperName =
"__llvm_orc_bootstrap_deregister_ehframe_section_wrapper";
+
const char *RunAsMainWrapperName = "__llvm_orc_bootstrap_run_as_main_wrapper";
} // end namespace rt
diff --git a/llvm/lib/ExecutionEngine/Orc/TargetProcess/CMakeLists.txt b/llvm/lib/ExecutionEngine/Orc/TargetProcess/CMakeLists.txt
index c4edece96cb04..4adee96ee2ef3 100644
--- a/llvm/lib/ExecutionEngine/Orc/TargetProcess/CMakeLists.txt
+++ b/llvm/lib/ExecutionEngine/Orc/TargetProcess/CMakeLists.txt
@@ -1,4 +1,9 @@
+if( CMAKE_HOST_UNIX AND HAVE_LIBRT )
+ set(rt_lib rt)
+endif()
+
add_llvm_component_library(LLVMOrcTargetProcess
+ ExecutorSharedMemoryMapperService.cpp
JITLoaderGDB.cpp
OrcRTBootstrap.cpp
RegisterEHFrames.cpp
@@ -12,6 +17,7 @@ add_llvm_component_library(LLVMOrcTargetProcess
LINK_LIBS
${LLVM_PTHREAD_LIB}
+ ${rt_lib}
LINK_COMPONENTS
OrcShared
diff --git a/llvm/lib/ExecutionEngine/Orc/TargetProcess/ExecutorSharedMemoryMapperService.cpp b/llvm/lib/ExecutionEngine/Orc/TargetProcess/ExecutorSharedMemoryMapperService.cpp
new file mode 100644
index 0000000000000..6c9f099061aec
--- /dev/null
+++ b/llvm/lib/ExecutionEngine/Orc/TargetProcess/ExecutorSharedMemoryMapperService.cpp
@@ -0,0 +1,341 @@
+//===---------- ExecutorSharedMemoryMapperService.cpp -----------*- 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
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/ExecutionEngine/Orc/TargetProcess/ExecutorSharedMemoryMapperService.h"
+
+#include "llvm/ExecutionEngine/Orc/Shared/OrcRTBridge.h"
+#include "llvm/Support/Process.h"
+#include "llvm/Support/WindowsError.h"
+
+#include <sstream>
+
+#if defined(LLVM_ON_UNIX)
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/mman.h>
+#include <unistd.h>
+#endif
+
+#if defined(_WIN32)
+static DWORD getWindowsProtectionFlags(unsigned Flags) {
+ switch (Flags & llvm::sys::Memory::MF_RWE_MASK) {
+ case llvm::sys::Memory::MF_READ:
+ return PAGE_READONLY;
+ case llvm::sys::Memory::MF_WRITE:
+ // Note: PAGE_WRITE is not supported by VirtualProtect
+ return PAGE_READWRITE;
+ case llvm::sys::Memory::MF_READ | llvm::sys::Memory::MF_WRITE:
+ return PAGE_READWRITE;
+ case llvm::sys::Memory::MF_READ | llvm::sys::Memory::MF_EXEC:
+ return PAGE_EXECUTE_READ;
+ case llvm::sys::Memory::MF_READ | llvm::sys::Memory::MF_WRITE |
+ llvm::sys::Memory::MF_EXEC:
+ return PAGE_EXECUTE_READWRITE;
+ case llvm::sys::Memory::MF_EXEC:
+ return PAGE_EXECUTE;
+ default:
+ llvm_unreachable("Illegal memory protection flag specified!");
+ }
+ // Provide a default return value as required by some compilers.
+ return PAGE_NOACCESS;
+}
+#endif
+
+namespace llvm {
+namespace orc {
+namespace rt_bootstrap {
+
+Expected<std::pair<ExecutorAddr, std::string>>
+ExecutorSharedMemoryMapperService::reserve(uint64_t Size) {
+#if defined(LLVM_ON_UNIX) || defined(_WIN32)
+
+#if defined(LLVM_ON_UNIX)
+
+ std::string SharedMemoryName;
+ {
+ std::stringstream SharedMemoryNameStream;
+ SharedMemoryNameStream << "/jitlink_" << sys::Process::getProcessId() << '_'
+ << (++SharedMemoryCount);
+ SharedMemoryName = SharedMemoryNameStream.str();
+ }
+
+ int SharedMemoryFile =
+ shm_open(SharedMemoryName.c_str(), O_RDWR | O_CREAT | O_EXCL, 0700);
+ if (SharedMemoryFile < 0)
+ return errorCodeToError(std::error_code(errno, std::generic_category()));
+
+ // by default size is 0
+ if (ftruncate(SharedMemoryFile, Size) < 0)
+ return errorCodeToError(std::error_code(errno, std::generic_category()));
+
+ void *Addr = mmap(nullptr, Size, PROT_NONE, MAP_SHARED, SharedMemoryFile, 0);
+ if (Addr == MAP_FAILED)
+ return errorCodeToError(std::error_code(errno, std::generic_category()));
+
+ close(SharedMemoryFile);
+
+#elif defined(_WIN32)
+
+ std::string SharedMemoryName;
+ {
+ std::stringstream SharedMemoryNameStream;
+ SharedMemoryNameStream << "jitlink_" << sys::Process::getProcessId() << '_'
+ << (++SharedMemoryCount);
+ SharedMemoryName = SharedMemoryNameStream.str();
+ }
+
+ std::wstring WideSharedMemoryName(SharedMemoryName.begin(),
+ SharedMemoryName.end());
+ HANDLE SharedMemoryFile = CreateFileMappingW(
+ INVALID_HANDLE_VALUE, NULL, PAGE_EXECUTE_READWRITE, Size >> 32,
+ Size & 0xffffffff, WideSharedMemoryName.c_str());
+ if (!SharedMemoryFile)
+ return errorCodeToError(mapWindowsError(GetLastError()));
+
+ void *Addr = MapViewOfFile(SharedMemoryFile,
+ FILE_MAP_ALL_ACCESS | FILE_MAP_EXECUTE, 0, 0, 0);
+ if (!Addr) {
+ CloseHandle(SharedMemoryFile);
+ return errorCodeToError(mapWindowsError(GetLastError()));
+ }
+
+#endif
+
+ {
+ std::lock_guard<std::mutex> Lock(Mutex);
+ Reservations[Addr].Size = Size;
+#if defined(_WIN32)
+ Reservations[Addr].SharedMemoryFile = SharedMemoryFile;
+#endif
+ }
+
+ return std::make_pair(ExecutorAddr::fromPtr(Addr),
+ std::move(SharedMemoryName));
+#else
+ return make_error<StringError>(
+ "SharedMemoryMapper is not supported on this platform yet",
+ inconvertibleErrorCode());
+#endif
+}
+
+Expected<ExecutorAddr> ExecutorSharedMemoryMapperService::initialize(
+ ExecutorAddr Reservation, tpctypes::SharedMemoryFinalizeRequest &FR) {
+#if defined(LLVM_ON_UNIX) || defined(_WIN32)
+
+ ExecutorAddr MinAddr(~0ULL);
+
+ // Contents are already in place
+ for (auto &Segment : FR.Segments) {
+ if (Segment.Addr < MinAddr)
+ MinAddr = Segment.Addr;
+
+#if defined(LLVM_ON_UNIX)
+
+ int NativeProt = 0;
+ if (Segment.Prot & tpctypes::WPF_Read)
+ NativeProt |= PROT_READ;
+ if (Segment.Prot & tpctypes::WPF_Write)
+ NativeProt |= PROT_WRITE;
+ if (Segment.Prot & tpctypes::WPF_Exec)
+ NativeProt |= PROT_EXEC;
+
+ if (mprotect(Segment.Addr.toPtr<void *>(), Segment.Size, NativeProt))
+ return errorCodeToError(std::error_code(errno, std::generic_category()));
+
+#elif defined(_WIN32)
+
+ DWORD NativeProt =
+ getWindowsProtectionFlags(fromWireProtectionFlags(Segment.Prot));
+
+ if (!VirtualProtect(Segment.Addr.toPtr<void *>(), Segment.Size, NativeProt,
+ &NativeProt))
+ return errorCodeToError(mapWindowsError(GetLastError()));
+
+#endif
+
+ if (Segment.Prot & tpctypes::WPF_Exec)
+ sys::Memory::InvalidateInstructionCache(Segment.Addr.toPtr<void *>(),
+ Segment.Size);
+ }
+
+ // Run finalization actions and get deinitlization action list.
+ auto DeinitializeActions = shared::runFinalizeActions(FR.Actions);
+ if (!DeinitializeActions) {
+ return DeinitializeActions.takeError();
+ }
+
+ {
+ std::lock_guard<std::mutex> Lock(Mutex);
+ Allocations[MinAddr].DeinitializationActions =
+ std::move(*DeinitializeActions);
+ Reservations[Reservation.toPtr<void *>()].Allocations.push_back(MinAddr);
+ }
+
+ return MinAddr;
+
+#else
+ return make_error<StringError>(
+ "SharedMemoryMapper is not supported on this platform yet",
+ inconvertibleErrorCode());
+#endif
+}
+
+Error ExecutorSharedMemoryMapperService::deinitialize(
+ const std::vector<ExecutorAddr> &Bases) {
+ Error AllErr = Error::success();
+
+ {
+ std::lock_guard<std::mutex> Lock(Mutex);
+
+ for (auto Base : Bases) {
+ if (Error Err = shared::runDeallocActions(
+ Allocations[Base].DeinitializationActions)) {
+ AllErr = joinErrors(std::move(AllErr), std::move(Err));
+ }
+
+ Allocations.erase(Base);
+ }
+ }
+
+ return AllErr;
+}
+
+Error ExecutorSharedMemoryMapperService::release(
+ const std::vector<ExecutorAddr> &Bases) {
+#if defined(LLVM_ON_UNIX) || defined(_WIN32)
+ Error Err = Error::success();
+
+ for (auto Base : Bases) {
+ std::vector<ExecutorAddr> AllocAddrs;
+ size_t Size;
+
+#if defined(_WIN32)
+ HANDLE SharedMemoryFile;
+#endif
+
+ {
+ std::lock_guard<std::mutex> Lock(Mutex);
+ auto &R = Reservations[Base.toPtr<void *>()];
+ Size = R.Size;
+
+#if defined(_WIN32)
+ SharedMemoryFile = R.SharedMemoryFile;
+#endif
+
+ AllocAddrs.swap(R.Allocations);
+ }
+
+ // deinitialize sub allocations
+ if (Error E = deinitialize(AllocAddrs))
+ Err = joinErrors(std::move(Err), std::move(E));
+
+#if defined(LLVM_ON_UNIX)
+
+ if (munmap(Base.toPtr<void *>(), Size) != 0)
+ Err = joinErrors(std::move(Err), errorCodeToError(std::error_code(
+ errno, std::generic_category())));
+
+#elif defined(_WIN32)
+
+ if (!UnmapViewOfFile(Base.toPtr<void *>()))
+ Err = joinErrors(std::move(Err),
+ errorCodeToError(mapWindowsError(GetLastError())));
+
+ CloseHandle(SharedMemoryFile);
+
+#endif
+
+ std::lock_guard<std::mutex> Lock(Mutex);
+ Reservations.erase(Base.toPtr<void *>());
+ }
+
+ return Err;
+#else
+ return make_error<StringError>(
+ "SharedMemoryMapper is not supported on this platform yet",
+ inconvertibleErrorCode());
+#endif
+}
+
+Error ExecutorSharedMemoryMapperService::shutdown() {
+ std::vector<ExecutorAddr> ReservationAddrs;
+ if (!Reservations.empty()) {
+ std::lock_guard<std::mutex> Lock(Mutex);
+ {
+ ReservationAddrs.reserve(Reservations.size());
+ for (const auto &R : Reservations) {
+ ReservationAddrs.push_back(ExecutorAddr::fromPtr(R.getFirst()));
+ }
+ }
+ }
+ return release(ReservationAddrs);
+
+ return Error::success();
+}
+
+void ExecutorSharedMemoryMapperService::addBootstrapSymbols(
+ StringMap<ExecutorAddr> &M) {
+ M[rt::ExecutorSharedMemoryMapperServiceInstanceName] =
+ ExecutorAddr::fromPtr(this);
+ M[rt::ExecutorSharedMemoryMapperServiceReserveWrapperName] =
+ ExecutorAddr::fromPtr(&reserveWrapper);
+ M[rt::ExecutorSharedMemoryMapperServiceInitializeWrapperName] =
+ ExecutorAddr::fromPtr(&initializeWrapper);
+ M[rt::ExecutorSharedMemoryMapperServiceDeinitializeWrapperName] =
+ ExecutorAddr::fromPtr(&deinitializeWrapper);
+ M[rt::ExecutorSharedMemoryMapperServiceReleaseWrapperName] =
+ ExecutorAddr::fromPtr(&releaseWrapper);
+}
+
+llvm::orc::shared::CWrapperFunctionResult
+ExecutorSharedMemoryMapperService::reserveWrapper(const char *ArgData,
+ size_t ArgSize) {
+ return shared::WrapperFunction<
+ rt::SPSExecutorSharedMemoryMapperServiceReserveSignature>::
+ handle(ArgData, ArgSize,
+ shared::makeMethodWrapperHandler(
+ &ExecutorSharedMemoryMapperService::reserve))
+ .release();
+}
+
+llvm::orc::shared::CWrapperFunctionResult
+ExecutorSharedMemoryMapperService::initializeWrapper(const char *ArgData,
+ size_t ArgSize) {
+ return shared::WrapperFunction<
+ rt::SPSExecutorSharedMemoryMapperServiceInitializeSignature>::
+ handle(ArgData, ArgSize,
+ shared::makeMethodWrapperHandler(
+ &ExecutorSharedMemoryMapperService::initialize))
+ .release();
+}
+
+llvm::orc::shared::CWrapperFunctionResult
+ExecutorSharedMemoryMapperService::deinitializeWrapper(const char *ArgData,
+ size_t ArgSize) {
+ return shared::WrapperFunction<
+ rt::SPSExecutorSharedMemoryMapperServiceDeinitializeSignature>::
+ handle(ArgData, ArgSize,
+ shared::makeMethodWrapperHandler(
+ &ExecutorSharedMemoryMapperService::deinitialize))
+ .release();
+}
+
+llvm::orc::shared::CWrapperFunctionResult
+ExecutorSharedMemoryMapperService::releaseWrapper(const char *ArgData,
+ size_t ArgSize) {
+ return shared::WrapperFunction<
+ rt::SPSExecutorSharedMemoryMapperServiceReleaseSignature>::
+ handle(ArgData, ArgSize,
+ shared::makeMethodWrapperHandler(
+ &ExecutorSharedMemoryMapperService::release))
+ .release();
+}
+
+} // namespace rt_bootstrap
+} // end namespace orc
+} // end namespace llvm
diff --git a/llvm/unittests/ExecutionEngine/Orc/CMakeLists.txt b/llvm/unittests/ExecutionEngine/Orc/CMakeLists.txt
index dd4028e5302ca..376ce82474854 100644
--- a/llvm/unittests/ExecutionEngine/Orc/CMakeLists.txt
+++ b/llvm/unittests/ExecutionEngine/Orc/CMakeLists.txt
@@ -30,6 +30,7 @@ add_llvm_unittest(OrcJITTests
OrcTestCommon.cpp
ResourceTrackerTest.cpp
RTDyldObjectLinkingLayerTest.cpp
+ SharedMemoryMapperTest.cpp
SimpleExecutorMemoryManagerTest.cpp
SimplePackedSerializationTest.cpp
SymbolStringPoolTest.cpp
diff --git a/llvm/unittests/ExecutionEngine/Orc/SharedMemoryMapperTest.cpp b/llvm/unittests/ExecutionEngine/Orc/SharedMemoryMapperTest.cpp
new file mode 100644
index 0000000000000..3ce9ecc8ba9e3
--- /dev/null
+++ b/llvm/unittests/ExecutionEngine/Orc/SharedMemoryMapperTest.cpp
@@ -0,0 +1,133 @@
+//===- SharedMemoryMapperTest.cpp -- Tests for SharedMemoryMapper ---------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#include "OrcTestCommon.h"
+
+#include "llvm/ExecutionEngine/Orc/MemoryMapper.h"
+#include "llvm/ExecutionEngine/Orc/Shared/OrcRTBridge.h"
+#include "llvm/ExecutionEngine/Orc/TargetProcess/ExecutorSharedMemoryMapperService.h"
+#include "llvm/Testing/Support/Error.h"
+
+using namespace llvm;
+using namespace llvm::orc;
+using namespace llvm::orc::shared;
+using namespace llvm::orc::rt_bootstrap;
+
+#if defined(LLVM_ON_UNIX) || defined(_WIN32)
+
+// A basic function to be used as both initializer/deinitializer
+orc::shared::CWrapperFunctionResult incrementWrapper(const char *ArgData,
+ size_t ArgSize) {
+ return WrapperFunction<SPSError(SPSExecutorAddr)>::handle(
+ ArgData, ArgSize,
+ [](ExecutorAddr A) -> Error {
+ *A.toPtr<int *>() += 1;
+ return Error::success();
+ })
+ .release();
+}
+
+TEST(SharedMemoryMapperTest, MemReserveInitializeDeinitializeRelease) {
+ // These counters are used to track how many times the initializer and
+ // deinitializer functions are called
+ int InitializeCounter = 0;
+ int DeinitializeCounter = 0;
+
+ auto SelfEPC = cantFail(SelfExecutorProcessControl::Create());
+
+ ExecutorSharedMemoryMapperService MapperService;
+
+ SharedMemoryMapper::SymbolAddrs SAs;
+ {
+ StringMap<ExecutorAddr> Map;
+ MapperService.addBootstrapSymbols(Map);
+ SAs.Instance = Map[rt::ExecutorSharedMemoryMapperServiceInstanceName];
+ SAs.Reserve = Map[rt::ExecutorSharedMemoryMapperServiceReserveWrapperName];
+ SAs.Initialize =
+ Map[rt::ExecutorSharedMemoryMapperServiceInitializeWrapperName];
+ SAs.Deinitialize =
+ Map[rt::ExecutorSharedMemoryMapperServiceDeinitializeWrapperName];
+ SAs.Release = Map[rt::ExecutorSharedMemoryMapperServiceReleaseWrapperName];
+ }
+
+ std::string TestString = "Hello, World!";
+
+ // barrier
+ std::promise<void> P;
+ auto F = P.get_future();
+
+ {
+ auto PageSize = cantFail(sys::Process::getPageSize());
+ size_t ReqSize = PageSize;
+
+ std::unique_ptr<MemoryMapper> Mapper =
+ std::make_unique<SharedMemoryMapper>(*SelfEPC, SAs);
+
+ Mapper->reserve(ReqSize, [&](Expected<ExecutorAddrRange> Result) {
+ EXPECT_THAT_ERROR(Result.takeError(), Succeeded());
+ auto Reservation = std::move(*Result);
+ {
+ char *Addr = Mapper->prepare(Reservation.Start, TestString.size() + 1);
+ std::strcpy(Addr, TestString.c_str());
+ }
+ MemoryMapper::AllocInfo AI;
+ {
+ MemoryMapper::AllocInfo::SegInfo SI;
+ SI.Offset = 0;
+ SI.ContentSize = TestString.size() + 1;
+ SI.ZeroFillSize = PageSize - SI.ContentSize;
+ SI.Prot = sys::Memory::MF_READ | sys::Memory::MF_WRITE;
+
+ AI.MappingBase = Reservation.Start;
+ AI.Segments.push_back(SI);
+ AI.Actions.push_back(
+ {cantFail(WrapperFunctionCall::Create<SPSArgList<SPSExecutorAddr>>(
+ ExecutorAddr::fromPtr(incrementWrapper),
+ ExecutorAddr::fromPtr(&InitializeCounter))),
+ cantFail(WrapperFunctionCall::Create<SPSArgList<SPSExecutorAddr>>(
+ ExecutorAddr::fromPtr(incrementWrapper),
+ ExecutorAddr::fromPtr(&DeinitializeCounter)))});
+ }
+
+ EXPECT_EQ(InitializeCounter, 0);
+ EXPECT_EQ(DeinitializeCounter, 0);
+
+ Mapper->initialize(AI, [&, Reservation](Expected<ExecutorAddr> Result) {
+ EXPECT_THAT_ERROR(Result.takeError(), Succeeded());
+
+ EXPECT_EQ(TestString, std::string(static_cast<char *>(
+ Reservation.Start.toPtr<char *>())));
+
+ EXPECT_EQ(InitializeCounter, 1);
+ EXPECT_EQ(DeinitializeCounter, 0);
+
+ Mapper->deinitialize({*Result}, [&, Reservation](Error Err) {
+ EXPECT_THAT_ERROR(std::move(Err), Succeeded());
+
+ EXPECT_EQ(InitializeCounter, 1);
+ EXPECT_EQ(DeinitializeCounter, 1);
+
+ Mapper->release({Reservation.Start}, [&](Error Err) {
+ EXPECT_THAT_ERROR(std::move(Err), Succeeded());
+
+ P.set_value();
+ });
+ });
+ });
+ });
+
+ // This will block the test if any of the above callbacks are not executed
+ F.wait();
+ // Mapper must be destructed before calling shutdown to avoid double free
+ }
+
+ EXPECT_THAT_ERROR(MapperService.shutdown(), Succeeded());
+ cantFail(SelfEPC->disconnect());
+}
+
+#endif
More information about the llvm-commits
mailing list