| // 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 <pthread.h> |
| #else |
| #endif |
| |
| #if defined(FML_OS_WIN) |
| #include <windows.h> |
| #endif |
| |
| #include <algorithm> |
| #include <memory> |
| #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 |