| OLD | NEW |
| (Empty) |
| 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/atomicops.h" | |
| 14 #include "webrtc/base/checks.h" | |
| 15 #include "webrtc/base/timeutils.h" | |
| 16 #include "webrtc/base/trace_event.h" | |
| 17 | |
| 18 #if defined(WEBRTC_LINUX) | |
| 19 #include <sys/prctl.h> | |
| 20 #include <sys/syscall.h> | |
| 21 #endif | |
| 22 | |
| 23 namespace rtc { | |
| 24 | |
| 25 PlatformThreadId CurrentThreadId() { | |
| 26 PlatformThreadId ret; | |
| 27 #if defined(WEBRTC_WIN) | |
| 28 ret = GetCurrentThreadId(); | |
| 29 #elif defined(WEBRTC_POSIX) | |
| 30 #if defined(WEBRTC_MAC) || defined(WEBRTC_IOS) | |
| 31 ret = pthread_mach_thread_np(pthread_self()); | |
| 32 #elif defined(WEBRTC_LINUX) | |
| 33 ret = syscall(__NR_gettid); | |
| 34 #elif defined(WEBRTC_ANDROID) | |
| 35 ret = gettid(); | |
| 36 #else | |
| 37 // Default implementation for nacl and solaris. | |
| 38 ret = reinterpret_cast<pid_t>(pthread_self()); | |
| 39 #endif | |
| 40 #endif // defined(WEBRTC_POSIX) | |
| 41 RTC_DCHECK(ret); | |
| 42 return ret; | |
| 43 } | |
| 44 | |
| 45 PlatformThreadRef CurrentThreadRef() { | |
| 46 #if defined(WEBRTC_WIN) | |
| 47 return GetCurrentThreadId(); | |
| 48 #elif defined(WEBRTC_POSIX) | |
| 49 return pthread_self(); | |
| 50 #endif | |
| 51 } | |
| 52 | |
| 53 bool IsThreadRefEqual(const PlatformThreadRef& a, const PlatformThreadRef& b) { | |
| 54 #if defined(WEBRTC_WIN) | |
| 55 return a == b; | |
| 56 #elif defined(WEBRTC_POSIX) | |
| 57 return pthread_equal(a, b); | |
| 58 #endif | |
| 59 } | |
| 60 | |
| 61 void SetCurrentThreadName(const char* name) { | |
| 62 #if defined(WEBRTC_WIN) | |
| 63 struct { | |
| 64 DWORD dwType; | |
| 65 LPCSTR szName; | |
| 66 DWORD dwThreadID; | |
| 67 DWORD dwFlags; | |
| 68 } threadname_info = {0x1000, name, static_cast<DWORD>(-1), 0}; | |
| 69 | |
| 70 __try { | |
| 71 ::RaiseException(0x406D1388, 0, sizeof(threadname_info) / sizeof(DWORD), | |
| 72 reinterpret_cast<ULONG_PTR*>(&threadname_info)); | |
| 73 } __except (EXCEPTION_EXECUTE_HANDLER) { | |
| 74 } | |
| 75 #elif defined(WEBRTC_LINUX) || defined(WEBRTC_ANDROID) | |
| 76 prctl(PR_SET_NAME, reinterpret_cast<unsigned long>(name)); | |
| 77 #elif defined(WEBRTC_MAC) || defined(WEBRTC_IOS) | |
| 78 pthread_setname_np(name); | |
| 79 #endif | |
| 80 } | |
| 81 | |
| 82 namespace { | |
| 83 #if defined(WEBRTC_WIN) | |
| 84 void CALLBACK RaiseFlag(ULONG_PTR param) { | |
| 85 *reinterpret_cast<bool*>(param) = true; | |
| 86 } | |
| 87 #else | |
| 88 struct ThreadAttributes { | |
| 89 ThreadAttributes() { pthread_attr_init(&attr); } | |
| 90 ~ThreadAttributes() { pthread_attr_destroy(&attr); } | |
| 91 pthread_attr_t* operator&() { return &attr; } | |
| 92 pthread_attr_t attr; | |
| 93 }; | |
| 94 #endif // defined(WEBRTC_WIN) | |
| 95 } | |
| 96 | |
| 97 PlatformThread::PlatformThread(ThreadRunFunctionDeprecated func, | |
| 98 void* obj, | |
| 99 const char* thread_name) | |
| 100 : run_function_deprecated_(func), | |
| 101 obj_(obj), | |
| 102 name_(thread_name ? thread_name : "webrtc") { | |
| 103 RTC_DCHECK(func); | |
| 104 RTC_DCHECK(name_.length() < 64); | |
| 105 spawned_thread_checker_.DetachFromThread(); | |
| 106 } | |
| 107 | |
| 108 PlatformThread::PlatformThread(ThreadRunFunction func, | |
| 109 void* obj, | |
| 110 const char* thread_name, | |
| 111 ThreadPriority priority /*= kNormalPriority*/) | |
| 112 : run_function_(func), priority_(priority), obj_(obj), name_(thread_name) { | |
| 113 RTC_DCHECK(func); | |
| 114 RTC_DCHECK(!name_.empty()); | |
| 115 // TODO(tommi): Consider lowering the limit to 15 (limit on Linux). | |
| 116 RTC_DCHECK(name_.length() < 64); | |
| 117 spawned_thread_checker_.DetachFromThread(); | |
| 118 } | |
| 119 | |
| 120 PlatformThread::~PlatformThread() { | |
| 121 RTC_DCHECK(thread_checker_.CalledOnValidThread()); | |
| 122 #if defined(WEBRTC_WIN) | |
| 123 RTC_DCHECK(!thread_); | |
| 124 RTC_DCHECK(!thread_id_); | |
| 125 #endif // defined(WEBRTC_WIN) | |
| 126 } | |
| 127 | |
| 128 #if defined(WEBRTC_WIN) | |
| 129 DWORD WINAPI PlatformThread::StartThread(void* param) { | |
| 130 // The GetLastError() function only returns valid results when it is called | |
| 131 // after a Win32 API function that returns a "failed" result. A crash dump | |
| 132 // contains the result from GetLastError() and to make sure it does not | |
| 133 // falsely report a Windows error we call SetLastError here. | |
| 134 ::SetLastError(ERROR_SUCCESS); | |
| 135 static_cast<PlatformThread*>(param)->Run(); | |
| 136 return 0; | |
| 137 } | |
| 138 #else | |
| 139 void* PlatformThread::StartThread(void* param) { | |
| 140 static_cast<PlatformThread*>(param)->Run(); | |
| 141 return 0; | |
| 142 } | |
| 143 #endif // defined(WEBRTC_WIN) | |
| 144 | |
| 145 void PlatformThread::Start() { | |
| 146 RTC_DCHECK(thread_checker_.CalledOnValidThread()); | |
| 147 RTC_DCHECK(!thread_) << "Thread already started?"; | |
| 148 #if defined(WEBRTC_WIN) | |
| 149 stop_ = false; | |
| 150 | |
| 151 // See bug 2902 for background on STACK_SIZE_PARAM_IS_A_RESERVATION. | |
| 152 // Set the reserved stack stack size to 1M, which is the default on Windows | |
| 153 // and Linux. | |
| 154 thread_ = ::CreateThread(nullptr, 1024 * 1024, &StartThread, this, | |
| 155 STACK_SIZE_PARAM_IS_A_RESERVATION, &thread_id_); | |
| 156 RTC_CHECK(thread_) << "CreateThread failed"; | |
| 157 RTC_DCHECK(thread_id_); | |
| 158 #else | |
| 159 ThreadAttributes attr; | |
| 160 // Set the stack stack size to 1M. | |
| 161 pthread_attr_setstacksize(&attr, 1024 * 1024); | |
| 162 RTC_CHECK_EQ(0, pthread_create(&thread_, &attr, &StartThread, this)); | |
| 163 #endif // defined(WEBRTC_WIN) | |
| 164 } | |
| 165 | |
| 166 bool PlatformThread::IsRunning() const { | |
| 167 RTC_DCHECK(thread_checker_.CalledOnValidThread()); | |
| 168 #if defined(WEBRTC_WIN) | |
| 169 return thread_ != nullptr; | |
| 170 #else | |
| 171 return thread_ != 0; | |
| 172 #endif // defined(WEBRTC_WIN) | |
| 173 } | |
| 174 | |
| 175 PlatformThreadRef PlatformThread::GetThreadRef() const { | |
| 176 #if defined(WEBRTC_WIN) | |
| 177 return thread_id_; | |
| 178 #else | |
| 179 return thread_; | |
| 180 #endif // defined(WEBRTC_WIN) | |
| 181 } | |
| 182 | |
| 183 void PlatformThread::Stop() { | |
| 184 RTC_DCHECK(thread_checker_.CalledOnValidThread()); | |
| 185 if (!IsRunning()) | |
| 186 return; | |
| 187 | |
| 188 #if defined(WEBRTC_WIN) | |
| 189 // Set stop_ to |true| on the worker thread. | |
| 190 bool queued = QueueAPC(&RaiseFlag, reinterpret_cast<ULONG_PTR>(&stop_)); | |
| 191 // Queuing the APC can fail if the thread is being terminated. | |
| 192 RTC_CHECK(queued || GetLastError() == ERROR_GEN_FAILURE); | |
| 193 WaitForSingleObject(thread_, INFINITE); | |
| 194 CloseHandle(thread_); | |
| 195 thread_ = nullptr; | |
| 196 thread_id_ = 0; | |
| 197 #else | |
| 198 if (!run_function_) | |
| 199 RTC_CHECK_EQ(1, AtomicOps::Increment(&stop_flag_)); | |
| 200 RTC_CHECK_EQ(0, pthread_join(thread_, nullptr)); | |
| 201 if (!run_function_) | |
| 202 AtomicOps::ReleaseStore(&stop_flag_, 0); | |
| 203 thread_ = 0; | |
| 204 #endif // defined(WEBRTC_WIN) | |
| 205 spawned_thread_checker_.DetachFromThread(); | |
| 206 } | |
| 207 | |
| 208 // TODO(tommi): Deprecate the loop behavior in PlatformThread. | |
| 209 // * Introduce a new callback type that returns void. | |
| 210 // * Remove potential for a busy loop in PlatformThread. | |
| 211 // * Delegate the responsibility for how to stop the thread, to the | |
| 212 // implementation that actually uses the thread. | |
| 213 // All implementations will need to be aware of how the thread should be stopped | |
| 214 // and encouraging a busy polling loop, can be costly in terms of power and cpu. | |
| 215 void PlatformThread::Run() { | |
| 216 // Attach the worker thread checker to this thread. | |
| 217 RTC_DCHECK(spawned_thread_checker_.CalledOnValidThread()); | |
| 218 rtc::SetCurrentThreadName(name_.c_str()); | |
| 219 | |
| 220 if (run_function_) { | |
| 221 SetPriority(priority_); | |
| 222 run_function_(obj_); | |
| 223 return; | |
| 224 } | |
| 225 | |
| 226 // TODO(tommi): Delete the rest of this function when looping isn't supported. | |
| 227 #if RTC_DCHECK_IS_ON | |
| 228 // These constants control the busy loop detection algorithm below. | |
| 229 // |kMaxLoopCount| controls the limit for how many times we allow the loop | |
| 230 // to run within a period, before DCHECKing. | |
| 231 // |kPeriodToMeasureMs| controls how long that period is. | |
| 232 static const int kMaxLoopCount = 1000; | |
| 233 static const int kPeriodToMeasureMs = 100; | |
| 234 int64_t loop_stamps[kMaxLoopCount] = {}; | |
| 235 int64_t sequence_nr = 0; | |
| 236 #endif | |
| 237 | |
| 238 do { | |
| 239 TRACE_EVENT1("webrtc", "PlatformThread::Run", "name", name_.c_str()); | |
| 240 | |
| 241 // The interface contract of Start/Stop is that for a successful call to | |
| 242 // Start, there should be at least one call to the run function. So we | |
| 243 // call the function before checking |stop_|. | |
| 244 if (!run_function_deprecated_(obj_)) | |
| 245 break; | |
| 246 #if RTC_DCHECK_IS_ON | |
| 247 auto id = sequence_nr % kMaxLoopCount; | |
| 248 loop_stamps[id] = rtc::TimeMillis(); | |
| 249 if (sequence_nr > kMaxLoopCount) { | |
| 250 auto compare_id = (id + 1) % kMaxLoopCount; | |
| 251 auto diff = loop_stamps[id] - loop_stamps[compare_id]; | |
| 252 RTC_DCHECK_GE(diff, 0); | |
| 253 if (diff < kPeriodToMeasureMs) { | |
| 254 RTC_NOTREACHED() << "This thread is too busy: " << name_ << " " << diff | |
| 255 << "ms sequence=" << sequence_nr << " " | |
| 256 << loop_stamps[id] << " vs " << loop_stamps[compare_id] | |
| 257 << ", " << id << " vs " << compare_id; | |
| 258 } | |
| 259 } | |
| 260 ++sequence_nr; | |
| 261 #endif | |
| 262 #if defined(WEBRTC_WIN) | |
| 263 // Alertable sleep to permit RaiseFlag to run and update |stop_|. | |
| 264 SleepEx(0, true); | |
| 265 } while (!stop_); | |
| 266 #else | |
| 267 #if defined(WEBRTC_MAC) | |
| 268 sched_yield(); | |
| 269 #else | |
| 270 static const struct timespec ts_null = {0}; | |
| 271 nanosleep(&ts_null, nullptr); | |
| 272 #endif | |
| 273 } while (!AtomicOps::AcquireLoad(&stop_flag_)); | |
| 274 #endif // defined(WEBRTC_WIN) | |
| 275 } | |
| 276 | |
| 277 bool PlatformThread::SetPriority(ThreadPriority priority) { | |
| 278 #if RTC_DCHECK_IS_ON | |
| 279 if (run_function_) { | |
| 280 // The non-deprecated way of how this function gets called, is that it must | |
| 281 // be called on the worker thread itself. | |
| 282 RTC_DCHECK(!thread_checker_.CalledOnValidThread()); | |
| 283 RTC_DCHECK(spawned_thread_checker_.CalledOnValidThread()); | |
| 284 } else { | |
| 285 // In the case of deprecated use of this method, it must be called on the | |
| 286 // same thread as the PlatformThread object is constructed on. | |
| 287 RTC_DCHECK(thread_checker_.CalledOnValidThread()); | |
| 288 RTC_DCHECK(IsRunning()); | |
| 289 } | |
| 290 #endif | |
| 291 | |
| 292 #if defined(WEBRTC_WIN) | |
| 293 return SetThreadPriority(thread_, priority) != FALSE; | |
| 294 #elif defined(__native_client__) | |
| 295 // Setting thread priorities is not supported in NaCl. | |
| 296 return true; | |
| 297 #elif defined(WEBRTC_CHROMIUM_BUILD) && defined(WEBRTC_LINUX) | |
| 298 // TODO(tommi): Switch to the same mechanism as Chromium uses for changing | |
| 299 // thread priorities. | |
| 300 return true; | |
| 301 #else | |
| 302 #ifdef WEBRTC_THREAD_RR | |
| 303 const int policy = SCHED_RR; | |
| 304 #else | |
| 305 const int policy = SCHED_FIFO; | |
| 306 #endif | |
| 307 const int min_prio = sched_get_priority_min(policy); | |
| 308 const int max_prio = sched_get_priority_max(policy); | |
| 309 if (min_prio == -1 || max_prio == -1) { | |
| 310 return false; | |
| 311 } | |
| 312 | |
| 313 if (max_prio - min_prio <= 2) | |
| 314 return false; | |
| 315 | |
| 316 // Convert webrtc priority to system priorities: | |
| 317 sched_param param; | |
| 318 const int top_prio = max_prio - 1; | |
| 319 const int low_prio = min_prio + 1; | |
| 320 switch (priority) { | |
| 321 case kLowPriority: | |
| 322 param.sched_priority = low_prio; | |
| 323 break; | |
| 324 case kNormalPriority: | |
| 325 // The -1 ensures that the kHighPriority is always greater or equal to | |
| 326 // kNormalPriority. | |
| 327 param.sched_priority = (low_prio + top_prio - 1) / 2; | |
| 328 break; | |
| 329 case kHighPriority: | |
| 330 param.sched_priority = std::max(top_prio - 2, low_prio); | |
| 331 break; | |
| 332 case kHighestPriority: | |
| 333 param.sched_priority = std::max(top_prio - 1, low_prio); | |
| 334 break; | |
| 335 case kRealtimePriority: | |
| 336 param.sched_priority = top_prio; | |
| 337 break; | |
| 338 } | |
| 339 return pthread_setschedparam(thread_, policy, ¶m) == 0; | |
| 340 #endif // defined(WEBRTC_WIN) | |
| 341 } | |
| 342 | |
| 343 #if defined(WEBRTC_WIN) | |
| 344 bool PlatformThread::QueueAPC(PAPCFUNC function, ULONG_PTR data) { | |
| 345 RTC_DCHECK(thread_checker_.CalledOnValidThread()); | |
| 346 RTC_DCHECK(IsRunning()); | |
| 347 | |
| 348 return QueueUserAPC(function, thread_, data) != FALSE; | |
| 349 } | |
| 350 #endif | |
| 351 | |
| 352 } // namespace rtc | |
| OLD | NEW |