blob: d32311bcea3ac0260111f57d9551cc0b75779393 [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
Tommibebc6902015-05-18 09:51:42 +020079} // namespace rtc
pbos12411ef2015-11-23 14:47:56 -080080
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
104int ConvertToSystemPriority(ThreadPriority priority,
105 int min_prio,
106 int max_prio) {
107 RTC_DCHECK(max_prio - min_prio > 2);
108 const int top_prio = max_prio - 1;
109 const int low_prio = min_prio + 1;
110
111 switch (priority) {
112 case kLowPriority:
113 return low_prio;
114 case kNormalPriority:
115 // The -1 ensures that the kHighPriority is always greater or equal to
116 // kNormalPriority.
117 return (low_prio + top_prio - 1) / 2;
118 case kHighPriority:
119 return std::max(top_prio - 2, low_prio);
120 case kHighestPriority:
121 return std::max(top_prio - 1, low_prio);
122 case kRealtimePriority:
123 return top_prio;
124 }
125 RTC_DCHECK(false);
126 return low_prio;
127}
128#endif // defined(WEBRTC_WIN)
129}
130
131PlatformThread::PlatformThread(ThreadRunFunction func,
132 void* obj,
133 const char* thread_name)
134 : run_function_(func),
135 obj_(obj),
136 name_(thread_name ? thread_name : "webrtc"),
137#if defined(WEBRTC_WIN)
138 stop_(false),
139 thread_(NULL) {
140#else
141 stop_event_(false, false),
142 thread_(0) {
143#endif // defined(WEBRTC_WIN)
144 RTC_DCHECK(func);
145 RTC_DCHECK(name_.length() < 64);
146}
147
148PlatformThread::~PlatformThread() {
149 RTC_DCHECK(thread_checker_.CalledOnValidThread());
150#if defined(WEBRTC_WIN)
151 RTC_DCHECK(!thread_);
152#endif // defined(WEBRTC_WIN)
153}
154
155#if defined(WEBRTC_WIN)
156DWORD WINAPI PlatformThread::StartThread(void* param) {
157 static_cast<PlatformThread*>(param)->Run();
158 return 0;
159}
160#else
161void* PlatformThread::StartThread(void* param) {
162 static_cast<PlatformThread*>(param)->Run();
163 return 0;
164}
165#endif // defined(WEBRTC_WIN)
166
167bool PlatformThread::Start() {
168 RTC_DCHECK(thread_checker_.CalledOnValidThread());
169 RTC_DCHECK(!thread_) << "Thread already started?";
170#if defined(WEBRTC_WIN)
171 stop_ = false;
172
173 // See bug 2902 for background on STACK_SIZE_PARAM_IS_A_RESERVATION.
174 // Set the reserved stack stack size to 1M, which is the default on Windows
175 // and Linux.
176 DWORD thread_id;
177 thread_ = ::CreateThread(NULL, 1024 * 1024, &StartThread, this,
178 STACK_SIZE_PARAM_IS_A_RESERVATION, &thread_id);
179 RTC_CHECK(thread_) << "CreateThread failed";
180#else
181 ThreadAttributes attr;
182 // Set the stack stack size to 1M.
183 pthread_attr_setstacksize(&attr, 1024 * 1024);
184 RTC_CHECK_EQ(0, pthread_create(&thread_, &attr, &StartThread, this));
185#endif // defined(WEBRTC_WIN)
186 return true;
187}
188
189bool PlatformThread::Stop() {
190 RTC_DCHECK(thread_checker_.CalledOnValidThread());
191#if defined(WEBRTC_WIN)
192 if (thread_) {
193 // Set stop_ to |true| on the worker thread.
194 QueueUserAPC(&RaiseFlag, thread_, reinterpret_cast<ULONG_PTR>(&stop_));
195 WaitForSingleObject(thread_, INFINITE);
196 CloseHandle(thread_);
197 thread_ = nullptr;
198 }
199#else
200 if (!thread_)
201 return true;
202
203 stop_event_.Set();
204 RTC_CHECK_EQ(0, pthread_join(thread_, nullptr));
205 thread_ = 0;
206#endif // defined(WEBRTC_WIN)
207 return true;
208}
209
210void PlatformThread::Run() {
211 if (!name_.empty())
212 rtc::SetCurrentThreadName(name_.c_str());
213 do {
214 // The interface contract of Start/Stop is that for a successfull call to
215 // Start, there should be at least one call to the run function. So we
216 // call the function before checking |stop_|.
217 if (!run_function_(obj_))
218 break;
219#if defined(WEBRTC_WIN)
220 // Alertable sleep to permit RaiseFlag to run and update |stop_|.
221 SleepEx(0, true);
222 } while (!stop_);
223#else
224 } while (!stop_event_.Wait(0));
225#endif // defined(WEBRTC_WIN)
226}
227
228bool PlatformThread::SetPriority(ThreadPriority priority) {
229 RTC_DCHECK(thread_checker_.CalledOnValidThread());
230#if defined(WEBRTC_WIN)
231 return thread_ && SetThreadPriority(thread_, priority);
232#else
233 if (!thread_)
234 return false;
235#if defined(WEBRTC_CHROMIUM_BUILD) && defined(WEBRTC_LINUX)
236 // TODO(tommi): Switch to the same mechanism as Chromium uses for
237 // changing thread priorities.
238 return true;
239#else
240#ifdef WEBRTC_THREAD_RR
241 const int policy = SCHED_RR;
242#else
243 const int policy = SCHED_FIFO;
244#endif
245 const int min_prio = sched_get_priority_min(policy);
246 const int max_prio = sched_get_priority_max(policy);
247 if (min_prio == -1 || max_prio == -1) {
248 return false;
249 }
250
251 if (max_prio - min_prio <= 2)
252 return false;
253
254 sched_param param;
255 param.sched_priority = ConvertToSystemPriority(priority, min_prio, max_prio);
256 if (pthread_setschedparam(thread_, policy, &param) != 0) {
257 return false;
258 }
259
260 return true;
261#endif // defined(WEBRTC_CHROMIUM_BUILD) && defined(WEBRTC_LINUX)
262#endif // defined(WEBRTC_WIN)
263}
264
265} // namespace webrtc