blob: 0b025f2af048f94d9e776f171276850f54ef952e [file] [log] [blame]
Tommibebc6902015-05-18 09:51:42 +02001/*
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)
Tommiea14f0a2015-05-18 13:51:06 +020016#include <sys/prctl.h>
Tommibebc6902015-05-18 09:51:42 +020017#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)
henrikg91d6ede2015-09-17 00:24:34 -070038 RTC_DCHECK(ret);
Tommibebc6902015-05-18 09:51:42 +020039 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
Tommiea14f0a2015-05-18 13:51:06 +020058void SetCurrentThreadName(const char* name) {
Tommiea14f0a2015-05-18 13:51:06 +020059#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
pbos12411ef2015-11-23 14:47:56 -080079namespace {
80#if defined(WEBRTC_WIN)
81void CALLBACK RaiseFlag(ULONG_PTR param) {
82 *reinterpret_cast<bool*>(param) = true;
83}
84#else
85struct 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};
pbos12411ef2015-11-23 14:47:56 -080091#endif // defined(WEBRTC_WIN)
92}
93
94PlatformThread::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),
tommi845afa82016-04-22 09:08:44 -0700102 thread_(NULL),
103 thread_id_(0) {
pbos12411ef2015-11-23 14:47:56 -0800104#else
105 stop_event_(false, false),
106 thread_(0) {
107#endif // defined(WEBRTC_WIN)
108 RTC_DCHECK(func);
109 RTC_DCHECK(name_.length() < 64);
110}
111
112PlatformThread::~PlatformThread() {
113 RTC_DCHECK(thread_checker_.CalledOnValidThread());
114#if defined(WEBRTC_WIN)
115 RTC_DCHECK(!thread_);
tommi845afa82016-04-22 09:08:44 -0700116 RTC_DCHECK(!thread_id_);
pbos12411ef2015-11-23 14:47:56 -0800117#endif // defined(WEBRTC_WIN)
118}
119
120#if defined(WEBRTC_WIN)
121DWORD WINAPI PlatformThread::StartThread(void* param) {
perkj6a2e20a2016-11-30 04:53:08 -0800122 // The GetLastError() function only returns valid results when it is called
123 // after a Win32 API function that returns a "failed" result. A crash dump
124 // contains the result from GetLastError() and to make sure it does not
125 // falsely report a Windows error we call SetLastError here.
126 ::SetLastError(ERROR_SUCCESS);
pbos12411ef2015-11-23 14:47:56 -0800127 static_cast<PlatformThread*>(param)->Run();
128 return 0;
129}
130#else
131void* PlatformThread::StartThread(void* param) {
132 static_cast<PlatformThread*>(param)->Run();
133 return 0;
134}
135#endif // defined(WEBRTC_WIN)
136
Peter Boström8c38e8b2015-11-26 17:45:47 +0100137void PlatformThread::Start() {
pbos12411ef2015-11-23 14:47:56 -0800138 RTC_DCHECK(thread_checker_.CalledOnValidThread());
139 RTC_DCHECK(!thread_) << "Thread already started?";
140#if defined(WEBRTC_WIN)
141 stop_ = false;
142
143 // See bug 2902 for background on STACK_SIZE_PARAM_IS_A_RESERVATION.
144 // Set the reserved stack stack size to 1M, which is the default on Windows
145 // and Linux.
pbos12411ef2015-11-23 14:47:56 -0800146 thread_ = ::CreateThread(NULL, 1024 * 1024, &StartThread, this,
tommi845afa82016-04-22 09:08:44 -0700147 STACK_SIZE_PARAM_IS_A_RESERVATION, &thread_id_);
pbos12411ef2015-11-23 14:47:56 -0800148 RTC_CHECK(thread_) << "CreateThread failed";
tommi845afa82016-04-22 09:08:44 -0700149 RTC_DCHECK(thread_id_);
pbos12411ef2015-11-23 14:47:56 -0800150#else
151 ThreadAttributes attr;
152 // Set the stack stack size to 1M.
153 pthread_attr_setstacksize(&attr, 1024 * 1024);
154 RTC_CHECK_EQ(0, pthread_create(&thread_, &attr, &StartThread, this));
155#endif // defined(WEBRTC_WIN)
pbos12411ef2015-11-23 14:47:56 -0800156}
157
Peter Boström8c38e8b2015-11-26 17:45:47 +0100158bool PlatformThread::IsRunning() const {
pbos12411ef2015-11-23 14:47:56 -0800159 RTC_DCHECK(thread_checker_.CalledOnValidThread());
160#if defined(WEBRTC_WIN)
Peter Boström8c38e8b2015-11-26 17:45:47 +0100161 return thread_ != nullptr;
pbos12411ef2015-11-23 14:47:56 -0800162#else
Peter Boström8c38e8b2015-11-26 17:45:47 +0100163 return thread_ != 0;
164#endif // defined(WEBRTC_WIN)
165}
pbos12411ef2015-11-23 14:47:56 -0800166
tommi845afa82016-04-22 09:08:44 -0700167PlatformThreadRef PlatformThread::GetThreadRef() const {
168#if defined(WEBRTC_WIN)
169 return thread_id_;
170#else
171 return thread_;
172#endif // defined(WEBRTC_WIN)
173}
174
Peter Boström8c38e8b2015-11-26 17:45:47 +0100175void PlatformThread::Stop() {
176 RTC_DCHECK(thread_checker_.CalledOnValidThread());
177 if (!IsRunning())
178 return;
179
180#if defined(WEBRTC_WIN)
181 // Set stop_ to |true| on the worker thread.
tommi845afa82016-04-22 09:08:44 -0700182 bool queued = QueueAPC(&RaiseFlag, reinterpret_cast<ULONG_PTR>(&stop_));
183 // Queuing the APC can fail if the thread is being terminated.
184 RTC_CHECK(queued || GetLastError() == ERROR_GEN_FAILURE);
Peter Boström8c38e8b2015-11-26 17:45:47 +0100185 WaitForSingleObject(thread_, INFINITE);
186 CloseHandle(thread_);
187 thread_ = nullptr;
tommi845afa82016-04-22 09:08:44 -0700188 thread_id_ = 0;
Peter Boström8c38e8b2015-11-26 17:45:47 +0100189#else
pbos12411ef2015-11-23 14:47:56 -0800190 stop_event_.Set();
191 RTC_CHECK_EQ(0, pthread_join(thread_, nullptr));
192 thread_ = 0;
193#endif // defined(WEBRTC_WIN)
pbos12411ef2015-11-23 14:47:56 -0800194}
195
196void PlatformThread::Run() {
197 if (!name_.empty())
198 rtc::SetCurrentThreadName(name_.c_str());
199 do {
sprange791ffd2016-01-26 01:53:20 -0800200 // The interface contract of Start/Stop is that for a successful call to
pbos12411ef2015-11-23 14:47:56 -0800201 // Start, there should be at least one call to the run function. So we
202 // call the function before checking |stop_|.
203 if (!run_function_(obj_))
204 break;
205#if defined(WEBRTC_WIN)
206 // Alertable sleep to permit RaiseFlag to run and update |stop_|.
207 SleepEx(0, true);
208 } while (!stop_);
209#else
210 } while (!stop_event_.Wait(0));
211#endif // defined(WEBRTC_WIN)
212}
213
214bool PlatformThread::SetPriority(ThreadPriority priority) {
215 RTC_DCHECK(thread_checker_.CalledOnValidThread());
Peter Boström8c38e8b2015-11-26 17:45:47 +0100216 RTC_DCHECK(IsRunning());
Peter Boströmc6612132015-11-24 18:10:24 +0100217#if defined(WEBRTC_WIN)
Peter Boström8c38e8b2015-11-26 17:45:47 +0100218 return SetThreadPriority(thread_, priority) != FALSE;
Peter Boströmc6612132015-11-24 18:10:24 +0100219#elif defined(__native_client__)
220 // Setting thread priorities is not supported in NaCl.
221 return true;
222#elif defined(WEBRTC_CHROMIUM_BUILD) && defined(WEBRTC_LINUX)
223 // TODO(tommi): Switch to the same mechanism as Chromium uses for changing
224 // thread priorities.
pbos12411ef2015-11-23 14:47:56 -0800225 return true;
226#else
227#ifdef WEBRTC_THREAD_RR
228 const int policy = SCHED_RR;
229#else
230 const int policy = SCHED_FIFO;
231#endif
232 const int min_prio = sched_get_priority_min(policy);
233 const int max_prio = sched_get_priority_max(policy);
234 if (min_prio == -1 || max_prio == -1) {
235 return false;
236 }
237
238 if (max_prio - min_prio <= 2)
239 return false;
240
Peter Boström97c821d2015-11-24 13:48:13 +0100241 // Convert webrtc priority to system priorities:
pbos12411ef2015-11-23 14:47:56 -0800242 sched_param param;
Peter Boström97c821d2015-11-24 13:48:13 +0100243 const int top_prio = max_prio - 1;
244 const int low_prio = min_prio + 1;
245 switch (priority) {
246 case kLowPriority:
247 param.sched_priority = low_prio;
248 break;
249 case kNormalPriority:
250 // The -1 ensures that the kHighPriority is always greater or equal to
251 // kNormalPriority.
252 param.sched_priority = (low_prio + top_prio - 1) / 2;
253 break;
254 case kHighPriority:
255 param.sched_priority = std::max(top_prio - 2, low_prio);
256 break;
257 case kHighestPriority:
258 param.sched_priority = std::max(top_prio - 1, low_prio);
259 break;
260 case kRealtimePriority:
261 param.sched_priority = top_prio;
262 break;
pbos12411ef2015-11-23 14:47:56 -0800263 }
Peter Boström97c821d2015-11-24 13:48:13 +0100264 return pthread_setschedparam(thread_, policy, &param) == 0;
pbos12411ef2015-11-23 14:47:56 -0800265#endif // defined(WEBRTC_WIN)
266}
267
tommi845afa82016-04-22 09:08:44 -0700268#if defined(WEBRTC_WIN)
269bool PlatformThread::QueueAPC(PAPCFUNC function, ULONG_PTR data) {
270 RTC_DCHECK(thread_checker_.CalledOnValidThread());
271 RTC_DCHECK(IsRunning());
272
273 return QueueUserAPC(function, thread_, data) != FALSE;
274}
275#endif
276
Peter Boström8c38e8b2015-11-26 17:45:47 +0100277} // namespace rtc