| /* | |
| * Copyright (C) 2020 The Android Open Source Project | |
| * | |
| * Licensed under the Apache License, Version 2.0 (the "License"); | |
| * you may not use this file except in compliance with the License. | |
| * You may obtain a copy of the License at | |
| * | |
| * http://www.apache.org/licenses/LICENSE-2.0 | |
| * | |
| * Unless required by applicable law or agreed to in writing, software | |
| * distributed under the License is distributed on an "AS IS" BASIS, | |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| * See the License for the specific language governing permissions and | |
| * limitations under the License. | |
| */ | |
| #include "message_queue.h" | |
| #include <thread> | |
| #include "common_runtime_test.h" | |
| #include "thread-current-inl.h" | |
| #include "runtime.h" | |
| namespace art HIDDEN { | |
| class MessageQueueTest : public CommonRuntimeTest { | |
| protected: | |
| MessageQueueTest() { | |
| this->use_boot_image_ = true; // Make the Runtime creation cheaper. | |
| } | |
| }; | |
| namespace { | |
| // Define some message types | |
| struct EmptyMessage {}; | |
| struct IntMessage { | |
| int value; | |
| }; | |
| struct OtherIntMessage { | |
| int other_value; | |
| }; | |
| struct TwoIntMessage { | |
| int value1; | |
| int value2; | |
| }; | |
| struct StringMessage { | |
| std::string message; | |
| }; | |
| using TestMessageQueue = | |
| MessageQueue<EmptyMessage, IntMessage, OtherIntMessage, TwoIntMessage, StringMessage>; | |
| } // namespace | |
| TEST_F(MessageQueueTest, SendReceiveTest) { | |
| TestMessageQueue queue; | |
| queue.SendMessage(EmptyMessage{}); | |
| ASSERT_TRUE(std::holds_alternative<EmptyMessage>(queue.ReceiveMessage())); | |
| queue.SendMessage(IntMessage{42}); | |
| ASSERT_TRUE(std::holds_alternative<IntMessage>(queue.ReceiveMessage())); | |
| queue.SendMessage(OtherIntMessage{43}); | |
| ASSERT_TRUE(std::holds_alternative<OtherIntMessage>(queue.ReceiveMessage())); | |
| queue.SendMessage(TwoIntMessage{1, 2}); | |
| ASSERT_TRUE(std::holds_alternative<TwoIntMessage>(queue.ReceiveMessage())); | |
| queue.SendMessage(StringMessage{"Hello, World!"}); | |
| ASSERT_TRUE(std::holds_alternative<StringMessage>(queue.ReceiveMessage())); | |
| } | |
| TEST_F(MessageQueueTest, TestTimeout) { | |
| TestMessageQueue queue; | |
| constexpr uint64_t kDuration = 500; | |
| const auto start = MilliTime(); | |
| queue.SetTimeout(kDuration); | |
| ASSERT_TRUE(std::holds_alternative<TimeoutExpiredMessage>(queue.ReceiveMessage())); | |
| const auto elapsed = MilliTime() - start; | |
| ASSERT_GT(elapsed, kDuration); | |
| } | |
| TEST_F(MessageQueueTest, TwoWayMessaging) { | |
| CHECK(Runtime::Current() != nullptr); // Runtime is needed by Mutex. | |
| TestMessageQueue queue1; | |
| TestMessageQueue queue2; | |
| std::thread thread{[&]() { | |
| // Tell the parent thread we are running. | |
| queue1.SendMessage(EmptyMessage{}); | |
| // Wait for a message from the parent thread. | |
| queue2.ReceiveMessage(); | |
| }}; | |
| queue1.ReceiveMessage(); | |
| queue2.SendMessage(EmptyMessage{}); | |
| thread.join(); | |
| } | |
| TEST_F(MessageQueueTest, SwitchReceiveTest) { | |
| TestMessageQueue queue; | |
| queue.SendMessage(EmptyMessage{}); | |
| queue.SendMessage(IntMessage{42}); | |
| queue.SendMessage(OtherIntMessage{43}); | |
| queue.SendMessage(TwoIntMessage{1, 2}); | |
| queue.SendMessage(StringMessage{"Hello, World!"}); | |
| queue.SetTimeout(500); | |
| bool empty_received = false; | |
| bool int_received = false; | |
| bool other_int_received = false; | |
| bool two_int_received = false; | |
| bool string_received = false; | |
| bool timeout_received = false; | |
| while (!(empty_received && int_received && other_int_received && two_int_received && | |
| string_received && timeout_received)) { | |
| queue.SwitchReceive( | |
| [&]([[maybe_unused]] const EmptyMessage& message) { | |
| ASSERT_FALSE(empty_received); | |
| empty_received = true; | |
| }, | |
| [&](const IntMessage& message) { | |
| ASSERT_FALSE(int_received); | |
| int_received = true; | |
| ASSERT_EQ(message.value, 42); | |
| }, | |
| [&](const OtherIntMessage& message) { | |
| ASSERT_FALSE(other_int_received); | |
| other_int_received = true; | |
| ASSERT_EQ(message.other_value, 43); | |
| }, | |
| // The timeout message is here to make sure the cases can go in any order | |
| [&]([[maybe_unused]] const TimeoutExpiredMessage& message) { | |
| ASSERT_FALSE(timeout_received); | |
| timeout_received = true; | |
| }, | |
| [&](const TwoIntMessage& message) { | |
| ASSERT_FALSE(two_int_received); | |
| two_int_received = true; | |
| ASSERT_EQ(message.value1, 1); | |
| ASSERT_EQ(message.value2, 2); | |
| }, | |
| [&](const StringMessage& message) { | |
| ASSERT_FALSE(string_received); | |
| string_received = true; | |
| ASSERT_EQ(message.message, "Hello, World!"); | |
| }); | |
| } | |
| } | |
| TEST_F(MessageQueueTest, SwitchReceiveAutoTest) { | |
| TestMessageQueue queue; | |
| queue.SendMessage(EmptyMessage{}); | |
| queue.SendMessage(IntMessage{42}); | |
| queue.SendMessage(OtherIntMessage{43}); | |
| queue.SendMessage(TwoIntMessage{1, 2}); | |
| queue.SendMessage(StringMessage{"Hello, World!"}); | |
| queue.SetTimeout(500); | |
| int pending_messages = 6; | |
| while (pending_messages > 0) { | |
| queue.SwitchReceive([&]([[maybe_unused]] auto message) { pending_messages--; }); | |
| } | |
| } | |
| TEST_F(MessageQueueTest, SwitchReceivePartialAutoTest) { | |
| TestMessageQueue queue; | |
| queue.SendMessage(EmptyMessage{}); | |
| queue.SendMessage(IntMessage{42}); | |
| queue.SendMessage(OtherIntMessage{43}); | |
| queue.SendMessage(TwoIntMessage{1, 2}); | |
| queue.SendMessage(StringMessage{"Hello, World!"}); | |
| queue.SetTimeout(500); | |
| bool running = true; | |
| while (running) { | |
| queue.SwitchReceive( | |
| [&](const StringMessage& message) { | |
| ASSERT_EQ(message.message, "Hello, World!"); | |
| running = false; | |
| }, | |
| [&]([[maybe_unused]] const auto& message) { | |
| const bool is_string{std::is_same<StringMessage, decltype(message)>()}; | |
| ASSERT_FALSE(is_string); | |
| }); | |
| } | |
| } | |
| TEST_F(MessageQueueTest, SwitchReceiveReturn) { | |
| TestMessageQueue queue; | |
| queue.SendMessage(EmptyMessage{}); | |
| ASSERT_TRUE( | |
| queue.SwitchReceive<bool>([&]([[maybe_unused]] const EmptyMessage& message) { return true; }, | |
| [&]([[maybe_unused]] const auto& message) { return false; })); | |
| queue.SendMessage(IntMessage{42}); | |
| ASSERT_FALSE( | |
| queue.SwitchReceive<bool>([&]([[maybe_unused]] const EmptyMessage& message) { return true; }, | |
| [&]([[maybe_unused]] const auto& message) { return false; })); | |
| } | |
| TEST_F(MessageQueueTest, ReceiveInOrder) { | |
| TestMessageQueue queue; | |
| std::vector<TestMessageQueue::Message> messages{ | |
| EmptyMessage{}, | |
| IntMessage{42}, | |
| OtherIntMessage{43}, | |
| TwoIntMessage{1, 2}, | |
| StringMessage{"Hello, World!"}, | |
| }; | |
| // Send the messages | |
| for (const auto& message : messages) { | |
| queue.SendMessage(message); | |
| } | |
| queue.SetTimeout(500); | |
| // Receive the messages. Make sure they came in order, except for the TimeoutExpiredMessage, which | |
| // can come at any time. | |
| bool received_timeout = false; | |
| size_t i = 0; | |
| while (i < messages.size()) { | |
| auto message = queue.ReceiveMessage(); | |
| if (std::holds_alternative<TimeoutExpiredMessage>(message)) { | |
| ASSERT_FALSE(received_timeout); | |
| received_timeout = true; | |
| } else { | |
| ASSERT_EQ(message.index(), messages[i].index()); | |
| i++; | |
| } | |
| } | |
| if (!received_timeout) { | |
| // If we have not received the timeout yet, receive one more message and make sure it's the | |
| // timeout. | |
| ASSERT_TRUE(std::holds_alternative<TimeoutExpiredMessage>(queue.ReceiveMessage())); | |
| } | |
| } | |
| } // namespace art |