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 20 namespace rtc { 21 22 PlatformThreadId 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 42 PlatformThreadRef CurrentThreadRef() { 43 #if defined(WEBRTC_WIN) 44 return GetCurrentThreadId(); 45 #elif defined(WEBRTC_POSIX) 46 return pthread_self(); 47 #endif 48 } 49 50 bool 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 58 void 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 { 80 #if defined(WEBRTC_WIN) 81 void CALLBACK RaiseFlag(ULONG_PTR param) { 82 *reinterpret_cast<bool*>(param) = true; 83 } 84 #else 85 struct ThreadAttributes { 86 ThreadAttributes() { pthread_attr_init(&attr); } 87 ~ThreadAttributes() { pthread_attr_destroy(&attr); } 88 pthread_attr_t* operator&() { return &attr; } 89 pthread_attr_t attr; 90 }; 91 #endif // defined(WEBRTC_WIN) 92 } 93 94 PlatformThread::PlatformThread(ThreadRunFunction func, 95 void* obj, 96 const char* thread_name) 97 : run_function_(func), 98 obj_(obj), 99 name_(thread_name ? thread_name : "webrtc"), 100 #if defined(WEBRTC_WIN) 101 stop_(false), 102 thread_(NULL) { 103 #else 104 stop_event_(false, false), 105 thread_(0) { 106 #endif // defined(WEBRTC_WIN) 107 RTC_DCHECK(func); 108 RTC_DCHECK(name_.length() < 64); 109 } 110 111 PlatformThread::~PlatformThread() { 112 RTC_DCHECK(thread_checker_.CalledOnValidThread()); 113 #if defined(WEBRTC_WIN) 114 RTC_DCHECK(!thread_); 115 #endif // defined(WEBRTC_WIN) 116 } 117 118 #if defined(WEBRTC_WIN) 119 DWORD WINAPI PlatformThread::StartThread(void* param) { 120 static_cast<PlatformThread*>(param)->Run(); 121 return 0; 122 } 123 #else 124 void* PlatformThread::StartThread(void* param) { 125 static_cast<PlatformThread*>(param)->Run(); 126 return 0; 127 } 128 #endif // defined(WEBRTC_WIN) 129 130 void PlatformThread::Start() { 131 RTC_DCHECK(thread_checker_.CalledOnValidThread()); 132 RTC_DCHECK(!thread_) << "Thread already started?"; 133 #if defined(WEBRTC_WIN) 134 stop_ = false; 135 136 // See bug 2902 for background on STACK_SIZE_PARAM_IS_A_RESERVATION. 137 // Set the reserved stack stack size to 1M, which is the default on Windows 138 // and Linux. 139 DWORD thread_id; 140 thread_ = ::CreateThread(NULL, 1024 * 1024, &StartThread, this, 141 STACK_SIZE_PARAM_IS_A_RESERVATION, &thread_id); 142 RTC_CHECK(thread_) << "CreateThread failed"; 143 #else 144 ThreadAttributes attr; 145 // Set the stack stack size to 1M. 146 pthread_attr_setstacksize(&attr, 1024 * 1024); 147 RTC_CHECK_EQ(0, pthread_create(&thread_, &attr, &StartThread, this)); 148 #endif // defined(WEBRTC_WIN) 149 } 150 151 bool PlatformThread::IsRunning() const { 152 RTC_DCHECK(thread_checker_.CalledOnValidThread()); 153 #if defined(WEBRTC_WIN) 154 return thread_ != nullptr; 155 #else 156 return thread_ != 0; 157 #endif // defined(WEBRTC_WIN) 158 } 159 160 void PlatformThread::Stop() { 161 RTC_DCHECK(thread_checker_.CalledOnValidThread()); 162 if (!IsRunning()) 163 return; 164 165 #if defined(WEBRTC_WIN) 166 // Set stop_ to |true| on the worker thread. 167 QueueUserAPC(&RaiseFlag, thread_, reinterpret_cast<ULONG_PTR>(&stop_)); 168 WaitForSingleObject(thread_, INFINITE); 169 CloseHandle(thread_); 170 thread_ = nullptr; 171 #else 172 stop_event_.Set(); 173 RTC_CHECK_EQ(0, pthread_join(thread_, nullptr)); 174 thread_ = 0; 175 #endif // defined(WEBRTC_WIN) 176 } 177 178 void PlatformThread::Run() { 179 if (!name_.empty()) 180 rtc::SetCurrentThreadName(name_.c_str()); 181 do { 182 // The interface contract of Start/Stop is that for a successfull call to 183 // Start, there should be at least one call to the run function. So we 184 // call the function before checking |stop_|. 185 if (!run_function_(obj_)) 186 break; 187 #if defined(WEBRTC_WIN) 188 // Alertable sleep to permit RaiseFlag to run and update |stop_|. 189 SleepEx(0, true); 190 } while (!stop_); 191 #else 192 } while (!stop_event_.Wait(0)); 193 #endif // defined(WEBRTC_WIN) 194 } 195 196 bool PlatformThread::SetPriority(ThreadPriority priority) { 197 RTC_DCHECK(thread_checker_.CalledOnValidThread()); 198 RTC_DCHECK(IsRunning()); 199 #if defined(WEBRTC_WIN) 200 return SetThreadPriority(thread_, priority) != FALSE; 201 #elif defined(__native_client__) 202 // Setting thread priorities is not supported in NaCl. 203 return true; 204 #elif defined(WEBRTC_CHROMIUM_BUILD) && defined(WEBRTC_LINUX) 205 // TODO(tommi): Switch to the same mechanism as Chromium uses for changing 206 // thread priorities. 207 return true; 208 #else 209 #ifdef WEBRTC_THREAD_RR 210 const int policy = SCHED_RR; 211 #else 212 const int policy = SCHED_FIFO; 213 #endif 214 const int min_prio = sched_get_priority_min(policy); 215 const int max_prio = sched_get_priority_max(policy); 216 if (min_prio == -1 || max_prio == -1) { 217 return false; 218 } 219 220 if (max_prio - min_prio <= 2) 221 return false; 222 223 // Convert webrtc priority to system priorities: 224 sched_param param; 225 const int top_prio = max_prio - 1; 226 const int low_prio = min_prio + 1; 227 switch (priority) { 228 case kLowPriority: 229 param.sched_priority = low_prio; 230 break; 231 case kNormalPriority: 232 // The -1 ensures that the kHighPriority is always greater or equal to 233 // kNormalPriority. 234 param.sched_priority = (low_prio + top_prio - 1) / 2; 235 break; 236 case kHighPriority: 237 param.sched_priority = std::max(top_prio - 2, low_prio); 238 break; 239 case kHighestPriority: 240 param.sched_priority = std::max(top_prio - 1, low_prio); 241 break; 242 case kRealtimePriority: 243 param.sched_priority = top_prio; 244 break; 245 } 246 return pthread_setschedparam(thread_, policy, ¶m) == 0; 247 #endif // defined(WEBRTC_WIN) 248 } 249 250 } // namespace rtc 251