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 
CurrentThreadId()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 
CurrentThreadRef()42 PlatformThreadRef CurrentThreadRef() {
43 #if defined(WEBRTC_WIN)
44   return GetCurrentThreadId();
45 #elif defined(WEBRTC_POSIX)
46   return pthread_self();
47 #endif
48 }
49 
IsThreadRefEqual(const PlatformThreadRef & a,const PlatformThreadRef & b)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 
SetCurrentThreadName(const char * name)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)
RaiseFlag(ULONG_PTR param)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 
PlatformThread(ThreadRunFunction func,void * obj,const char * thread_name)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 
SetPriority(ThreadPriority priority)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, &param) == 0;
247 #endif  // defined(WEBRTC_WIN)
248 }
249 
250 }  // namespace rtc
251