1 //===----------- RPCUtilsTest.cpp - Unit tests the Orc RPC utils ----------===//
2 //
3 // The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9
10 #include "llvm/ExecutionEngine/Orc/RPCChannel.h"
11 #include "llvm/ExecutionEngine/Orc/RPCUtils.h"
12 #include "gtest/gtest.h"
13
14 #include <queue>
15
16 using namespace llvm;
17 using namespace llvm::orc;
18 using namespace llvm::orc::remote;
19
20 class Queue : public std::queue<char> {
21 public:
getLock()22 std::mutex &getLock() { return Lock; }
23
24 private:
25 std::mutex Lock;
26 };
27
28 class QueueChannel : public RPCChannel {
29 public:
QueueChannel(Queue & InQueue,Queue & OutQueue)30 QueueChannel(Queue &InQueue, Queue &OutQueue)
31 : InQueue(InQueue), OutQueue(OutQueue) {}
32
readBytes(char * Dst,unsigned Size)33 Error readBytes(char *Dst, unsigned Size) override {
34 while (Size != 0) {
35 // If there's nothing to read then yield.
36 while (InQueue.empty())
37 std::this_thread::yield();
38
39 // Lock the channel and read what we can.
40 std::lock_guard<std::mutex> Lock(InQueue.getLock());
41 while (!InQueue.empty() && Size) {
42 *Dst++ = InQueue.front();
43 --Size;
44 InQueue.pop();
45 }
46 }
47 return Error::success();
48 }
49
appendBytes(const char * Src,unsigned Size)50 Error appendBytes(const char *Src, unsigned Size) override {
51 std::lock_guard<std::mutex> Lock(OutQueue.getLock());
52 while (Size--)
53 OutQueue.push(*Src++);
54 return Error::success();
55 }
56
send()57 Error send() override { return Error::success(); }
58
59 private:
60 Queue &InQueue;
61 Queue &OutQueue;
62 };
63
64 class DummyRPC : public testing::Test, public RPC<QueueChannel> {
65 public:
66 enum FuncId : uint32_t {
67 VoidBoolId = RPCFunctionIdTraits<FuncId>::FirstValidId,
68 IntIntId,
69 AllTheTypesId
70 };
71
72 typedef Function<VoidBoolId, void(bool)> VoidBool;
73 typedef Function<IntIntId, int32_t(int32_t)> IntInt;
74 typedef Function<AllTheTypesId,
75 void(int8_t, uint8_t, int16_t, uint16_t, int32_t, uint32_t,
76 int64_t, uint64_t, bool, std::string, std::vector<int>)>
77 AllTheTypes;
78 };
79
TEST_F(DummyRPC,TestAsyncVoidBool)80 TEST_F(DummyRPC, TestAsyncVoidBool) {
81 Queue Q1, Q2;
82 QueueChannel C1(Q1, Q2);
83 QueueChannel C2(Q2, Q1);
84
85 // Make an async call.
86 auto ResOrErr = callAsyncWithSeq<VoidBool>(C1, true);
87 EXPECT_TRUE(!!ResOrErr) << "Simple call over queue failed";
88
89 {
90 // Expect a call to Proc1.
91 auto EC = expect<VoidBool>(C2, [&](bool &B) {
92 EXPECT_EQ(B, true) << "Bool serialization broken";
93 return Error::success();
94 });
95 EXPECT_FALSE(EC) << "Simple expect over queue failed";
96 }
97
98 {
99 // Wait for the result.
100 auto EC = waitForResult(C1, ResOrErr->second, handleNone);
101 EXPECT_FALSE(EC) << "Could not read result.";
102 }
103
104 // Verify that the function returned ok.
105 auto Val = ResOrErr->first.get();
106 EXPECT_TRUE(Val) << "Remote void function failed to execute.";
107 }
108
TEST_F(DummyRPC,TestAsyncIntInt)109 TEST_F(DummyRPC, TestAsyncIntInt) {
110 Queue Q1, Q2;
111 QueueChannel C1(Q1, Q2);
112 QueueChannel C2(Q2, Q1);
113
114 // Make an async call.
115 auto ResOrErr = callAsyncWithSeq<IntInt>(C1, 21);
116 EXPECT_TRUE(!!ResOrErr) << "Simple call over queue failed";
117
118 {
119 // Expect a call to Proc1.
120 auto EC = expect<IntInt>(C2, [&](int32_t I) -> Expected<int32_t> {
121 EXPECT_EQ(I, 21) << "Bool serialization broken";
122 return 2 * I;
123 });
124 EXPECT_FALSE(EC) << "Simple expect over queue failed";
125 }
126
127 {
128 // Wait for the result.
129 auto EC = waitForResult(C1, ResOrErr->second, handleNone);
130 EXPECT_FALSE(EC) << "Could not read result.";
131 }
132
133 // Verify that the function returned ok.
134 auto Val = ResOrErr->first.get();
135 EXPECT_TRUE(!!Val) << "Remote int function failed to execute.";
136 EXPECT_EQ(*Val, 42) << "Remote int function return wrong value.";
137 }
138
TEST_F(DummyRPC,TestSerialization)139 TEST_F(DummyRPC, TestSerialization) {
140 Queue Q1, Q2;
141 QueueChannel C1(Q1, Q2);
142 QueueChannel C2(Q2, Q1);
143
144 // Make a call to Proc1.
145 std::vector<int> v({42, 7});
146 auto ResOrErr = callAsyncWithSeq<AllTheTypes>(
147 C1, -101, 250, -10000, 10000, -1000000000, 1000000000, -10000000000,
148 10000000000, true, "foo", v);
149 EXPECT_TRUE(!!ResOrErr) << "Big (serialization test) call over queue failed";
150
151 {
152 // Expect a call to Proc1.
153 auto EC = expect<AllTheTypes>(
154 C2, [&](int8_t &s8, uint8_t &u8, int16_t &s16, uint16_t &u16,
155 int32_t &s32, uint32_t &u32, int64_t &s64, uint64_t &u64,
156 bool &b, std::string &s, std::vector<int> &v) {
157
158 EXPECT_EQ(s8, -101) << "int8_t serialization broken";
159 EXPECT_EQ(u8, 250) << "uint8_t serialization broken";
160 EXPECT_EQ(s16, -10000) << "int16_t serialization broken";
161 EXPECT_EQ(u16, 10000) << "uint16_t serialization broken";
162 EXPECT_EQ(s32, -1000000000) << "int32_t serialization broken";
163 EXPECT_EQ(u32, 1000000000ULL) << "uint32_t serialization broken";
164 EXPECT_EQ(s64, -10000000000) << "int64_t serialization broken";
165 EXPECT_EQ(u64, 10000000000ULL) << "uint64_t serialization broken";
166 EXPECT_EQ(b, true) << "bool serialization broken";
167 EXPECT_EQ(s, "foo") << "std::string serialization broken";
168 EXPECT_EQ(v, std::vector<int>({42, 7}))
169 << "std::vector serialization broken";
170 return Error::success();
171 });
172 EXPECT_FALSE(EC) << "Big (serialization test) call over queue failed";
173 }
174
175 {
176 // Wait for the result.
177 auto EC = waitForResult(C1, ResOrErr->second, handleNone);
178 EXPECT_FALSE(EC) << "Could not read result.";
179 }
180
181 // Verify that the function returned ok.
182 auto Val = ResOrErr->first.get();
183 EXPECT_TRUE(Val) << "Remote void function failed to execute.";
184 }
185
186 // Test the synchronous call API.
187 // FIXME: Re-enable once deadlock encountered on S390 has been debugged / fixed,
188 // see http://lab.llvm.org:8011/builders/clang-s390x-linux/builds/3459
189 // TEST_F(DummyRPC, TestSynchronousCall) {
190 // Queue Q1, Q2;
191 // QueueChannel C1(Q1, Q2);
192 // QueueChannel C2(Q2, Q1);
193 //
194 // auto ServerResult =
195 // std::async(std::launch::async,
196 // [&]() {
197 // return expect<IntInt>(C2, [&](int32_t V) { return V; });
198 // });
199 //
200 // auto ValOrErr = callST<IntInt>(C1, 42);
201 //
202 // EXPECT_FALSE(!!ServerResult.get())
203 // << "Server returned an error.";
204 // EXPECT_TRUE(!!ValOrErr)
205 // << "callST returned an error.";
206 // EXPECT_EQ(*ValOrErr, 42)
207 // << "Incorrect callST<IntInt> result";
208 // }
209