You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
254 lines
7.1 KiB
254 lines
7.1 KiB
4 months ago
|
/*
|
||
|
* 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"
|
||
|
|
||
|
namespace art {
|
||
|
|
||
|
class MessageQueueTest : public CommonRuntimeTest {};
|
||
|
|
||
|
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) {
|
||
|
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
|