/* * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved. * * Use of this source code is governed by a BSD-style license * that can be found in the LICENSE file in the root of the source * tree. An additional intellectual property rights grant can be found * in the file PATENTS. All contributing project authors may * be found in the AUTHORS file in the root of the source tree. */ #include "rtc_base/platform_thread.h" #include #include #if !defined(WEBRTC_WIN) #include #endif #include "rtc_base/checks.h" namespace rtc { namespace { #if defined(WEBRTC_WIN) int Win32PriorityFromThreadPriority(ThreadPriority priority) { switch (priority) { case ThreadPriority::kLow: return THREAD_PRIORITY_BELOW_NORMAL; case ThreadPriority::kNormal: return THREAD_PRIORITY_NORMAL; case ThreadPriority::kHigh: return THREAD_PRIORITY_ABOVE_NORMAL; case ThreadPriority::kRealtime: return THREAD_PRIORITY_TIME_CRITICAL; } } #endif bool SetPriority(ThreadPriority priority) { #if defined(WEBRTC_WIN) return SetThreadPriority(GetCurrentThread(), Win32PriorityFromThreadPriority(priority)) != FALSE; #elif defined(__native_client__) || defined(WEBRTC_FUCHSIA) || \ (defined(__EMSCRIPTEN__) && !defined(__EMSCRIPTEN_PTHREADS__)) // Setting thread priorities is not supported in NaCl, Fuchsia or Emscripten // without pthreads. return true; #elif defined(WEBRTC_CHROMIUM_BUILD) && defined(WEBRTC_LINUX) // TODO(tommi): Switch to the same mechanism as Chromium uses for changing // thread priorities. return true; #else const int policy = SCHED_FIFO; const int min_prio = sched_get_priority_min(policy); const int max_prio = sched_get_priority_max(policy); if (min_prio == -1 || max_prio == -1) { return false; } if (max_prio - min_prio <= 2) return false; // Convert webrtc priority to system priorities: sched_param param; const int top_prio = max_prio - 1; const int low_prio = min_prio + 1; switch (priority) { case ThreadPriority::kLow: param.sched_priority = low_prio; break; case ThreadPriority::kNormal: // The -1 ensures that the kHighPriority is always greater or equal to // kNormalPriority. param.sched_priority = (low_prio + top_prio - 1) / 2; break; case ThreadPriority::kHigh: param.sched_priority = std::max(top_prio - 2, low_prio); break; case ThreadPriority::kRealtime: param.sched_priority = top_prio; break; } return pthread_setschedparam(pthread_self(), policy, ¶m) == 0; #endif // defined(WEBRTC_WIN) } #if defined(WEBRTC_WIN) DWORD WINAPI RunPlatformThread(void* param) { // The GetLastError() function only returns valid results when it is called // after a Win32 API function that returns a "failed" result. A crash dump // contains the result from GetLastError() and to make sure it does not // falsely report a Windows error we call SetLastError here. ::SetLastError(ERROR_SUCCESS); auto function = static_cast*>(param); (*function)(); delete function; return 0; } #else void* RunPlatformThread(void* param) { auto function = static_cast*>(param); (*function)(); delete function; return 0; } #endif // defined(WEBRTC_WIN) } // namespace PlatformThread::PlatformThread(Handle handle, bool joinable) : handle_(handle), joinable_(joinable) {} PlatformThread::PlatformThread(PlatformThread&& rhs) : handle_(rhs.handle_), joinable_(rhs.joinable_) { rhs.handle_ = absl::nullopt; } PlatformThread& PlatformThread::operator=(PlatformThread&& rhs) { Finalize(); handle_ = rhs.handle_; joinable_ = rhs.joinable_; rhs.handle_ = absl::nullopt; return *this; } PlatformThread::~PlatformThread() { Finalize(); } PlatformThread PlatformThread::SpawnJoinable( std::function thread_function, absl::string_view name, ThreadAttributes attributes) { return SpawnThread(std::move(thread_function), name, attributes, /*joinable=*/true); } PlatformThread PlatformThread::SpawnDetached( std::function thread_function, absl::string_view name, ThreadAttributes attributes) { return SpawnThread(std::move(thread_function), name, attributes, /*joinable=*/false); } absl::optional PlatformThread::GetHandle() const { return handle_; } #if defined(WEBRTC_WIN) bool PlatformThread::QueueAPC(PAPCFUNC function, ULONG_PTR data) { RTC_DCHECK(handle_.has_value()); return handle_.has_value() ? QueueUserAPC(function, *handle_, data) != FALSE : false; } #endif void PlatformThread::Finalize() { if (!handle_.has_value()) return; #if defined(WEBRTC_WIN) if (joinable_) WaitForSingleObject(*handle_, INFINITE); CloseHandle(*handle_); #else if (joinable_) RTC_CHECK_EQ(0, pthread_join(*handle_, nullptr)); #endif handle_ = absl::nullopt; } PlatformThread PlatformThread::SpawnThread( std::function thread_function, absl::string_view name, ThreadAttributes attributes, bool joinable) { RTC_DCHECK(thread_function); RTC_DCHECK(!name.empty()); // TODO(tommi): Consider lowering the limit to 15 (limit on Linux). RTC_DCHECK(name.length() < 64); auto start_thread_function_ptr = new std::function([thread_function = std::move(thread_function), name = std::string(name), attributes] { rtc::SetCurrentThreadName(name.c_str()); SetPriority(attributes.priority); thread_function(); }); #if defined(WEBRTC_WIN) // See bug 2902 for background on STACK_SIZE_PARAM_IS_A_RESERVATION. // Set the reserved stack stack size to 1M, which is the default on Windows // and Linux. DWORD thread_id = 0; PlatformThread::Handle handle = ::CreateThread( nullptr, 1024 * 1024, &RunPlatformThread, start_thread_function_ptr, STACK_SIZE_PARAM_IS_A_RESERVATION, &thread_id); RTC_CHECK(handle) << "CreateThread failed"; #else pthread_attr_t attr; pthread_attr_init(&attr); // Set the stack stack size to 1M. pthread_attr_setstacksize(&attr, 1024 * 1024); pthread_attr_setdetachstate( &attr, joinable ? PTHREAD_CREATE_JOINABLE : PTHREAD_CREATE_DETACHED); PlatformThread::Handle handle; RTC_CHECK_EQ(0, pthread_create(&handle, &attr, &RunPlatformThread, start_thread_function_ptr)); pthread_attr_destroy(&attr); #endif // defined(WEBRTC_WIN) return PlatformThread(handle, joinable); } } // namespace rtc