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
24 namespace art {
25
26 class MessageQueueTest : public CommonRuntimeTest {};
27
28 namespace {
29
30 // Define some message types
31 struct EmptyMessage {};
32 struct IntMessage {
33 int value;
34 };
35 struct OtherIntMessage {
36 int other_value;
37 };
38 struct TwoIntMessage {
39 int value1;
40 int value2;
41 };
42 struct StringMessage {
43 std::string message;
44 };
45
46 using TestMessageQueue =
47 MessageQueue<EmptyMessage, IntMessage, OtherIntMessage, TwoIntMessage, StringMessage>;
48
49 } // namespace
50
TEST_F(MessageQueueTest,SendReceiveTest)51 TEST_F(MessageQueueTest, SendReceiveTest) {
52 TestMessageQueue queue;
53
54 queue.SendMessage(EmptyMessage{});
55 ASSERT_TRUE(std::holds_alternative<EmptyMessage>(queue.ReceiveMessage()));
56
57 queue.SendMessage(IntMessage{42});
58 ASSERT_TRUE(std::holds_alternative<IntMessage>(queue.ReceiveMessage()));
59
60 queue.SendMessage(OtherIntMessage{43});
61 ASSERT_TRUE(std::holds_alternative<OtherIntMessage>(queue.ReceiveMessage()));
62
63 queue.SendMessage(TwoIntMessage{1, 2});
64 ASSERT_TRUE(std::holds_alternative<TwoIntMessage>(queue.ReceiveMessage()));
65
66 queue.SendMessage(StringMessage{"Hello, World!"});
67 ASSERT_TRUE(std::holds_alternative<StringMessage>(queue.ReceiveMessage()));
68 }
69
TEST_F(MessageQueueTest,TestTimeout)70 TEST_F(MessageQueueTest, TestTimeout) {
71 TestMessageQueue queue;
72
73 constexpr uint64_t kDuration = 500;
74
75 const auto start = MilliTime();
76 queue.SetTimeout(kDuration);
77 ASSERT_TRUE(std::holds_alternative<TimeoutExpiredMessage>(queue.ReceiveMessage()));
78 const auto elapsed = MilliTime() - start;
79
80 ASSERT_GT(elapsed, kDuration);
81 }
82
TEST_F(MessageQueueTest,TwoWayMessaging)83 TEST_F(MessageQueueTest, TwoWayMessaging) {
84 TestMessageQueue queue1;
85 TestMessageQueue queue2;
86
87 std::thread thread{[&]() {
88 // Tell the parent thread we are running.
89 queue1.SendMessage(EmptyMessage{});
90
91 // Wait for a message from the parent thread.
92 queue2.ReceiveMessage();
93 }};
94
95 queue1.ReceiveMessage();
96 queue2.SendMessage(EmptyMessage{});
97
98 thread.join();
99 }
100
TEST_F(MessageQueueTest,SwitchReceiveTest)101 TEST_F(MessageQueueTest, SwitchReceiveTest) {
102 TestMessageQueue queue;
103
104 queue.SendMessage(EmptyMessage{});
105 queue.SendMessage(IntMessage{42});
106 queue.SendMessage(OtherIntMessage{43});
107 queue.SendMessage(TwoIntMessage{1, 2});
108 queue.SendMessage(StringMessage{"Hello, World!"});
109 queue.SetTimeout(500);
110
111 bool empty_received = false;
112 bool int_received = false;
113 bool other_int_received = false;
114 bool two_int_received = false;
115 bool string_received = false;
116 bool timeout_received = false;
117
118 while (!(empty_received && int_received && other_int_received && two_int_received &&
119 string_received && timeout_received)) {
120 queue.SwitchReceive(
121 [&]([[maybe_unused]] const EmptyMessage& message) {
122 ASSERT_FALSE(empty_received);
123 empty_received = true;
124 },
125 [&](const IntMessage& message) {
126 ASSERT_FALSE(int_received);
127 int_received = true;
128
129 ASSERT_EQ(message.value, 42);
130 },
131 [&](const OtherIntMessage& message) {
132 ASSERT_FALSE(other_int_received);
133 other_int_received = true;
134
135 ASSERT_EQ(message.other_value, 43);
136 },
137 // The timeout message is here to make sure the cases can go in any order
138 [&]([[maybe_unused]] const TimeoutExpiredMessage& message) {
139 ASSERT_FALSE(timeout_received);
140 timeout_received = true;
141 },
142 [&](const TwoIntMessage& message) {
143 ASSERT_FALSE(two_int_received);
144 two_int_received = true;
145
146 ASSERT_EQ(message.value1, 1);
147 ASSERT_EQ(message.value2, 2);
148 },
149 [&](const StringMessage& message) {
150 ASSERT_FALSE(string_received);
151 string_received = true;
152
153 ASSERT_EQ(message.message, "Hello, World!");
154 });
155 }
156 }
157
TEST_F(MessageQueueTest,SwitchReceiveAutoTest)158 TEST_F(MessageQueueTest, SwitchReceiveAutoTest) {
159 TestMessageQueue queue;
160
161 queue.SendMessage(EmptyMessage{});
162 queue.SendMessage(IntMessage{42});
163 queue.SendMessage(OtherIntMessage{43});
164 queue.SendMessage(TwoIntMessage{1, 2});
165 queue.SendMessage(StringMessage{"Hello, World!"});
166 queue.SetTimeout(500);
167
168 int pending_messages = 6;
169
170 while (pending_messages > 0) {
171 queue.SwitchReceive([&]([[maybe_unused]] auto message) { pending_messages--; });
172 }
173 }
174
TEST_F(MessageQueueTest,SwitchReceivePartialAutoTest)175 TEST_F(MessageQueueTest, SwitchReceivePartialAutoTest) {
176 TestMessageQueue queue;
177
178 queue.SendMessage(EmptyMessage{});
179 queue.SendMessage(IntMessage{42});
180 queue.SendMessage(OtherIntMessage{43});
181 queue.SendMessage(TwoIntMessage{1, 2});
182 queue.SendMessage(StringMessage{"Hello, World!"});
183 queue.SetTimeout(500);
184
185 bool running = true;
186 while (running) {
187 queue.SwitchReceive(
188 [&](const StringMessage& message) {
189 ASSERT_EQ(message.message, "Hello, World!");
190 running = false;
191 },
192 [&]([[maybe_unused]] const auto& message) {
193 const bool is_string{std::is_same<StringMessage, decltype(message)>()};
194 ASSERT_FALSE(is_string);
195 });
196 }
197 }
198
TEST_F(MessageQueueTest,SwitchReceiveReturn)199 TEST_F(MessageQueueTest, SwitchReceiveReturn) {
200 TestMessageQueue queue;
201
202 queue.SendMessage(EmptyMessage{});
203
204 ASSERT_TRUE(
205 queue.SwitchReceive<bool>([&]([[maybe_unused]] const EmptyMessage& message) { return true; },
206 [&]([[maybe_unused]] const auto& message) { return false; }));
207
208 queue.SendMessage(IntMessage{42});
209
210 ASSERT_FALSE(
211 queue.SwitchReceive<bool>([&]([[maybe_unused]] const EmptyMessage& message) { return true; },
212 [&]([[maybe_unused]] const auto& message) { return false; }));
213 }
214
TEST_F(MessageQueueTest,ReceiveInOrder)215 TEST_F(MessageQueueTest, ReceiveInOrder) {
216 TestMessageQueue queue;
217
218 std::vector<TestMessageQueue::Message> messages{
219 EmptyMessage{},
220 IntMessage{42},
221 OtherIntMessage{43},
222 TwoIntMessage{1, 2},
223 StringMessage{"Hello, World!"},
224 };
225
226 // Send the messages
227 for (const auto& message : messages) {
228 queue.SendMessage(message);
229 }
230 queue.SetTimeout(500);
231
232 // Receive the messages. Make sure they came in order, except for the TimeoutExpiredMessage, which
233 // can come at any time.
234 bool received_timeout = false;
235 size_t i = 0;
236 while (i < messages.size()) {
237 auto message = queue.ReceiveMessage();
238 if (std::holds_alternative<TimeoutExpiredMessage>(message)) {
239 ASSERT_FALSE(received_timeout);
240 received_timeout = true;
241 } else {
242 ASSERT_EQ(message.index(), messages[i].index());
243 i++;
244 }
245 }
246 if (!received_timeout) {
247 // If we have not received the timeout yet, receive one more message and make sure it's the
248 // timeout.
249 ASSERT_TRUE(std::holds_alternative<TimeoutExpiredMessage>(queue.ReceiveMessage()));
250 }
251 }
252
253 } // namespace art
254