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