diff options
author | Jack He <siyuanh@google.com> | 2018-07-25 12:02:22 -0700 |
---|---|---|
committer | Jack He <siyuanh@google.com> | 2018-08-02 10:57:43 -0700 |
commit | 0ac5b1b6935f3df9e5aa0780c8d1fa1c8f807ca9 (patch) | |
tree | 7fca19177d80a012dc9717947304e478754269d2 /system/common/message_loop_thread_unittest.cc | |
parent | 4d024528da666402e41a4a69b92372a882292fb4 (diff) |
Add MessageLoopThread, ExecutionBarrier, and performance tests
* Add MessageLoopThread to abstract thread implementation to our code
* Add ExecutionBarrier utility class to help with thread synchronization
* Add more performance tests and performance benchmarks to evaluate
execution efficiency in both batch and sequential usages, for various
thread implementations including:
- libosi reactor on pthread
- MessageLoop on libosi reactor on pthread
- MessageLoop on STL std::thread
- MessageLoop on Posix pthread
- MessageLoop on libchrome base::Thread
- MessageLoop on MessageLoopThread
Bug: 110303473
Test: make, native and Java unit tests,
test/run_benchmarks.sh net_benchmark_thread_performance,
test/run_unit_tests.sh net_test_performance,
test/run_unit_tests.sh bluetooth_test_common,
testplans/details/184455/3975
Change-Id: I5b4ce2ee910a0f1d2edf95e0296916dea04d3f89
Diffstat (limited to 'system/common/message_loop_thread_unittest.cc')
-rw-r--r-- | system/common/message_loop_thread_unittest.cc | 259 |
1 files changed, 259 insertions, 0 deletions
diff --git a/system/common/message_loop_thread_unittest.cc b/system/common/message_loop_thread_unittest.cc new file mode 100644 index 0000000000..28f5d9e48c --- /dev/null +++ b/system/common/message_loop_thread_unittest.cc @@ -0,0 +1,259 @@ +/* + * Copyright 2018 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 <condition_variable> +#include <memory> +#include <mutex> + +#include <gtest/gtest.h> + +#include <base/bind.h> +#include <base/threading/platform_thread.h> +#include <base/tracked_objects.h> +#include <sys/capability.h> +#include <syscall.h> + +#include "execution_barrier.h" +#include "message_loop_thread.h" + +using bluetooth::common::ExecutionBarrier; +using bluetooth::common::MessageLoopThread; + +/** + * Unit tests to verify MessageLoopThread. Must have CAP_SYS_NICE capability. + */ +class MessageLoopThreadTest : public ::testing::Test { + public: + void ShouldNotHappen() { FAIL() << "Should not happen"; } + + void GetThreadId(base::PlatformThreadId* thread_id, + std::shared_ptr<ExecutionBarrier> execution_barrier) { + *thread_id = base::PlatformThread::CurrentId(); + execution_barrier->NotifyFinished(); + } + + void GetLinuxTid(pid_t* tid, + std::shared_ptr<ExecutionBarrier> execution_barrier) { + *tid = static_cast<pid_t>(syscall(SYS_gettid)); + execution_barrier->NotifyFinished(); + } + + void GetName(std::string* name, + std::shared_ptr<ExecutionBarrier> execution_barrier) { + char my_name[256]; + pthread_getname_np(pthread_self(), my_name, sizeof(my_name)); + name->append(my_name); + execution_barrier->NotifyFinished(); + } + + void GetSchedulingPolicyAndPriority( + int* scheduling_policy, int* schedule_priority, + std::shared_ptr<ExecutionBarrier> execution_barrier) { + *scheduling_policy = sched_getscheduler(0); + struct sched_param param = {}; + ASSERT_EQ(sched_getparam(0, ¶m), 0); + *schedule_priority = param.sched_priority; + execution_barrier->NotifyFinished(); + } + + protected: + static bool CanSetCurrentThreadPriority() { + struct __user_cap_header_struct linux_user_header = { + .version = _LINUX_CAPABILITY_VERSION_3}; + struct __user_cap_data_struct linux_user_data = {}; + if (capget(&linux_user_header, &linux_user_data) != 0) { + LOG(ERROR) << "Failed to get capability for current thread, error: " + << strerror(errno); + // Log record in XML + RecordProperty("MessageLoopThreadTestCannotGetCapabilityReason", + strerror(errno)); + return false; + } + return ((linux_user_data.permitted >> CAP_SYS_NICE) & 0x1) != 0; + } +}; + +TEST_F(MessageLoopThreadTest, test_running_thread) { + MessageLoopThread message_loop_thread("test_thread"); + message_loop_thread.StartUp(); + ASSERT_GE(message_loop_thread.GetThreadId(), 0); + ASSERT_TRUE(message_loop_thread.IsRunning()); + message_loop_thread.ShutDown(); + ASSERT_LT(message_loop_thread.GetThreadId(), 0); + ASSERT_FALSE(message_loop_thread.IsRunning()); +} + +TEST_F(MessageLoopThreadTest, test_not_self) { + MessageLoopThread message_loop_thread("test_thread"); + message_loop_thread.StartUp(); + ASSERT_GE(message_loop_thread.GetThreadId(), 0); + ASSERT_NE(message_loop_thread.GetThreadId(), + base::PlatformThread::CurrentId()); +} + +TEST_F(MessageLoopThreadTest, test_shutdown_without_start) { + MessageLoopThread message_loop_thread("test_thread"); + message_loop_thread.ShutDown(); + ASSERT_LT(message_loop_thread.GetThreadId(), 0); +} + +TEST_F(MessageLoopThreadTest, test_do_in_thread_before_start) { + std::string name = "test_thread"; + MessageLoopThread message_loop_thread(name); + ASSERT_FALSE(message_loop_thread.DoInThread( + FROM_HERE, base::Bind(&MessageLoopThreadTest::ShouldNotHappen, + base::Unretained(this)))); +} + +TEST_F(MessageLoopThreadTest, test_do_in_thread_after_shutdown) { + std::string name = "test_thread"; + MessageLoopThread message_loop_thread(name); + message_loop_thread.StartUp(); + message_loop_thread.ShutDown(); + ASSERT_FALSE(message_loop_thread.DoInThread( + FROM_HERE, base::Bind(&MessageLoopThreadTest::ShouldNotHappen, + base::Unretained(this)))); +} + +TEST_F(MessageLoopThreadTest, test_name) { + std::string name = "test_thread"; + MessageLoopThread message_loop_thread(name); + message_loop_thread.StartUp(); + ASSERT_GE(message_loop_thread.GetThreadId(), 0); + std::shared_ptr<ExecutionBarrier> execution_barrier = + std::make_shared<ExecutionBarrier>(); + std::string myName; + message_loop_thread.DoInThread( + FROM_HERE, + base::Bind(&MessageLoopThreadTest::GetName, base::Unretained(this), + &myName, execution_barrier)); + execution_barrier->WaitForExecution(); + ASSERT_EQ(name, myName); + ASSERT_EQ(name, message_loop_thread.GetName()); +} + +TEST_F(MessageLoopThreadTest, test_thread_id) { + std::string name = "test_thread"; + MessageLoopThread message_loop_thread(name); + message_loop_thread.StartUp(); + base::PlatformThreadId thread_id = message_loop_thread.GetThreadId(); + ASSERT_GE(thread_id, 0); + std::shared_ptr<ExecutionBarrier> execution_barrier = + std::make_shared<ExecutionBarrier>(); + base::PlatformThreadId my_thread_id; + message_loop_thread.DoInThread( + FROM_HERE, + base::Bind(&MessageLoopThreadTest::GetThreadId, base::Unretained(this), + &my_thread_id, execution_barrier)); + execution_barrier->WaitForExecution(); + ASSERT_EQ(thread_id, my_thread_id); +} + +TEST_F(MessageLoopThreadTest, test_set_realtime_priority_fail_before_start) { + std::string name = "test_thread"; + MessageLoopThread message_loop_thread(name); + ASSERT_FALSE(message_loop_thread.EnableRealTimeScheduling()); +} + +TEST_F(MessageLoopThreadTest, test_set_realtime_priority_success) { + std::string name = "test_thread"; + MessageLoopThread message_loop_thread(name); + message_loop_thread.StartUp(); + bool ret = message_loop_thread.EnableRealTimeScheduling(); + if (!ret) { + if (CanSetCurrentThreadPriority()) { + FAIL() << "Cannot set real time priority even though we have permission"; + } else { + LOG(WARNING) << "Allowing EnableRealTimeScheduling to fail because we" + " don't have CAP_SYS_NICE capability"; + // Log record in XML + RecordProperty("MessageLoopThreadTestConditionalSuccess", + "Mark test as success even though EnableRealTimeScheduling" + " failed because we don't have CAP_SYS_NICE capability"); + // Quit early since further verification is no longer needed + return; + } + } + std::shared_ptr<ExecutionBarrier> execution_barrier = + std::make_shared<ExecutionBarrier>(); + int scheduling_policy = -1; + int scheduling_priority = -1; + message_loop_thread.DoInThread( + FROM_HERE, + base::Bind(&MessageLoopThreadTest::GetSchedulingPolicyAndPriority, + base::Unretained(this), &scheduling_policy, + &scheduling_priority, execution_barrier)); + execution_barrier->WaitForExecution(); + ASSERT_EQ(scheduling_policy, SCHED_FIFO); + // Internal implementation verified here + ASSERT_EQ(scheduling_priority, 1); + execution_barrier = std::make_shared<ExecutionBarrier>(); + pid_t linux_tid = -1; + message_loop_thread.DoInThread( + FROM_HERE, + base::Bind(&MessageLoopThreadTest::GetLinuxTid, base::Unretained(this), + &linux_tid, execution_barrier)); + execution_barrier->WaitForExecution(); + ASSERT_GT(linux_tid, 0); + ASSERT_EQ(sched_getscheduler(linux_tid), SCHED_FIFO); + struct sched_param param = {}; + ASSERT_EQ(sched_getparam(linux_tid, ¶m), 0); + // Internal implementation verified here + ASSERT_EQ(param.sched_priority, 1); +} + +TEST_F(MessageLoopThreadTest, test_message_loop_null_before_start) { + std::string name = "test_thread"; + MessageLoopThread message_loop_thread(name); + ASSERT_EQ(message_loop_thread.message_loop(), nullptr); +} + +TEST_F(MessageLoopThreadTest, test_message_loop_not_null_start) { + std::string name = "test_thread"; + MessageLoopThread message_loop_thread(name); + message_loop_thread.StartUp(); + ASSERT_NE(message_loop_thread.message_loop(), nullptr); +} + +TEST_F(MessageLoopThreadTest, test_message_loop_null_after_stop) { + std::string name = "test_thread"; + MessageLoopThread message_loop_thread(name); + message_loop_thread.StartUp(); + ASSERT_NE(message_loop_thread.message_loop(), nullptr); + message_loop_thread.ShutDown(); + ASSERT_EQ(message_loop_thread.message_loop(), nullptr); +} + +TEST_F(MessageLoopThreadTest, test_to_string_method) { + std::string name = "test_thread"; + MessageLoopThread message_loop_thread(name); + std::string thread_string_before_start = message_loop_thread.ToString(); + ASSERT_FALSE(thread_string_before_start.empty()); + LOG(INFO) << "Before start: " << message_loop_thread; + message_loop_thread.StartUp(); + std::string thread_string_running = message_loop_thread.ToString(); + ASSERT_FALSE(thread_string_running.empty()); + LOG(INFO) << "Running: " << message_loop_thread; + // String representation should look different when thread is not running + ASSERT_STRNE(thread_string_running.c_str(), + thread_string_before_start.c_str()); + message_loop_thread.ShutDown(); + std::string thread_string_after_shutdown = message_loop_thread.ToString(); + LOG(INFO) << "After shutdown: " << message_loop_thread; + // String representation should look the same when thread is not running + ASSERT_STREQ(thread_string_after_shutdown.c_str(), + thread_string_before_start.c_str()); +}
\ No newline at end of file |