//===----------- RPCUtilsTest.cpp - Unit tests the Orc RPC utils ----------===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "llvm/ExecutionEngine/Orc/RawByteChannel.h" #include "llvm/ExecutionEngine/Orc/RPCUtils.h" #include "gtest/gtest.h" #include using namespace llvm; using namespace llvm::orc; using namespace llvm::orc::rpc; class Queue : public std::queue { public: std::mutex &getLock() { return Lock; } private: std::mutex Lock; }; class QueueChannel : public RawByteChannel { 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 Lock(InQueue.getLock()); while (!InQueue.empty() && Size) { *Dst++ = InQueue.front(); --Size; InQueue.pop(); } } return Error::success(); } Error appendBytes(const char *Src, unsigned Size) override { std::lock_guard Lock(OutQueue.getLock()); while (Size--) OutQueue.push(*Src++); return Error::success(); } Error send() override { return Error::success(); } private: Queue &InQueue; Queue &OutQueue; }; class DummyRPCAPI { public: class VoidBool : public Function { public: static const char* getName() { return "VoidBool"; } }; class IntInt : public Function { public: static const char* getName() { return "IntInt"; } }; class AllTheTypes : public Function)> { public: static const char* getName() { return "AllTheTypes"; } }; }; class DummyRPCEndpoint : public DummyRPCAPI, public SingleThreadedRPC { public: DummyRPCEndpoint(Queue &Q1, Queue &Q2) : SingleThreadedRPC(C, true), C(Q1, Q2) {} private: QueueChannel C; }; // FIXME: Temporarily disabled while the infnite loop discovered in r286639 is // fixed (see llvm commits list discussion for that commit). // TEST(DummyRPC, TestAsyncVoidBool) { // Queue Q1, Q2; // DummyRPCEndpoint Client(Q1, Q2); // DummyRPCEndpoint Server(Q2, Q1); // std::thread ServerThread([&]() { // Server.addHandler( // [](bool B) { // EXPECT_EQ(B, true) // << "Server void(bool) received unexpected result"; // }); // { // // Poke the server to handle the negotiate call. // auto Err = Server.handleOne(); // EXPECT_FALSE(!!Err) << "Server failed to handle call to negotiate"; // } // { // // Poke the server to handle the VoidBool call. // auto Err = Server.handleOne(); // EXPECT_FALSE(!!Err) << "Server failed to handle call to void(bool)"; // } // }); // { // // Make an async call. // auto Err = Client.callAsync( // [](Error Err) { // EXPECT_FALSE(!!Err) << "Async void(bool) response handler failed"; // return Error::success(); // }, true); // EXPECT_FALSE(!!Err) << "Client.callAsync failed for void(bool)"; // } // { // // Poke the client to process the result of the void(bool) call. // auto Err = Client.handleOne(); // EXPECT_FALSE(!!Err) << "Client failed to handle response from void(bool)"; // } // ServerThread.join(); // } // TEST(DummyRPC, TestAsyncIntInt) { // Queue Q1, Q2; // DummyRPCEndpoint Client(Q1, Q2); // DummyRPCEndpoint Server(Q2, Q1); // std::thread ServerThread([&]() { // Server.addHandler( // [](int X) -> int { // EXPECT_EQ(X, 21) << "Server int(int) receieved unexpected result"; // return 2 * X; // }); // { // // Poke the server to handle the negotiate call. // auto Err = Server.handleOne(); // EXPECT_FALSE(!!Err) << "Server failed to handle call to negotiate"; // } // { // // Poke the server to handle the int(int) call. // auto Err = Server.handleOne(); // EXPECT_FALSE(!!Err) << "Server failed to handle call to int(int)"; // } // }); // { // auto Err = Client.callAsync( // [](Expected Result) { // EXPECT_TRUE(!!Result) << "Async int(int) response handler failed"; // EXPECT_EQ(*Result, 42) // << "Async int(int) response handler received incorrect result"; // return Error::success(); // }, 21); // EXPECT_FALSE(!!Err) << "Client.callAsync failed for int(int)"; // } // { // // Poke the client to process the result. // auto Err = Client.handleOne(); // EXPECT_FALSE(!!Err) << "Client failed to handle response from void(bool)"; // } // ServerThread.join(); // } // TEST(DummyRPC, TestSerialization) { // Queue Q1, Q2; // DummyRPCEndpoint Client(Q1, Q2); // DummyRPCEndpoint Server(Q2, Q1); // std::thread ServerThread([&]() { // Server.addHandler( // [&](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 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({42, 7})) // << "std::vector serialization broken"; // return Error::success(); // }); // { // // Poke the server to handle the negotiate call. // auto Err = Server.handleOne(); // EXPECT_FALSE(!!Err) << "Server failed to handle call to negotiate"; // } // { // // Poke the server to handle the AllTheTypes call. // auto Err = Server.handleOne(); // EXPECT_FALSE(!!Err) << "Server failed to handle call to void(bool)"; // } // }); // { // // Make an async call. // std::vector v({42, 7}); // auto Err = Client.callAsync( // [](Error Err) { // EXPECT_FALSE(!!Err) << "Async AllTheTypes response handler failed"; // return Error::success(); // }, // static_cast(-101), static_cast(250), // static_cast(-10000), static_cast(10000), // static_cast(-1000000000), static_cast(1000000000), // static_cast(-10000000000), static_cast(10000000000), // true, std::string("foo"), v); // EXPECT_FALSE(!!Err) << "Client.callAsync failed for AllTheTypes"; // } // { // // Poke the client to process the result of the AllTheTypes call. // auto Err = Client.handleOne(); // EXPECT_FALSE(!!Err) << "Client failed to handle response from AllTheTypes"; // } // ServerThread.join(); // } // Test the synchronous call API. // TEST_F(DummyRPC, TestSynchronousCall) { // Queue Q1, Q2; // QueueChannel C1(Q1, Q2); // QueueChannel C2(Q2, Q1); // // auto ServerResult = // std::async(std::launch::async, // [&]() { // return expect(C2, [&](int32_t V) { return V; }); // }); // // auto ValOrErr = callST(C1, 42); // // EXPECT_FALSE(!!ServerResult.get()) // << "Server returned an error."; // EXPECT_TRUE(!!ValOrErr) // << "callST returned an error."; // EXPECT_EQ(*ValOrErr, 42) // << "Incorrect callST result"; // }