| // 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 |