|  | /* | 
|  | *  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 <algorithm> | 
|  | #include <functional> | 
|  | #include <optional> | 
|  | #include <string> | 
|  | #include <utility> | 
|  |  | 
|  | #include "absl/strings/string_view.h" | 
|  | #include "rtc_base/platform_thread_types.h" | 
|  |  | 
|  | #if !defined(WEBRTC_WIN) | 
|  | #include <sched.h> | 
|  | #endif | 
|  |  | 
|  | #include "rtc_base/checks.h" | 
|  |  | 
|  | namespace webrtc { | 
|  | 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<std::function<void()>*>(param); | 
|  | (*function)(); | 
|  | delete function; | 
|  | return 0; | 
|  | } | 
|  | #else | 
|  | void* RunPlatformThread(void* param) { | 
|  | auto function = static_cast<std::function<void()>*>(param); | 
|  | (*function)(); | 
|  | delete function; | 
|  | return nullptr; | 
|  | } | 
|  | #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_ = std::nullopt; | 
|  | } | 
|  |  | 
|  | PlatformThread& PlatformThread::operator=(PlatformThread&& rhs) { | 
|  | Finalize(); | 
|  | handle_ = rhs.handle_; | 
|  | joinable_ = rhs.joinable_; | 
|  | rhs.handle_ = std::nullopt; | 
|  | return *this; | 
|  | } | 
|  |  | 
|  | PlatformThread::~PlatformThread() { | 
|  | Finalize(); | 
|  | } | 
|  |  | 
|  | PlatformThread PlatformThread::SpawnJoinable( | 
|  | std::function<void()> thread_function, | 
|  | absl::string_view name, | 
|  | ThreadAttributes attributes) { | 
|  | return SpawnThread(std::move(thread_function), name, attributes, | 
|  | /*joinable=*/true); | 
|  | } | 
|  |  | 
|  | PlatformThread PlatformThread::SpawnDetached( | 
|  | std::function<void()> thread_function, | 
|  | absl::string_view name, | 
|  | ThreadAttributes attributes) { | 
|  | return SpawnThread(std::move(thread_function), name, attributes, | 
|  | /*joinable=*/false); | 
|  | } | 
|  |  | 
|  | std::optional<PlatformThread::Handle> 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_ = std::nullopt; | 
|  | } | 
|  |  | 
|  | PlatformThread PlatformThread::SpawnThread( | 
|  | std::function<void()> 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<void()>([thread_function = std::move(thread_function), | 
|  | name = std::string(name), attributes] { | 
|  | 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 webrtc |