diff options
Diffstat (limited to 'unittests/ExecutionEngine/Orc')
-rw-r--r-- | unittests/ExecutionEngine/Orc/CMakeLists.txt | 2 | ||||
-rw-r--r-- | unittests/ExecutionEngine/Orc/CompileOnDemandLayerTest.cpp | 34 | ||||
-rw-r--r-- | unittests/ExecutionEngine/Orc/IndirectionUtilsTest.cpp | 3 | ||||
-rw-r--r-- | unittests/ExecutionEngine/Orc/Makefile | 16 | ||||
-rw-r--r-- | unittests/ExecutionEngine/Orc/ObjectLinkingLayerTest.cpp | 99 | ||||
-rw-r--r-- | unittests/ExecutionEngine/Orc/ObjectTransformLayerTest.cpp | 77 | ||||
-rw-r--r-- | unittests/ExecutionEngine/Orc/OrcCAPITest.cpp | 4 | ||||
-rw-r--r-- | unittests/ExecutionEngine/Orc/OrcTestCommon.h | 5 | ||||
-rw-r--r-- | unittests/ExecutionEngine/Orc/RPCUtilsTest.cpp | 247 |
9 files changed, 335 insertions, 152 deletions
diff --git a/unittests/ExecutionEngine/Orc/CMakeLists.txt b/unittests/ExecutionEngine/Orc/CMakeLists.txt index 41fef24556b11..68f6d0c28d7ca 100644 --- a/unittests/ExecutionEngine/Orc/CMakeLists.txt +++ b/unittests/ExecutionEngine/Orc/CMakeLists.txt @@ -20,3 +20,5 @@ add_llvm_unittest(OrcJITTests OrcTestCommon.cpp RPCUtilsTest.cpp ) + +target_link_libraries(OrcJITTests ${PTHREAD_LIB}) diff --git a/unittests/ExecutionEngine/Orc/CompileOnDemandLayerTest.cpp b/unittests/ExecutionEngine/Orc/CompileOnDemandLayerTest.cpp index a27e649b616f8..8140a1ff24939 100644 --- a/unittests/ExecutionEngine/Orc/CompileOnDemandLayerTest.cpp +++ b/unittests/ExecutionEngine/Orc/CompileOnDemandLayerTest.cpp @@ -18,19 +18,20 @@ namespace { class DummyCallbackManager : public orc::JITCompileCallbackManager { public: - DummyCallbackManager() : JITCompileCallbackManager(0) { } + DummyCallbackManager() : JITCompileCallbackManager(0) {} + public: void grow() override { llvm_unreachable("not implemented"); } }; class DummyStubsManager : public orc::IndirectStubsManager { public: - std::error_code createStub(StringRef StubName, TargetAddress InitAddr, - JITSymbolFlags Flags) override { + Error createStub(StringRef StubName, TargetAddress InitAddr, + JITSymbolFlags Flags) override { llvm_unreachable("Not implemented"); } - std::error_code createStubs(const StubInitsMap &StubInits) override { + Error createStubs(const StubInitsMap &StubInits) override { llvm_unreachable("Not implemented"); } @@ -42,22 +43,20 @@ public: llvm_unreachable("Not implemented"); } - std::error_code updatePointer(StringRef Name, - TargetAddress NewAddr) override { + Error updatePointer(StringRef Name, TargetAddress NewAddr) override { llvm_unreachable("Not implemented"); } }; TEST(CompileOnDemandLayerTest, FindSymbol) { - auto MockBaseLayer = - createMockBaseLayer<int>(DoNothingAndReturn<int>(0), - DoNothingAndReturn<void>(), - [](const std::string &Name, bool) { - if (Name == "foo") - return JITSymbol(1, JITSymbolFlags::Exported); - return JITSymbol(nullptr); - }, - DoNothingAndReturn<JITSymbol>(nullptr)); + auto MockBaseLayer = createMockBaseLayer<int>( + DoNothingAndReturn<int>(0), DoNothingAndReturn<void>(), + [](const std::string &Name, bool) { + if (Name == "foo") + return JITSymbol(1, JITSymbolFlags::Exported); + return JITSymbol(nullptr); + }, + DoNothingAndReturn<JITSymbol>(nullptr)); typedef decltype(MockBaseLayer) MockBaseLayerT; DummyCallbackManager CallbackMgr; @@ -68,8 +67,7 @@ TEST(CompileOnDemandLayerTest, FindSymbol) { auto Sym = COD.findSymbol("foo", true); - EXPECT_TRUE(!!Sym) - << "CompileOnDemand::findSymbol should call findSymbol in the base layer."; + EXPECT_TRUE(!!Sym) << "CompileOnDemand::findSymbol should call findSymbol in " + "the base layer."; } - } diff --git a/unittests/ExecutionEngine/Orc/IndirectionUtilsTest.cpp b/unittests/ExecutionEngine/Orc/IndirectionUtilsTest.cpp index 38b60ea7fcd47..ac847039d9fb2 100644 --- a/unittests/ExecutionEngine/Orc/IndirectionUtilsTest.cpp +++ b/unittests/ExecutionEngine/Orc/IndirectionUtilsTest.cpp @@ -17,7 +17,8 @@ using namespace llvm; namespace { TEST(IndirectionUtilsTest, MakeStub) { - ModuleBuilder MB(getGlobalContext(), "x86_64-apple-macosx10.10", ""); + LLVMContext Context; + ModuleBuilder MB(Context, "x86_64-apple-macosx10.10", ""); Function *F = MB.createFunctionDecl<void(DummyStruct, DummyStruct)>(""); SmallVector<AttributeSet, 4> Attrs; Attrs.push_back( diff --git a/unittests/ExecutionEngine/Orc/Makefile b/unittests/ExecutionEngine/Orc/Makefile deleted file mode 100644 index c899728e50789..0000000000000 --- a/unittests/ExecutionEngine/Orc/Makefile +++ /dev/null @@ -1,16 +0,0 @@ -##===- unittests/ExecutionEngine/Orc/Makefile --------------*- Makefile -*-===## -# -# The LLVM Compiler Infrastructure -# -# This file is distributed under the University of Illinois Open Source -# License. See LICENSE.TXT for details. -# -##===----------------------------------------------------------------------===## - -LEVEL = ../../.. -TESTNAME = OrcJIT -LINK_COMPONENTS := core ipo mcjit orcjit native support - -include $(LEVEL)/Makefile.config -include $(LLVM_SRC_ROOT)/unittests/Makefile.unittest - diff --git a/unittests/ExecutionEngine/Orc/ObjectLinkingLayerTest.cpp b/unittests/ExecutionEngine/Orc/ObjectLinkingLayerTest.cpp index 59ee01f36010c..87928347d88ed 100644 --- a/unittests/ExecutionEngine/Orc/ObjectLinkingLayerTest.cpp +++ b/unittests/ExecutionEngine/Orc/ObjectLinkingLayerTest.cpp @@ -12,6 +12,7 @@ #include "llvm/ExecutionEngine/SectionMemoryManager.h" #include "llvm/ExecutionEngine/Orc/CompileUtils.h" #include "llvm/ExecutionEngine/Orc/LambdaResolver.h" +#include "llvm/ExecutionEngine/Orc/NullResolver.h" #include "llvm/ExecutionEngine/Orc/ObjectLinkingLayer.h" #include "llvm/IR/Constants.h" #include "llvm/IR/LLVMContext.h" @@ -24,19 +25,26 @@ namespace { class ObjectLinkingLayerExecutionTest : public testing::Test, public OrcExecutionTest { + }; class SectionMemoryManagerWrapper : public SectionMemoryManager { public: int FinalizationCount = 0; - bool finalizeMemory(std::string *ErrMsg = 0) override { + int NeedsToReserveAllocationSpaceCount = 0; + + bool needsToReserveAllocationSpace() override { + ++NeedsToReserveAllocationSpaceCount; + return SectionMemoryManager::needsToReserveAllocationSpace(); + } + + bool finalizeMemory(std::string *ErrMsg = nullptr) override { ++FinalizationCount; return SectionMemoryManager::finalizeMemory(ErrMsg); } }; TEST(ObjectLinkingLayerTest, TestSetProcessAllSections) { - class SectionMemoryManagerWrapper : public SectionMemoryManager { public: SectionMemoryManagerWrapper(bool &DebugSeen) : DebugSeen(DebugSeen) {} @@ -57,9 +65,10 @@ TEST(ObjectLinkingLayerTest, TestSetProcessAllSections) { ObjectLinkingLayer<> ObjLayer; - auto M = llvm::make_unique<Module>("", getGlobalContext()); + LLVMContext Context; + auto M = llvm::make_unique<Module>("", Context); M->setTargetTriple("x86_64-unknown-linux-gnu"); - Type *Int32Ty = IntegerType::get(getGlobalContext(), 32); + Type *Int32Ty = IntegerType::get(Context, 32); GlobalVariable *GV = new GlobalVariable(*M, Int32Ty, false, GlobalValue::ExternalLinkage, ConstantInt::get(Int32Ty, 42), "foo"); @@ -105,9 +114,7 @@ TEST(ObjectLinkingLayerTest, TestSetProcessAllSections) { } } - TEST_F(ObjectLinkingLayerExecutionTest, NoDuplicateFinalization) { - if (!TM) return; @@ -120,15 +127,19 @@ TEST_F(ObjectLinkingLayerExecutionTest, NoDuplicateFinalization) { // Module 2: // int bar(); // int foo() { return bar(); } + // + // Verify that the memory manager is only finalized once (for Module 2). + // Failure suggests that finalize is being called on the inner RTDyld + // instance (for Module 1) which is unsafe, as it will prevent relocation of + // Module 2. - ModuleBuilder MB1(getGlobalContext(), "", "dummy"); + ModuleBuilder MB1(Context, "", "dummy"); { MB1.getModule()->setDataLayout(TM->createDataLayout()); Function *BarImpl = MB1.createFunctionDecl<int32_t(void)>("bar"); - BasicBlock *BarEntry = BasicBlock::Create(getGlobalContext(), "entry", - BarImpl); + BasicBlock *BarEntry = BasicBlock::Create(Context, "entry", BarImpl); IRBuilder<> Builder(BarEntry); - IntegerType *Int32Ty = IntegerType::get(getGlobalContext(), 32); + IntegerType *Int32Ty = IntegerType::get(Context, 32); Value *FourtyTwo = ConstantInt::getSigned(Int32Ty, 42); Builder.CreateRet(FourtyTwo); } @@ -137,13 +148,12 @@ TEST_F(ObjectLinkingLayerExecutionTest, NoDuplicateFinalization) { std::vector<object::ObjectFile*> Obj1Set; Obj1Set.push_back(Obj1.getBinary()); - ModuleBuilder MB2(getGlobalContext(), "", "dummy"); + ModuleBuilder MB2(Context, "", "dummy"); { MB2.getModule()->setDataLayout(TM->createDataLayout()); Function *BarDecl = MB2.createFunctionDecl<int32_t(void)>("bar"); Function *FooImpl = MB2.createFunctionDecl<int32_t(void)>("foo"); - BasicBlock *FooEntry = BasicBlock::Create(getGlobalContext(), "entry", - FooImpl); + BasicBlock *FooEntry = BasicBlock::Create(Context, "entry", FooImpl); IRBuilder<> Builder(FooEntry); Builder.CreateRet(Builder.CreateCall(BarDecl)); } @@ -155,7 +165,7 @@ TEST_F(ObjectLinkingLayerExecutionTest, NoDuplicateFinalization) { createLambdaResolver( [&](const std::string &Name) { if (auto Sym = ObjLayer.findSymbol(Name, true)) - return RuntimeDyld::SymbolInfo(Sym.getAddress(), Sym.getFlags()); + return Sym.toRuntimeDyldSymbol(); return RuntimeDyld::SymbolInfo(nullptr); }, [](const std::string &Name) { @@ -173,4 +183,65 @@ TEST_F(ObjectLinkingLayerExecutionTest, NoDuplicateFinalization) { << "Extra call to finalize"; } +TEST_F(ObjectLinkingLayerExecutionTest, NoPrematureAllocation) { + if (!TM) + return; + + ObjectLinkingLayer<> ObjLayer; + SimpleCompiler Compile(*TM); + + // Create a pair of unrelated modules: + // + // Module 1: + // int foo() { return 42; } + // Module 2: + // int bar() { return 7; } + // + // Both modules will share a memory manager. We want to verify that the + // second object is not loaded before the first one is finalized. To do this + // in a portable way, we abuse the + // RuntimeDyld::MemoryManager::needsToReserveAllocationSpace hook, which is + // called once per object before any sections are allocated. + + ModuleBuilder MB1(Context, "", "dummy"); + { + MB1.getModule()->setDataLayout(TM->createDataLayout()); + Function *BarImpl = MB1.createFunctionDecl<int32_t(void)>("foo"); + BasicBlock *BarEntry = BasicBlock::Create(Context, "entry", BarImpl); + IRBuilder<> Builder(BarEntry); + IntegerType *Int32Ty = IntegerType::get(Context, 32); + Value *FourtyTwo = ConstantInt::getSigned(Int32Ty, 42); + Builder.CreateRet(FourtyTwo); + } + + auto Obj1 = Compile(*MB1.getModule()); + std::vector<object::ObjectFile*> Obj1Set; + Obj1Set.push_back(Obj1.getBinary()); + + ModuleBuilder MB2(Context, "", "dummy"); + { + MB2.getModule()->setDataLayout(TM->createDataLayout()); + Function *BarImpl = MB2.createFunctionDecl<int32_t(void)>("bar"); + BasicBlock *BarEntry = BasicBlock::Create(Context, "entry", BarImpl); + IRBuilder<> Builder(BarEntry); + IntegerType *Int32Ty = IntegerType::get(Context, 32); + Value *Seven = ConstantInt::getSigned(Int32Ty, 7); + Builder.CreateRet(Seven); + } + auto Obj2 = Compile(*MB2.getModule()); + std::vector<object::ObjectFile*> Obj2Set; + Obj2Set.push_back(Obj2.getBinary()); + + SectionMemoryManagerWrapper SMMW; + NullResolver NR; + auto H = ObjLayer.addObjectSet(std::move(Obj1Set), &SMMW, &NR); + ObjLayer.addObjectSet(std::move(Obj2Set), &SMMW, &NR); + ObjLayer.emitAndFinalize(H); + + // Only one call to needsToReserveAllocationSpace should have been made. + EXPECT_EQ(SMMW.NeedsToReserveAllocationSpaceCount, 1) + << "More than one call to needsToReserveAllocationSpace " + "(multiple unrelated objects loaded prior to finalization)"; } + +} // end anonymous namespace diff --git a/unittests/ExecutionEngine/Orc/ObjectTransformLayerTest.cpp b/unittests/ExecutionEngine/Orc/ObjectTransformLayerTest.cpp index c88c94f17b1c9..e1b1f2f927815 100644 --- a/unittests/ExecutionEngine/Orc/ObjectTransformLayerTest.cpp +++ b/unittests/ExecutionEngine/Orc/ObjectTransformLayerTest.cpp @@ -7,9 +7,14 @@ // //===----------------------------------------------------------------------===// -#include "llvm/ExecutionEngine/Orc/ObjectTransformLayer.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallVector.h" +#include "llvm/ExecutionEngine/Orc/CompileUtils.h" +#include "llvm/ExecutionEngine/Orc/IRCompileLayer.h" +#include "llvm/ExecutionEngine/Orc/NullResolver.h" +#include "llvm/ExecutionEngine/Orc/ObjectLinkingLayer.h" +#include "llvm/ExecutionEngine/Orc/ObjectTransformLayer.h" +#include "llvm/Object/ObjectFile.h" #include "gtest/gtest.h" using namespace llvm::orc; @@ -51,13 +56,14 @@ public: template <typename ObjSetT, typename MemoryManagerPtrT, typename SymbolResolverPtrT> - ObjSetHandleT addObjectSet(ObjSetT &Objects, MemoryManagerPtrT MemMgr, + ObjSetHandleT addObjectSet(ObjSetT Objects, MemoryManagerPtrT MemMgr, SymbolResolverPtrT Resolver) { EXPECT_EQ(MockManager, *MemMgr) << "MM should pass through"; EXPECT_EQ(MockResolver, *Resolver) << "Resolver should pass through"; size_t I = 0; for (auto &ObjPtr : Objects) { - EXPECT_EQ(MockObjects[I++] + 1, *ObjPtr) << "Transform should be applied"; + EXPECT_EQ(MockObjects[I] + 1, *ObjPtr) << "Transform should be applied"; + I++; } EXPECT_EQ(MockObjects.size(), I) << "Number of objects should match"; LastCalled = "addObjectSet"; @@ -216,13 +222,14 @@ TEST(ObjectTransformLayerTest, Main) { auto MM = llvm::make_unique<MockMemoryManager>(MockManager); auto SR = llvm::make_unique<MockSymbolResolver>(MockResolver); M.expectAddObjectSet(Objs1, MM.get(), SR.get()); - auto H = T1.addObjectSet(Objs1, std::move(MM), std::move(SR)); + auto H = T1.addObjectSet(std::move(Objs1), std::move(MM), std::move(SR)); M.verifyAddObjectSet(H); // Test addObjectSet with T2 (mutating, naked pointers) - llvm::SmallVector<MockObjectFile *, 2> Objs2; - Objs2.push_back(&MockObject1); - Objs2.push_back(&MockObject2); + llvm::SmallVector<MockObjectFile *, 2> Objs2Vec; + Objs2Vec.push_back(&MockObject1); + Objs2Vec.push_back(&MockObject2); + llvm::MutableArrayRef<MockObjectFile *> Objs2(Objs2Vec); M.expectAddObjectSet(Objs2, &MockManager, &MockResolver); H = T2.addObjectSet(Objs2, &MockManager, &MockResolver); M.verifyAddObjectSet(H); @@ -271,5 +278,61 @@ TEST(ObjectTransformLayerTest, Main) { const auto &T1C = T1; OwnedObj = T1C.getTransform()(std::move(OwnedObj)); EXPECT_EQ(289, *OwnedObj) << "Expected incrementing transform"; + + volatile bool RunStaticChecks = false; + if (!RunStaticChecks) + return; + + // Make sure that ObjectTransformLayer implements the object layer concept + // correctly by sandwitching one between an ObjectLinkingLayer and an + // IRCompileLayer, verifying that it compiles if we have a call to the + // IRComileLayer's addModuleSet that should call the transform layer's + // addObjectSet, and also calling the other public transform layer methods + // directly to make sure the methods they intend to forward to exist on + // the ObjectLinkingLayer. + + // We'll need a concrete MemoryManager class. + class NullManager : public llvm::RuntimeDyld::MemoryManager { + public: + uint8_t *allocateCodeSection(uintptr_t, unsigned, unsigned, + llvm::StringRef) override { + return nullptr; + } + uint8_t *allocateDataSection(uintptr_t, unsigned, unsigned, llvm::StringRef, + bool) override { + return nullptr; + } + void registerEHFrames(uint8_t *, uint64_t, size_t) override {} + void deregisterEHFrames(uint8_t *, uint64_t, size_t) override {} + bool finalizeMemory(std::string *) override { return false; } + }; + + // Construct the jit layers. + ObjectLinkingLayer<> BaseLayer; + auto IdentityTransform = []( + std::unique_ptr<llvm::object::OwningBinary<llvm::object::ObjectFile>> + Obj) { return Obj; }; + ObjectTransformLayer<decltype(BaseLayer), decltype(IdentityTransform)> + TransformLayer(BaseLayer, IdentityTransform); + auto NullCompiler = [](llvm::Module &) { + return llvm::object::OwningBinary<llvm::object::ObjectFile>(); + }; + IRCompileLayer<decltype(TransformLayer)> CompileLayer(TransformLayer, + NullCompiler); + + // Make sure that the calls from IRCompileLayer to ObjectTransformLayer + // compile. + NullResolver Resolver; + NullManager Manager; + CompileLayer.addModuleSet(std::vector<llvm::Module *>(), &Manager, &Resolver); + + // Make sure that the calls from ObjectTransformLayer to ObjectLinkingLayer + // compile. + decltype(TransformLayer)::ObjSetHandleT ObjSet; + TransformLayer.emitAndFinalize(ObjSet); + TransformLayer.findSymbolIn(ObjSet, Name, false); + TransformLayer.findSymbol(Name, true); + TransformLayer.mapSectionAddress(ObjSet, nullptr, 0); + TransformLayer.removeObjectSet(ObjSet); } } diff --git a/unittests/ExecutionEngine/Orc/OrcCAPITest.cpp b/unittests/ExecutionEngine/Orc/OrcCAPITest.cpp index 776d26970a311..305325b6c6ef1 100644 --- a/unittests/ExecutionEngine/Orc/OrcCAPITest.cpp +++ b/unittests/ExecutionEngine/Orc/OrcCAPITest.cpp @@ -25,11 +25,11 @@ DEFINE_SIMPLE_CONVERSION_FUNCTIONS(TargetMachine, LLVMTargetMachineRef) class OrcCAPIExecutionTest : public testing::Test, public OrcExecutionTest { protected: std::unique_ptr<Module> createTestModule(const Triple &TT) { - ModuleBuilder MB(getGlobalContext(), TT.str(), ""); + ModuleBuilder MB(Context, TT.str(), ""); Function *TestFunc = MB.createFunctionDecl<int()>("testFunc"); Function *Main = MB.createFunctionDecl<int(int, char*[])>("main"); - Main->getBasicBlockList().push_back(BasicBlock::Create(getGlobalContext())); + Main->getBasicBlockList().push_back(BasicBlock::Create(Context)); IRBuilder<> B(&Main->back()); Value* Result = B.CreateCall(TestFunc); B.CreateRet(Result); diff --git a/unittests/ExecutionEngine/Orc/OrcTestCommon.h b/unittests/ExecutionEngine/Orc/OrcTestCommon.h index f480e0789ae5f..fe3da88dc9d1f 100644 --- a/unittests/ExecutionEngine/Orc/OrcTestCommon.h +++ b/unittests/ExecutionEngine/Orc/OrcTestCommon.h @@ -46,12 +46,15 @@ public: if (TM) { // If we found a TargetMachine, check that it's one that Orc supports. const Triple& TT = TM->getTargetTriple(); - if (TT.getArch() != Triple::x86_64 || !TT.isOSDarwin()) + + if ((TT.getArch() != Triple::x86_64 && TT.getArch() != Triple::x86) || + TT.isOSWindows()) TM = nullptr; } }; protected: + LLVMContext Context; std::unique_ptr<TargetMachine> TM; private: static bool NativeTargetInitialized; diff --git a/unittests/ExecutionEngine/Orc/RPCUtilsTest.cpp b/unittests/ExecutionEngine/Orc/RPCUtilsTest.cpp index 8215144a514d6..7d55641e4ce2b 100644 --- a/unittests/ExecutionEngine/Orc/RPCUtilsTest.cpp +++ b/unittests/ExecutionEngine/Orc/RPCUtilsTest.cpp @@ -17,131 +17,192 @@ using namespace llvm; using namespace llvm::orc; using namespace llvm::orc::remote; -class QueueChannel : public RPCChannel { +class Queue : public std::queue<char> { public: - QueueChannel(std::queue<char> &Queue) : Queue(Queue) {} + std::mutex &getLock() { return Lock; } + +private: + std::mutex Lock; +}; - std::error_code readBytes(char *Dst, unsigned Size) override { - while (Size--) { - *Dst++ = Queue.front(); - Queue.pop(); +class QueueChannel : public RPCChannel { +public: + QueueChannel(Queue &InQueue, Queue &OutQueue) + : InQueue(InQueue), OutQueue(OutQueue) {} + + Error readBytes(char *Dst, unsigned Size) override { + while (Size != 0) { + // If there's nothing to read then yield. + while (InQueue.empty()) + std::this_thread::yield(); + + // Lock the channel and read what we can. + std::lock_guard<std::mutex> Lock(InQueue.getLock()); + while (!InQueue.empty() && Size) { + *Dst++ = InQueue.front(); + --Size; + InQueue.pop(); + } } - return std::error_code(); + return Error::success(); } - std::error_code appendBytes(const char *Src, unsigned Size) override { + Error appendBytes(const char *Src, unsigned Size) override { + std::lock_guard<std::mutex> Lock(OutQueue.getLock()); while (Size--) - Queue.push(*Src++); - return std::error_code(); + OutQueue.push(*Src++); + return Error::success(); } - std::error_code send() override { return std::error_code(); } + Error send() override { return Error::success(); } private: - std::queue<char> &Queue; + Queue &InQueue; + Queue &OutQueue; }; -class DummyRPC : public testing::Test, - public RPC<QueueChannel> { +class DummyRPC : public testing::Test, public RPC<QueueChannel> { public: - typedef Procedure<1, bool> Proc1; - typedef Procedure<2, int8_t, - uint8_t, - int16_t, - uint16_t, - int32_t, - uint32_t, - int64_t, - uint64_t, - bool, - std::string, - std::vector<int>> AllTheTypes; + enum FuncId : uint32_t { + VoidBoolId = RPCFunctionIdTraits<FuncId>::FirstValidId, + IntIntId, + AllTheTypesId + }; + + typedef Function<VoidBoolId, void(bool)> VoidBool; + typedef Function<IntIntId, int32_t(int32_t)> IntInt; + typedef Function<AllTheTypesId, + void(int8_t, uint8_t, int16_t, uint16_t, int32_t, uint32_t, + int64_t, uint64_t, bool, std::string, std::vector<int>)> + AllTheTypes; }; +TEST_F(DummyRPC, TestAsyncVoidBool) { + Queue Q1, Q2; + QueueChannel C1(Q1, Q2); + QueueChannel C2(Q2, Q1); + + // Make an async call. + auto ResOrErr = callAsyncWithSeq<VoidBool>(C1, true); + EXPECT_TRUE(!!ResOrErr) << "Simple call over queue failed"; -TEST_F(DummyRPC, TestBasic) { - std::queue<char> Queue; - QueueChannel C(Queue); + { + // Expect a call to Proc1. + auto EC = expect<VoidBool>(C2, [&](bool &B) { + EXPECT_EQ(B, true) << "Bool serialization broken"; + return Error::success(); + }); + EXPECT_FALSE(EC) << "Simple expect over queue failed"; + } { - // Make a call to Proc1. - auto EC = call<Proc1>(C, true); - EXPECT_FALSE(EC) << "Simple call over queue failed"; + // Wait for the result. + auto EC = waitForResult(C1, ResOrErr->second, handleNone); + EXPECT_FALSE(EC) << "Could not read result."; } + // Verify that the function returned ok. + auto Val = ResOrErr->first.get(); + EXPECT_TRUE(Val) << "Remote void function failed to execute."; +} + +TEST_F(DummyRPC, TestAsyncIntInt) { + Queue Q1, Q2; + QueueChannel C1(Q1, Q2); + QueueChannel C2(Q2, Q1); + + // Make an async call. + auto ResOrErr = callAsyncWithSeq<IntInt>(C1, 21); + EXPECT_TRUE(!!ResOrErr) << "Simple call over queue failed"; + { // Expect a call to Proc1. - auto EC = expect<Proc1>(C, - [&](bool &B) { - EXPECT_EQ(B, true) - << "Bool serialization broken"; - return std::error_code(); - }); + auto EC = expect<IntInt>(C2, [&](int32_t I) -> Expected<int32_t> { + EXPECT_EQ(I, 21) << "Bool serialization broken"; + return 2 * I; + }); EXPECT_FALSE(EC) << "Simple expect over queue failed"; } + + { + // Wait for the result. + auto EC = waitForResult(C1, ResOrErr->second, handleNone); + EXPECT_FALSE(EC) << "Could not read result."; + } + + // Verify that the function returned ok. + auto Val = ResOrErr->first.get(); + EXPECT_TRUE(!!Val) << "Remote int function failed to execute."; + EXPECT_EQ(*Val, 42) << "Remote int function return wrong value."; } TEST_F(DummyRPC, TestSerialization) { - std::queue<char> Queue; - QueueChannel C(Queue); + Queue Q1, Q2; + QueueChannel C1(Q1, Q2); + QueueChannel C2(Q2, Q1); + + // Make a call to Proc1. + std::vector<int> v({42, 7}); + auto ResOrErr = callAsyncWithSeq<AllTheTypes>( + C1, -101, 250, -10000, 10000, -1000000000, 1000000000, -10000000000, + 10000000000, true, "foo", v); + EXPECT_TRUE(!!ResOrErr) << "Big (serialization test) call over queue failed"; { - // Make a call to Proc1. - std::vector<int> v({42, 7}); - auto EC = call<AllTheTypes>(C, - -101, - 250, - -10000, - 10000, - -1000000000, - 1000000000, - -10000000000, - 10000000000, - true, - "foo", - v); + // Expect a call to Proc1. + auto EC = expect<AllTheTypes>( + C2, [&](int8_t &s8, uint8_t &u8, int16_t &s16, uint16_t &u16, + int32_t &s32, uint32_t &u32, int64_t &s64, uint64_t &u64, + bool &b, std::string &s, std::vector<int> &v) { + + EXPECT_EQ(s8, -101) << "int8_t serialization broken"; + EXPECT_EQ(u8, 250) << "uint8_t serialization broken"; + EXPECT_EQ(s16, -10000) << "int16_t serialization broken"; + EXPECT_EQ(u16, 10000) << "uint16_t serialization broken"; + EXPECT_EQ(s32, -1000000000) << "int32_t serialization broken"; + EXPECT_EQ(u32, 1000000000ULL) << "uint32_t serialization broken"; + EXPECT_EQ(s64, -10000000000) << "int64_t serialization broken"; + EXPECT_EQ(u64, 10000000000ULL) << "uint64_t serialization broken"; + EXPECT_EQ(b, true) << "bool serialization broken"; + EXPECT_EQ(s, "foo") << "std::string serialization broken"; + EXPECT_EQ(v, std::vector<int>({42, 7})) + << "std::vector serialization broken"; + return Error::success(); + }); EXPECT_FALSE(EC) << "Big (serialization test) call over queue failed"; } { - // Expect a call to Proc1. - auto EC = expect<AllTheTypes>(C, - [&](int8_t &s8, - uint8_t &u8, - int16_t &s16, - uint16_t &u16, - int32_t &s32, - uint32_t &u32, - int64_t &s64, - uint64_t &u64, - bool &b, - std::string &s, - std::vector<int> &v) { - - EXPECT_EQ(s8, -101) - << "int8_t serialization broken"; - EXPECT_EQ(u8, 250) - << "uint8_t serialization broken"; - EXPECT_EQ(s16, -10000) - << "int16_t serialization broken"; - EXPECT_EQ(u16, 10000) - << "uint16_t serialization broken"; - EXPECT_EQ(s32, -1000000000) - << "int32_t serialization broken"; - EXPECT_EQ(u32, 1000000000ULL) - << "uint32_t serialization broken"; - EXPECT_EQ(s64, -10000000000) - << "int64_t serialization broken"; - EXPECT_EQ(u64, 10000000000ULL) - << "uint64_t serialization broken"; - EXPECT_EQ(b, true) - << "bool serialization broken"; - EXPECT_EQ(s, "foo") - << "std::string serialization broken"; - EXPECT_EQ(v, std::vector<int>({42, 7})) - << "std::vector serialization broken"; - return std::error_code(); - }); - EXPECT_FALSE(EC) << "Big (serialization test) call over queue failed"; + // Wait for the result. + auto EC = waitForResult(C1, ResOrErr->second, handleNone); + EXPECT_FALSE(EC) << "Could not read result."; } + + // Verify that the function returned ok. + auto Val = ResOrErr->first.get(); + EXPECT_TRUE(Val) << "Remote void function failed to execute."; } + +// Test the synchronous call API. +// FIXME: Re-enable once deadlock encountered on S390 has been debugged / fixed, +// see http://lab.llvm.org:8011/builders/clang-s390x-linux/builds/3459 +// TEST_F(DummyRPC, TestSynchronousCall) { +// Queue Q1, Q2; +// QueueChannel C1(Q1, Q2); +// QueueChannel C2(Q2, Q1); +// +// auto ServerResult = +// std::async(std::launch::async, +// [&]() { +// return expect<IntInt>(C2, [&](int32_t V) { return V; }); +// }); +// +// auto ValOrErr = callST<IntInt>(C1, 42); +// +// EXPECT_FALSE(!!ServerResult.get()) +// << "Server returned an error."; +// EXPECT_TRUE(!!ValOrErr) +// << "callST returned an error."; +// EXPECT_EQ(*ValOrErr, 42) +// << "Incorrect callST<IntInt> result"; +// } |