| /* | |
| * 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 <memory> | |
| #if !defined(WEBRTC_WIN) | |
| #include <sched.h> | |
| #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) | |
| // Setting thread priorities is not supported in NaCl or Fuchsia. | |
| 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 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<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); | |
| } | |
| absl::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_ = absl::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] { | |
| 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 |