| // 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. |
| |
| #define FML_USED_ON_EMBEDDER |
| |
| #include "flutter/fml/thread.h" |
| |
| #include <memory> |
| #include <string> |
| #include <utility> |
| |
| #include "flutter/fml/build_config.h" |
| #include "flutter/fml/message_loop.h" |
| #include "flutter/fml/synchronization/waitable_event.h" |
| |
| #if defined(FML_OS_WIN) |
| #include <windows.h> |
| #elif defined(OS_FUCHSIA) |
| #include <lib/zx/thread.h> |
| #else |
| #include <pthread.h> |
| #endif |
| |
| namespace fml { |
| |
| typedef std::function<void()> ThreadFunction; |
| |
| class ThreadHandle { |
| public: |
| explicit ThreadHandle(ThreadFunction&& function); |
| ~ThreadHandle(); |
| |
| void Join(); |
| |
| private: |
| #if defined(FML_OS_WIN) |
| HANDLE thread_; |
| #else |
| pthread_t thread_; |
| #endif |
| }; |
| |
| #if defined(FML_OS_WIN) |
| ThreadHandle::ThreadHandle(ThreadFunction&& function) { |
| thread_ = (HANDLE*)_beginthreadex( |
| nullptr, Thread::GetDefaultStackSize(), |
| [](void* arg) -> unsigned { |
| std::unique_ptr<ThreadFunction> function( |
| reinterpret_cast<ThreadFunction*>(arg)); |
| (*function)(); |
| return 0; |
| }, |
| new ThreadFunction(std::move(function)), 0, nullptr); |
| FML_CHECK(thread_ != nullptr); |
| } |
| |
| void ThreadHandle::Join() { |
| WaitForSingleObjectEx(thread_, INFINITE, FALSE); |
| } |
| |
| ThreadHandle::~ThreadHandle() { |
| CloseHandle(thread_); |
| } |
| #else |
| ThreadHandle::ThreadHandle(ThreadFunction&& function) { |
| pthread_attr_t attr; |
| pthread_attr_init(&attr); |
| int result = pthread_attr_setstacksize(&attr, Thread::GetDefaultStackSize()); |
| FML_CHECK(result == 0); |
| result = pthread_create( |
| &thread_, &attr, |
| [](void* arg) -> void* { |
| std::unique_ptr<ThreadFunction> function( |
| reinterpret_cast<ThreadFunction*>(arg)); |
| (*function)(); |
| return nullptr; |
| }, |
| new ThreadFunction(std::move(function))); |
| FML_CHECK(result == 0); |
| result = pthread_attr_destroy(&attr); |
| FML_CHECK(result == 0); |
| } |
| |
| void ThreadHandle::Join() { |
| pthread_join(thread_, nullptr); |
| } |
| |
| ThreadHandle::~ThreadHandle() {} |
| #endif |
| |
| #if defined(FML_OS_WIN) |
| // The information on how to set the thread name comes from |
| // a MSDN article: http://msdn2.microsoft.com/en-us/library/xcb2z8hs.aspx |
| const DWORD kVCThreadNameException = 0x406D1388; |
| typedef struct tagTHREADNAME_INFO { |
| DWORD dwType; // Must be 0x1000. |
| LPCSTR szName; // Pointer to name (in user addr space). |
| DWORD dwThreadID; // Thread ID (-1=caller thread). |
| DWORD dwFlags; // Reserved for future use, must be zero. |
| } THREADNAME_INFO; |
| #endif |
| |
| void SetThreadName(const std::string& name) { |
| if (name == "") { |
| return; |
| } |
| #if defined(FML_OS_MACOSX) |
| pthread_setname_np(name.c_str()); |
| #elif defined(FML_OS_LINUX) || defined(FML_OS_ANDROID) |
| // Linux thread names are limited to 16 characters including the terminating |
| // null. |
| constexpr std::string::size_type kLinuxMaxThreadNameLen = 15; |
| pthread_setname_np(pthread_self(), |
| name.substr(0, kLinuxMaxThreadNameLen).c_str()); |
| #elif defined(FML_OS_WIN) |
| THREADNAME_INFO info; |
| info.dwType = 0x1000; |
| info.szName = name.c_str(); |
| info.dwThreadID = GetCurrentThreadId(); |
| info.dwFlags = 0; |
| __try { |
| RaiseException(kVCThreadNameException, 0, sizeof(info) / sizeof(DWORD), |
| reinterpret_cast<DWORD_PTR*>(&info)); |
| } __except (EXCEPTION_CONTINUE_EXECUTION) { |
| } |
| #elif defined(OS_FUCHSIA) |
| zx::thread::self()->set_property(ZX_PROP_NAME, name.c_str(), name.size()); |
| #else |
| FML_DLOG(INFO) << "Could not set the thread name to '" << name |
| << "' on this platform."; |
| #endif |
| } |
| |
| void Thread::SetCurrentThreadName(const Thread::ThreadConfig& config) { |
| SetThreadName(config.name); |
| } |
| |
| Thread::Thread(const std::string& name) |
| : Thread(Thread::SetCurrentThreadName, ThreadConfig(name)) {} |
| |
| Thread::Thread(const ThreadConfigSetter& setter, const ThreadConfig& config) |
| : joined_(false) { |
| fml::AutoResetWaitableEvent latch; |
| fml::RefPtr<fml::TaskRunner> runner; |
| |
| thread_ = std::make_unique<ThreadHandle>( |
| [&latch, &runner, setter, config]() -> void { |
| setter(config); |
| fml::MessageLoop::EnsureInitializedForCurrentThread(); |
| auto& loop = MessageLoop::GetCurrent(); |
| runner = loop.GetTaskRunner(); |
| latch.Signal(); |
| loop.Run(); |
| }); |
| latch.Wait(); |
| task_runner_ = runner; |
| } |
| |
| Thread::~Thread() { |
| Join(); |
| } |
| |
| fml::RefPtr<fml::TaskRunner> Thread::GetTaskRunner() const { |
| return task_runner_; |
| } |
| |
| void Thread::Join() { |
| if (joined_) { |
| return; |
| } |
| joined_ = true; |
| task_runner_->PostTask([]() { MessageLoop::GetCurrent().Terminate(); }); |
| thread_->Join(); |
| } |
| |
| size_t Thread::GetDefaultStackSize() { |
| return 1024 * 1024 * 2; |
| } |
| |
| } // namespace fml |