platform_thread.cc revision 7d842d660ef5e053da0305bc1d9405fb070b05dc
1/* 2 * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved. 3 * 4 * Use of this source code is governed by a BSD-style license 5 * that can be found in the LICENSE file in the root of the source 6 * tree. An additional intellectual property rights grant can be found 7 * in the file PATENTS. All contributing project authors may 8 * be found in the AUTHORS file in the root of the source tree. 9 */ 10 11#include "webrtc/base/platform_thread.h" 12 13#include "webrtc/base/checks.h" 14 15#if defined(WEBRTC_LINUX) 16#include <sys/prctl.h> 17#include <sys/syscall.h> 18#endif 19 20namespace rtc { 21 22PlatformThreadId CurrentThreadId() { 23 PlatformThreadId ret; 24#if defined(WEBRTC_WIN) 25 ret = GetCurrentThreadId(); 26#elif defined(WEBRTC_POSIX) 27#if defined(WEBRTC_MAC) || defined(WEBRTC_IOS) 28 ret = pthread_mach_thread_np(pthread_self()); 29#elif defined(WEBRTC_LINUX) 30 ret = syscall(__NR_gettid); 31#elif defined(WEBRTC_ANDROID) 32 ret = gettid(); 33#else 34 // Default implementation for nacl and solaris. 35 ret = reinterpret_cast<pid_t>(pthread_self()); 36#endif 37#endif // defined(WEBRTC_POSIX) 38 RTC_DCHECK(ret); 39 return ret; 40} 41 42PlatformThreadRef CurrentThreadRef() { 43#if defined(WEBRTC_WIN) 44 return GetCurrentThreadId(); 45#elif defined(WEBRTC_POSIX) 46 return pthread_self(); 47#endif 48} 49 50bool IsThreadRefEqual(const PlatformThreadRef& a, const PlatformThreadRef& b) { 51#if defined(WEBRTC_WIN) 52 return a == b; 53#elif defined(WEBRTC_POSIX) 54 return pthread_equal(a, b); 55#endif 56} 57 58void SetCurrentThreadName(const char* name) { 59#if defined(WEBRTC_WIN) 60 struct { 61 DWORD dwType; 62 LPCSTR szName; 63 DWORD dwThreadID; 64 DWORD dwFlags; 65 } threadname_info = {0x1000, name, static_cast<DWORD>(-1), 0}; 66 67 __try { 68 ::RaiseException(0x406D1388, 0, sizeof(threadname_info) / sizeof(DWORD), 69 reinterpret_cast<ULONG_PTR*>(&threadname_info)); 70 } __except (EXCEPTION_EXECUTE_HANDLER) { 71 } 72#elif defined(WEBRTC_LINUX) || defined(WEBRTC_ANDROID) 73 prctl(PR_SET_NAME, reinterpret_cast<unsigned long>(name)); 74#elif defined(WEBRTC_MAC) || defined(WEBRTC_IOS) 75 pthread_setname_np(name); 76#endif 77} 78 79} // namespace rtc 80 81namespace webrtc { 82 83rtc::scoped_ptr<PlatformThread> PlatformThread::CreateThread( 84 ThreadRunFunction func, 85 void* obj, 86 const char* thread_name) { 87 return rtc::scoped_ptr<PlatformThread>( 88 new PlatformThread(func, obj, thread_name)); 89} 90 91namespace { 92#if defined(WEBRTC_WIN) 93void CALLBACK RaiseFlag(ULONG_PTR param) { 94 *reinterpret_cast<bool*>(param) = true; 95} 96#else 97struct ThreadAttributes { 98 ThreadAttributes() { pthread_attr_init(&attr); } 99 ~ThreadAttributes() { pthread_attr_destroy(&attr); } 100 pthread_attr_t* operator&() { return &attr; } 101 pthread_attr_t attr; 102}; 103#endif // defined(WEBRTC_WIN) 104} 105 106PlatformThread::PlatformThread(ThreadRunFunction func, 107 void* obj, 108 const char* thread_name) 109 : run_function_(func), 110 obj_(obj), 111 name_(thread_name ? thread_name : "webrtc"), 112#if defined(WEBRTC_WIN) 113 stop_(false), 114 thread_(NULL) { 115#else 116 stop_event_(false, false), 117 thread_(0) { 118#endif // defined(WEBRTC_WIN) 119 RTC_DCHECK(func); 120 RTC_DCHECK(name_.length() < 64); 121} 122 123PlatformThread::~PlatformThread() { 124 RTC_DCHECK(thread_checker_.CalledOnValidThread()); 125#if defined(WEBRTC_WIN) 126 RTC_DCHECK(!thread_); 127#endif // defined(WEBRTC_WIN) 128} 129 130#if defined(WEBRTC_WIN) 131DWORD WINAPI PlatformThread::StartThread(void* param) { 132 static_cast<PlatformThread*>(param)->Run(); 133 return 0; 134} 135#else 136void* PlatformThread::StartThread(void* param) { 137 static_cast<PlatformThread*>(param)->Run(); 138 return 0; 139} 140#endif // defined(WEBRTC_WIN) 141 142bool PlatformThread::Start() { 143 RTC_DCHECK(thread_checker_.CalledOnValidThread()); 144 RTC_DCHECK(!thread_) << "Thread already started?"; 145#if defined(WEBRTC_WIN) 146 stop_ = false; 147 148 // See bug 2902 for background on STACK_SIZE_PARAM_IS_A_RESERVATION. 149 // Set the reserved stack stack size to 1M, which is the default on Windows 150 // and Linux. 151 DWORD thread_id; 152 thread_ = ::CreateThread(NULL, 1024 * 1024, &StartThread, this, 153 STACK_SIZE_PARAM_IS_A_RESERVATION, &thread_id); 154 RTC_CHECK(thread_) << "CreateThread failed"; 155#else 156 ThreadAttributes attr; 157 // Set the stack stack size to 1M. 158 pthread_attr_setstacksize(&attr, 1024 * 1024); 159 RTC_CHECK_EQ(0, pthread_create(&thread_, &attr, &StartThread, this)); 160#endif // defined(WEBRTC_WIN) 161 return true; 162} 163 164bool PlatformThread::Stop() { 165 RTC_DCHECK(thread_checker_.CalledOnValidThread()); 166#if defined(WEBRTC_WIN) 167 if (thread_) { 168 // Set stop_ to |true| on the worker thread. 169 QueueUserAPC(&RaiseFlag, thread_, reinterpret_cast<ULONG_PTR>(&stop_)); 170 WaitForSingleObject(thread_, INFINITE); 171 CloseHandle(thread_); 172 thread_ = nullptr; 173 } 174#else 175 if (!thread_) 176 return true; 177 178 stop_event_.Set(); 179 RTC_CHECK_EQ(0, pthread_join(thread_, nullptr)); 180 thread_ = 0; 181#endif // defined(WEBRTC_WIN) 182 return true; 183} 184 185void PlatformThread::Run() { 186 if (!name_.empty()) 187 rtc::SetCurrentThreadName(name_.c_str()); 188 do { 189 // The interface contract of Start/Stop is that for a successfull call to 190 // Start, there should be at least one call to the run function. So we 191 // call the function before checking |stop_|. 192 if (!run_function_(obj_)) 193 break; 194#if defined(WEBRTC_WIN) 195 // Alertable sleep to permit RaiseFlag to run and update |stop_|. 196 SleepEx(0, true); 197 } while (!stop_); 198#else 199 } while (!stop_event_.Wait(0)); 200#endif // defined(WEBRTC_WIN) 201} 202 203bool PlatformThread::SetPriority(ThreadPriority priority) { 204 RTC_DCHECK(thread_checker_.CalledOnValidThread()); 205#if defined(WEBRTC_WIN) 206 return thread_ && SetThreadPriority(thread_, priority); 207#elif defined(__native_client__) 208 // Setting thread priorities is not supported in NaCl. 209 return true; 210#elif defined(WEBRTC_CHROMIUM_BUILD) && defined(WEBRTC_LINUX) 211 // TODO(tommi): Switch to the same mechanism as Chromium uses for changing 212 // thread priorities. 213 return true; 214#else 215 if (!thread_) 216 return false; 217#ifdef WEBRTC_THREAD_RR 218 const int policy = SCHED_RR; 219#else 220 const int policy = SCHED_FIFO; 221#endif 222 const int min_prio = sched_get_priority_min(policy); 223 const int max_prio = sched_get_priority_max(policy); 224 if (min_prio == -1 || max_prio == -1) { 225 return false; 226 } 227 228 if (max_prio - min_prio <= 2) 229 return false; 230 231 // Convert webrtc priority to system priorities: 232 sched_param param; 233 const int top_prio = max_prio - 1; 234 const int low_prio = min_prio + 1; 235 switch (priority) { 236 case kLowPriority: 237 param.sched_priority = low_prio; 238 break; 239 case kNormalPriority: 240 // The -1 ensures that the kHighPriority is always greater or equal to 241 // kNormalPriority. 242 param.sched_priority = (low_prio + top_prio - 1) / 2; 243 break; 244 case kHighPriority: 245 param.sched_priority = std::max(top_prio - 2, low_prio); 246 break; 247 case kHighestPriority: 248 param.sched_priority = std::max(top_prio - 1, low_prio); 249 break; 250 case kRealtimePriority: 251 param.sched_priority = top_prio; 252 break; 253 } 254 return pthread_setschedparam(thread_, policy, ¶m) == 0; 255#endif // defined(WEBRTC_WIN) 256} 257 258} // namespace webrtc 259