blob: 09dbc3271da05092eea660f06b4b3703c3aa3fde [file] [log] [blame]
/*
* 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 {
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