// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "flutter/fml/build_config.h" #include "flutter/fml/thread.h" #if defined(FML_OS_MACOSX) || defined(FML_OS_LINUX) || defined(FML_OS_ANDROID) #define FLUTTER_PTHREAD_SUPPORTED 1 #else #define FLUTTER_PTHREAD_SUPPORTED 0 #endif #if FLUTTER_PTHREAD_SUPPORTED #include #else #endif #if defined(FML_OS_WIN) #include #endif #include #include #include "gtest/gtest.h" TEST(Thread, CanStartAndEnd) { fml::Thread thread; ASSERT_TRUE(thread.GetTaskRunner()); } TEST(Thread, CanStartAndEndWithExplicitJoin) { fml::Thread thread; ASSERT_TRUE(thread.GetTaskRunner()); thread.Join(); } TEST(Thread, HasARunningMessageLoop) { fml::Thread thread; bool done = false; thread.GetTaskRunner()->PostTask([&done]() { done = true; }); thread.Join(); ASSERT_TRUE(done); } TEST(Thread, HasExpectedStackSize) { size_t stack_size = 0; fml::Thread thread; thread.GetTaskRunner()->PostTask([&stack_size]() { #if defined(FML_OS_WIN) ULONG_PTR low_limit; ULONG_PTR high_limit; GetCurrentThreadStackLimits(&low_limit, &high_limit); stack_size = high_limit - low_limit; #elif defined(FML_OS_MACOSX) stack_size = pthread_get_stacksize_np(pthread_self()); #else pthread_attr_t attr; pthread_getattr_np(pthread_self(), &attr); pthread_attr_getstacksize(&attr, &stack_size); pthread_attr_destroy(&attr); #endif }); thread.Join(); // Actual stack size will be aligned to page size, this assumes no supported // platform has a page size larger than 16k. On Linux reducing the default // stack size (8MB) does not seem to have any effect. const size_t kPageSize = 16384; ASSERT_TRUE(stack_size / kPageSize >= fml::Thread::GetDefaultStackSize() / kPageSize); } #if FLUTTER_PTHREAD_SUPPORTED TEST(Thread, ThreadNameCreatedWithConfig) { const std::string name = "Thread1"; fml::Thread thread(name); bool done = false; thread.GetTaskRunner()->PostTask([&done, &name]() { done = true; char thread_name[16]; pthread_t current_thread = pthread_self(); pthread_getname_np(current_thread, thread_name, 16); ASSERT_EQ(thread_name, name); }); thread.Join(); ASSERT_TRUE(done); } static int clamp_priority(int priority, int policy) { return std::clamp(priority, sched_get_priority_min(policy), sched_get_priority_max(policy)); } static void MockThreadConfigSetter(const fml::Thread::ThreadConfig& config) { // set thread name fml::Thread::SetCurrentThreadName(config); pthread_t tid = pthread_self(); struct sched_param param; int policy = SCHED_OTHER; switch (config.priority) { case fml::Thread::ThreadPriority::kDisplay: param.sched_priority = clamp_priority(10, policy); break; default: param.sched_priority = clamp_priority(1, policy); } pthread_setschedparam(tid, policy, ¶m); } TEST(Thread, ThreadPriorityCreatedWithConfig) { const std::string thread1_name = "Thread1"; const std::string thread2_name = "Thread2"; fml::Thread thread(MockThreadConfigSetter, fml::Thread::ThreadConfig( thread1_name, fml::Thread::ThreadPriority::kNormal)); bool done = false; struct sched_param param; int policy; thread.GetTaskRunner()->PostTask([&]() { done = true; char thread_name[16]; pthread_t current_thread = pthread_self(); pthread_getname_np(current_thread, thread_name, 16); pthread_getschedparam(current_thread, &policy, ¶m); ASSERT_EQ(thread_name, thread1_name); ASSERT_EQ(policy, SCHED_OTHER); ASSERT_EQ(param.sched_priority, clamp_priority(1, policy)); }); fml::Thread thread2(MockThreadConfigSetter, fml::Thread::ThreadConfig( thread2_name, fml::Thread::ThreadPriority::kDisplay)); thread2.GetTaskRunner()->PostTask([&]() { done = true; char thread_name[16]; pthread_t current_thread = pthread_self(); pthread_getname_np(current_thread, thread_name, 16); pthread_getschedparam(current_thread, &policy, ¶m); ASSERT_EQ(thread_name, thread2_name); ASSERT_EQ(policy, SCHED_OTHER); ASSERT_EQ(param.sched_priority, clamp_priority(10, policy)); }); thread.Join(); ASSERT_TRUE(done); } #endif // FLUTTER_PTHREAD_SUPPORTED #if defined(FML_OS_LINUX) TEST(Thread, LinuxLongThreadNameTruncated) { const std::string name = "VeryLongThreadNameTest"; fml::Thread thread(name); thread.GetTaskRunner()->PostTask([&name]() { constexpr size_t kThreadNameLen = 16; char thread_name[kThreadNameLen]; pthread_getname_np(pthread_self(), thread_name, kThreadNameLen); ASSERT_EQ(thread_name, name.substr(0, kThreadNameLen - 1)); }); thread.Join(); } #endif // FML_OS_LINUX