Line data Source code
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 : #if defined(__NetBSD__)
21 : #include <lwp.h>
22 : #elif defined(__FreeBSD__)
23 : #include <pthread_np.h>
24 : #endif
25 :
26 : namespace rtc {
27 :
28 : #if defined(WEBRTC_WIN)
29 : // For use in ThreadWindowsUI callbacks
30 : static UINT static_reg_windows_msg = RegisterWindowMessageW(L"WebrtcWindowsUIThreadEvent");
31 : // timer id used in delayed callbacks
32 : static const UINT_PTR kTimerId = 1;
33 : static const wchar_t kThisProperty[] = L"ThreadWindowsUIPtr";
34 : static const wchar_t kThreadWindow[] = L"WebrtcWindowsUIThread";
35 : #endif
36 :
37 0 : PlatformThreadId CurrentThreadId() {
38 : PlatformThreadId ret;
39 : #if defined(WEBRTC_WIN)
40 : ret = GetCurrentThreadId();
41 : #elif defined(WEBRTC_POSIX)
42 : #if defined(WEBRTC_MAC) || defined(WEBRTC_IOS)
43 : ret = pthread_mach_thread_np(pthread_self());
44 : #elif defined(WEBRTC_LINUX)
45 0 : ret = syscall(__NR_gettid);
46 : #elif defined(WEBRTC_ANDROID)
47 : ret = gettid();
48 : #elif defined(__NetBSD__)
49 : ret = _lwp_self();
50 : #elif defined(__DragonFly__)
51 : ret = lwp_gettid();
52 : #elif defined(__OpenBSD__)
53 : ret = reinterpret_cast<uintptr_t> (pthread_self());
54 : #elif defined(__FreeBSD__)
55 : ret = pthread_getthreadid_np();
56 : #else
57 : // Default implementation for nacl and solaris.
58 : ret = reinterpret_cast<pid_t>(pthread_self());
59 : #endif
60 : #endif // defined(WEBRTC_POSIX)
61 0 : RTC_DCHECK(ret);
62 0 : return ret;
63 : }
64 :
65 0 : PlatformThreadRef CurrentThreadRef() {
66 : #if defined(WEBRTC_WIN)
67 : return GetCurrentThreadId();
68 : #elif defined(WEBRTC_POSIX)
69 0 : return pthread_self();
70 : #endif
71 : }
72 :
73 0 : bool IsThreadRefEqual(const PlatformThreadRef& a, const PlatformThreadRef& b) {
74 : #if defined(WEBRTC_WIN)
75 : return a == b;
76 : #elif defined(WEBRTC_POSIX)
77 0 : return pthread_equal(a, b);
78 : #endif
79 : }
80 :
81 0 : void SetCurrentThreadName(const char* name) {
82 : #if defined(WEBRTC_WIN)
83 : struct {
84 : DWORD dwType;
85 : LPCSTR szName;
86 : DWORD dwThreadID;
87 : DWORD dwFlags;
88 : } threadname_info = {0x1000, name, static_cast<DWORD>(-1), 0};
89 :
90 : __try {
91 : ::RaiseException(0x406D1388, 0, sizeof(threadname_info) / sizeof(DWORD),
92 : reinterpret_cast<ULONG_PTR*>(&threadname_info));
93 : } __except (EXCEPTION_EXECUTE_HANDLER) {
94 : }
95 : #elif defined(WEBRTC_LINUX) || defined(WEBRTC_ANDROID)
96 0 : prctl(PR_SET_NAME, reinterpret_cast<unsigned long>(name));
97 : #elif defined(WEBRTC_MAC) || defined(WEBRTC_IOS)
98 : pthread_setname_np(name);
99 : #endif
100 0 : }
101 :
102 : namespace {
103 : #if defined(WEBRTC_WIN)
104 : void CALLBACK RaiseFlag(ULONG_PTR param) {
105 : *reinterpret_cast<bool*>(param) = true;
106 : }
107 : #else
108 : struct ThreadAttributes {
109 0 : ThreadAttributes() { pthread_attr_init(&attr); }
110 0 : ~ThreadAttributes() { pthread_attr_destroy(&attr); }
111 0 : pthread_attr_t* operator&() { return &attr; }
112 : pthread_attr_t attr;
113 : };
114 : #endif // defined(WEBRTC_WIN)
115 : }
116 :
117 0 : PlatformThread::PlatformThread(ThreadRunFunction func,
118 : void* obj,
119 0 : const char* thread_name)
120 : : run_function_(func),
121 : obj_(obj),
122 : name_(thread_name ? thread_name : "webrtc"),
123 : #if defined(WEBRTC_WIN)
124 : stop_(false),
125 : thread_(NULL),
126 : thread_id_(0) {
127 : #else
128 : stop_event_(false, false),
129 0 : thread_(0) {
130 : #endif // defined(WEBRTC_WIN)
131 0 : RTC_DCHECK(func);
132 0 : RTC_DCHECK(name_.length() < 64);
133 0 : }
134 :
135 0 : PlatformThread::~PlatformThread() {
136 0 : RTC_DCHECK(thread_checker_.CalledOnValidThread());
137 : #if defined(WEBRTC_WIN)
138 : RTC_DCHECK(!thread_);
139 : RTC_DCHECK(!thread_id_);
140 : #endif // defined(WEBRTC_WIN)
141 0 : }
142 :
143 : #if defined(WEBRTC_WIN)
144 : bool PlatformUIThread::InternalInit() {
145 : // Create an event window for use in generating callbacks to capture
146 : // objects.
147 : if (hwnd_ == NULL) {
148 : WNDCLASSW wc;
149 : HMODULE hModule = GetModuleHandle(NULL);
150 : if (!GetClassInfoW(hModule, kThreadWindow, &wc)) {
151 : ZeroMemory(&wc, sizeof(WNDCLASSW));
152 : wc.hInstance = hModule;
153 : wc.lpfnWndProc = EventWindowProc;
154 : wc.lpszClassName = kThreadWindow;
155 : RegisterClassW(&wc);
156 : }
157 : hwnd_ = CreateWindowW(kThreadWindow, L"",
158 : 0, 0, 0, 0, 0,
159 : NULL, NULL, hModule, NULL);
160 : RTC_DCHECK(hwnd_);
161 : SetPropW(hwnd_, kThisProperty, this);
162 :
163 : if (timeout_) {
164 : // if someone set the timer before we started
165 : RequestCallbackTimer(timeout_);
166 : }
167 : }
168 : return !!hwnd_;
169 : }
170 :
171 : void PlatformUIThread::RequestCallback() {
172 : RTC_DCHECK(hwnd_);
173 : RTC_DCHECK(static_reg_windows_msg);
174 : PostMessage(hwnd_, static_reg_windows_msg, 0, 0);
175 : }
176 :
177 : bool PlatformUIThread::RequestCallbackTimer(unsigned int milliseconds) {
178 : if (!hwnd_) {
179 : RTC_DCHECK(!thread_);
180 : // set timer once thread starts
181 : } else {
182 : if (timerid_) {
183 : KillTimer(hwnd_, timerid_);
184 : }
185 : timerid_ = SetTimer(hwnd_, kTimerId, milliseconds, NULL);
186 : }
187 : timeout_ = milliseconds;
188 : return !!timerid_;
189 : }
190 :
191 : DWORD WINAPI PlatformThread::StartThread(void* param) {
192 : // The GetLastError() function only returns valid results when it is called
193 : // after a Win32 API function that returns a "failed" result. A crash dump
194 : // contains the result from GetLastError() and to make sure it does not
195 : // falsely report a Windows error we call SetLastError here.
196 : ::SetLastError(ERROR_SUCCESS);
197 : static_cast<PlatformThread*>(param)->Run();
198 : return 0;
199 : }
200 : #else
201 0 : void* PlatformThread::StartThread(void* param) {
202 0 : static_cast<PlatformThread*>(param)->Run();
203 0 : return 0;
204 : }
205 : #endif // defined(WEBRTC_WIN)
206 :
207 0 : void PlatformThread::Start() {
208 0 : RTC_DCHECK(thread_checker_.CalledOnValidThread());
209 0 : RTC_DCHECK(!thread_) << "Thread already started?";
210 : #if defined(WEBRTC_WIN)
211 : stop_ = false;
212 :
213 : // See bug 2902 for background on STACK_SIZE_PARAM_IS_A_RESERVATION.
214 : // Set the reserved stack stack size to 1M, which is the default on Windows
215 : // and Linux.
216 : thread_ = ::CreateThread(NULL, 1024 * 1024, &StartThread, this,
217 : STACK_SIZE_PARAM_IS_A_RESERVATION, &thread_id_);
218 : RTC_CHECK(thread_) << "CreateThread failed";
219 : RTC_DCHECK(thread_id_);
220 : #else
221 0 : ThreadAttributes attr;
222 : // Set the stack stack size to 1M.
223 0 : pthread_attr_setstacksize(&attr, 1024 * 1024);
224 0 : RTC_CHECK_EQ(0, pthread_create(&thread_, &attr, &StartThread, this));
225 : #endif // defined(WEBRTC_WIN)
226 0 : }
227 :
228 0 : bool PlatformThread::IsRunning() const {
229 0 : RTC_DCHECK(thread_checker_.CalledOnValidThread());
230 : #if defined(WEBRTC_WIN)
231 : return thread_ != nullptr;
232 : #else
233 0 : return thread_ != 0;
234 : #endif // defined(WEBRTC_WIN)
235 : }
236 :
237 0 : PlatformThreadRef PlatformThread::GetThreadRef() const {
238 : #if defined(WEBRTC_WIN)
239 : return thread_id_;
240 : #else
241 0 : return thread_;
242 : #endif // defined(WEBRTC_WIN)
243 : }
244 :
245 0 : void PlatformThread::Stop() {
246 0 : RTC_DCHECK(thread_checker_.CalledOnValidThread());
247 0 : if (!IsRunning())
248 0 : return;
249 :
250 : #if defined(WEBRTC_WIN)
251 : // Set stop_ to |true| on the worker thread.
252 : bool queued = QueueAPC(&RaiseFlag, reinterpret_cast<ULONG_PTR>(&stop_));
253 : // Queuing the APC can fail if the thread is being terminated.
254 : RTC_CHECK(queued || GetLastError() == ERROR_GEN_FAILURE);
255 : WaitForSingleObject(thread_, INFINITE);
256 : CloseHandle(thread_);
257 : thread_ = nullptr;
258 : thread_id_ = 0;
259 : #else
260 0 : stop_event_.Set();
261 0 : RTC_CHECK_EQ(0, pthread_join(thread_, nullptr));
262 0 : thread_ = 0;
263 : #endif // defined(WEBRTC_WIN)
264 : }
265 :
266 : #ifdef WEBRTC_WIN
267 : void PlatformUIThread::Stop() {
268 : RTC_DCHECK(thread_checker_.CalledOnValidThread());
269 : // Shut down the dispatch loop and let the background thread exit.
270 : if (timerid_) {
271 : KillTimer(hwnd_, timerid_);
272 : timerid_ = 0;
273 : }
274 :
275 : PostMessage(hwnd_, WM_CLOSE, 0, 0);
276 :
277 : PlatformThread::Stop();
278 : }
279 : #endif
280 :
281 0 : void PlatformThread::Run() {
282 0 : if (!name_.empty())
283 0 : rtc::SetCurrentThreadName(name_.c_str());
284 0 : do {
285 : // The interface contract of Start/Stop is that for a successful call to
286 : // Start, there should be at least one call to the run function. So we
287 : // call the function before checking |stop_|.
288 0 : if (!run_function_(obj_))
289 0 : break;
290 : #if defined(WEBRTC_WIN)
291 : // Alertable sleep to permit RaiseFlag to run and update |stop_|.
292 : SleepEx(0, true);
293 : } while (!stop_);
294 : #else
295 0 : } while (!stop_event_.Wait(0));
296 : #endif // defined(WEBRTC_WIN)
297 0 : }
298 :
299 : #if defined(WEBRTC_WIN)
300 : void PlatformUIThread::Run() {
301 : RTC_CHECK(InternalInit()); // always evaluates
302 : PlatformThread::Run();
303 : // Don't need to DestroyWindow(hwnd_) due to WM_CLOSE->WM_DESTROY handling
304 : }
305 :
306 : void PlatformUIThread::NativeEventCallback() {
307 : if (!run_function_) {
308 : stop_ = true;
309 : return;
310 : }
311 : stop_ = !run_function_(obj_);
312 : }
313 :
314 : /* static */
315 : LRESULT CALLBACK
316 : PlatformUIThread::EventWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
317 : if (uMsg == WM_DESTROY) {
318 : RemovePropW(hwnd, kThisProperty);
319 : PostQuitMessage(0);
320 : return 0;
321 : }
322 :
323 : PlatformUIThread *twui = static_cast<PlatformUIThread*>(GetPropW(hwnd, kThisProperty));
324 : if (!twui) {
325 : return DefWindowProc(hwnd, uMsg, wParam, lParam);
326 : }
327 :
328 : if ((uMsg == static_reg_windows_msg && uMsg != WM_NULL) ||
329 : (uMsg == WM_TIMER && wParam == kTimerId)) {
330 : twui->NativeEventCallback();
331 : return 0;
332 : }
333 :
334 : return DefWindowProc(hwnd, uMsg, wParam, lParam);
335 : }
336 : #endif
337 :
338 0 : bool PlatformThread::SetPriority(ThreadPriority priority) {
339 0 : RTC_DCHECK(thread_checker_.CalledOnValidThread());
340 0 : RTC_DCHECK(IsRunning());
341 : #if defined(WEBRTC_WIN)
342 : return SetThreadPriority(thread_, priority) != FALSE;
343 : #elif defined(__native_client__)
344 : // Setting thread priorities is not supported in NaCl.
345 : return true;
346 : #elif defined(WEBRTC_CHROMIUM_BUILD) && defined(WEBRTC_LINUX)
347 : // TODO(tommi): Switch to the same mechanism as Chromium uses for changing
348 : // thread priorities.
349 : return true;
350 : #else
351 : #ifdef WEBRTC_THREAD_RR
352 : const int policy = SCHED_RR;
353 : #else
354 0 : const int policy = SCHED_FIFO;
355 : #endif
356 0 : const int min_prio = sched_get_priority_min(policy);
357 0 : const int max_prio = sched_get_priority_max(policy);
358 0 : if (min_prio == -1 || max_prio == -1) {
359 0 : return false;
360 : }
361 :
362 0 : if (max_prio - min_prio <= 2)
363 0 : return false;
364 :
365 : // Convert webrtc priority to system priorities:
366 : sched_param param;
367 0 : const int top_prio = max_prio - 1;
368 0 : const int low_prio = min_prio + 1;
369 0 : switch (priority) {
370 : case kLowPriority:
371 0 : param.sched_priority = low_prio;
372 0 : break;
373 : case kNormalPriority:
374 : // The -1 ensures that the kHighPriority is always greater or equal to
375 : // kNormalPriority.
376 0 : param.sched_priority = (low_prio + top_prio - 1) / 2;
377 0 : break;
378 : case kHighPriority:
379 0 : param.sched_priority = std::max(top_prio - 2, low_prio);
380 0 : break;
381 : case kHighestPriority:
382 0 : param.sched_priority = std::max(top_prio - 1, low_prio);
383 0 : break;
384 : case kRealtimePriority:
385 0 : param.sched_priority = top_prio;
386 0 : break;
387 : }
388 0 : return pthread_setschedparam(thread_, policy, ¶m) == 0;
389 : #endif // defined(WEBRTC_WIN)
390 : }
391 :
392 : #if defined(WEBRTC_WIN)
393 : bool PlatformThread::QueueAPC(PAPCFUNC function, ULONG_PTR data) {
394 : RTC_DCHECK(thread_checker_.CalledOnValidThread());
395 : RTC_DCHECK(IsRunning());
396 :
397 : return QueueUserAPC(function, thread_, data) != FALSE;
398 : }
399 : #endif
400 :
401 : } // namespace rtc
|