1 /*
2 * Copyright (C) 2020 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include "message_queue.h"
18
19 #include <thread>
20
21 #include "common_runtime_test.h"
22 #include "thread-current-inl.h"
23 #include "runtime.h"
24
25 namespace art HIDDEN {
26
27 class MessageQueueTest : public CommonRuntimeTest {
28 protected:
MessageQueueTest()29 MessageQueueTest() {
30 this->use_boot_image_ = true; // Make the Runtime creation cheaper.
31 }
32 };
33
34 namespace {
35
36 // Define some message types
37 struct EmptyMessage {};
38 struct IntMessage {
39 int value;
40 };
41 struct OtherIntMessage {
42 int other_value;
43 };
44 struct TwoIntMessage {
45 int value1;
46 int value2;
47 };
48 struct StringMessage {
49 std::string message;
50 };
51
52 using TestMessageQueue =
53 MessageQueue<EmptyMessage, IntMessage, OtherIntMessage, TwoIntMessage, StringMessage>;
54
55 } // namespace
56
TEST_F(MessageQueueTest,SendReceiveTest)57 TEST_F(MessageQueueTest, SendReceiveTest) {
58 TestMessageQueue queue;
59
60 queue.SendMessage(EmptyMessage{});
61 ASSERT_TRUE(std::holds_alternative<EmptyMessage>(queue.ReceiveMessage()));
62
63 queue.SendMessage(IntMessage{42});
64 ASSERT_TRUE(std::holds_alternative<IntMessage>(queue.ReceiveMessage()));
65
66 queue.SendMessage(OtherIntMessage{43});
67 ASSERT_TRUE(std::holds_alternative<OtherIntMessage>(queue.ReceiveMessage()));
68
69 queue.SendMessage(TwoIntMessage{1, 2});
70 ASSERT_TRUE(std::holds_alternative<TwoIntMessage>(queue.ReceiveMessage()));
71
72 queue.SendMessage(StringMessage{"Hello, World!"});
73 ASSERT_TRUE(std::holds_alternative<StringMessage>(queue.ReceiveMessage()));
74 }
75
TEST_F(MessageQueueTest,TestTimeout)76 TEST_F(MessageQueueTest, TestTimeout) {
77 TestMessageQueue queue;
78
79 constexpr uint64_t kDuration = 500;
80
81 const auto start = MilliTime();
82 queue.SetTimeout(kDuration);
83 ASSERT_TRUE(std::holds_alternative<TimeoutExpiredMessage>(queue.ReceiveMessage()));
84 const auto elapsed = MilliTime() - start;
85
86 ASSERT_GT(elapsed, kDuration);
87 }
88
TEST_F(MessageQueueTest,TwoWayMessaging)89 TEST_F(MessageQueueTest, TwoWayMessaging) {
90 CHECK(Runtime::Current() != nullptr); // Runtime is needed by Mutex.
91
92 TestMessageQueue queue1;
93 TestMessageQueue queue2;
94
95 std::thread thread{[&]() {
96 // Tell the parent thread we are running.
97 queue1.SendMessage(EmptyMessage{});
98
99 // Wait for a message from the parent thread.
100 queue2.ReceiveMessage();
101 }};
102
103 queue1.ReceiveMessage();
104 queue2.SendMessage(EmptyMessage{});
105
106 thread.join();
107 }
108
TEST_F(MessageQueueTest,SwitchReceiveTest)109 TEST_F(MessageQueueTest, SwitchReceiveTest) {
110 TestMessageQueue queue;
111
112 queue.SendMessage(EmptyMessage{});
113 queue.SendMessage(IntMessage{42});
114 queue.SendMessage(OtherIntMessage{43});
115 queue.SendMessage(TwoIntMessage{1, 2});
116 queue.SendMessage(StringMessage{"Hello, World!"});
117 queue.SetTimeout(500);
118
119 bool empty_received = false;
120 bool int_received = false;
121 bool other_int_received = false;
122 bool two_int_received = false;
123 bool string_received = false;
124 bool timeout_received = false;
125
126 while (!(empty_received && int_received && other_int_received && two_int_received &&
127 string_received && timeout_received)) {
128 queue.SwitchReceive(
129 [&]([[maybe_unused]] const EmptyMessage& message) {
130 ASSERT_FALSE(empty_received);
131 empty_received = true;
132 },
133 [&](const IntMessage& message) {
134 ASSERT_FALSE(int_received);
135 int_received = true;
136
137 ASSERT_EQ(message.value, 42);
138 },
139 [&](const OtherIntMessage& message) {
140 ASSERT_FALSE(other_int_received);
141 other_int_received = true;
142
143 ASSERT_EQ(message.other_value, 43);
144 },
145 // The timeout message is here to make sure the cases can go in any order
146 [&]([[maybe_unused]] const TimeoutExpiredMessage& message) {
147 ASSERT_FALSE(timeout_received);
148 timeout_received = true;
149 },
150 [&](const TwoIntMessage& message) {
151 ASSERT_FALSE(two_int_received);
152 two_int_received = true;
153
154 ASSERT_EQ(message.value1, 1);
155 ASSERT_EQ(message.value2, 2);
156 },
157 [&](const StringMessage& message) {
158 ASSERT_FALSE(string_received);
159 string_received = true;
160
161 ASSERT_EQ(message.message, "Hello, World!");
162 });
163 }
164 }
165
TEST_F(MessageQueueTest,SwitchReceiveAutoTest)166 TEST_F(MessageQueueTest, SwitchReceiveAutoTest) {
167 TestMessageQueue queue;
168
169 queue.SendMessage(EmptyMessage{});
170 queue.SendMessage(IntMessage{42});
171 queue.SendMessage(OtherIntMessage{43});
172 queue.SendMessage(TwoIntMessage{1, 2});
173 queue.SendMessage(StringMessage{"Hello, World!"});
174 queue.SetTimeout(500);
175
176 int pending_messages = 6;
177
178 while (pending_messages > 0) {
179 queue.SwitchReceive([&]([[maybe_unused]] auto message) { pending_messages--; });
180 }
181 }
182
TEST_F(MessageQueueTest,SwitchReceivePartialAutoTest)183 TEST_F(MessageQueueTest, SwitchReceivePartialAutoTest) {
184 TestMessageQueue queue;
185
186 queue.SendMessage(EmptyMessage{});
187 queue.SendMessage(IntMessage{42});
188 queue.SendMessage(OtherIntMessage{43});
189 queue.SendMessage(TwoIntMessage{1, 2});
190 queue.SendMessage(StringMessage{"Hello, World!"});
191 queue.SetTimeout(500);
192
193 bool running = true;
194 while (running) {
195 queue.SwitchReceive(
196 [&](const StringMessage& message) {
197 ASSERT_EQ(message.message, "Hello, World!");
198 running = false;
199 },
200 [&]([[maybe_unused]] const auto& message) {
201 const bool is_string{std::is_same<StringMessage, decltype(message)>()};
202 ASSERT_FALSE(is_string);
203 });
204 }
205 }
206
TEST_F(MessageQueueTest,SwitchReceiveReturn)207 TEST_F(MessageQueueTest, SwitchReceiveReturn) {
208 TestMessageQueue queue;
209
210 queue.SendMessage(EmptyMessage{});
211
212 ASSERT_TRUE(
213 queue.SwitchReceive<bool>([&]([[maybe_unused]] const EmptyMessage& message) { return true; },
214 [&]([[maybe_unused]] const auto& message) { return false; }));
215
216 queue.SendMessage(IntMessage{42});
217
218 ASSERT_FALSE(
219 queue.SwitchReceive<bool>([&]([[maybe_unused]] const EmptyMessage& message) { return true; },
220 [&]([[maybe_unused]] const auto& message) { return false; }));
221 }
222
TEST_F(MessageQueueTest,ReceiveInOrder)223 TEST_F(MessageQueueTest, ReceiveInOrder) {
224 TestMessageQueue queue;
225
226 std::vector<TestMessageQueue::Message> messages{
227 EmptyMessage{},
228 IntMessage{42},
229 OtherIntMessage{43},
230 TwoIntMessage{1, 2},
231 StringMessage{"Hello, World!"},
232 };
233
234 // Send the messages
235 for (const auto& message : messages) {
236 queue.SendMessage(message);
237 }
238 queue.SetTimeout(500);
239
240 // Receive the messages. Make sure they came in order, except for the TimeoutExpiredMessage, which
241 // can come at any time.
242 bool received_timeout = false;
243 size_t i = 0;
244 while (i < messages.size()) {
245 auto message = queue.ReceiveMessage();
246 if (std::holds_alternative<TimeoutExpiredMessage>(message)) {
247 ASSERT_FALSE(received_timeout);
248 received_timeout = true;
249 } else {
250 ASSERT_EQ(message.index(), messages[i].index());
251 i++;
252 }
253 }
254 if (!received_timeout) {
255 // If we have not received the timeout yet, receive one more message and make sure it's the
256 // timeout.
257 ASSERT_TRUE(std::holds_alternative<TimeoutExpiredMessage>(queue.ReceiveMessage()));
258 }
259 }
260
261 } // namespace art
262