1/*
2 *  Copyright (c) 2012 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// The state of a thread is controlled by the two member variables
12// alive_ and dead_.
13// alive_ represents the state the thread has been ordered to achieve.
14// It is set to true by the thread at startup, and is set to false by
15// other threads, using SetNotAlive() and Stop().
16// dead_ represents the state the thread has achieved.
17// It is written by the thread encapsulated by this class only
18// (except at init). It is read only by the Stop() method.
19// The Run() method fires event_ when it's started; this ensures that the
20// Start() method does not continue until after dead_ is false.
21// This protects against premature Stop() calls from the creator thread, but
22// not from other threads.
23
24// Their transitions and states:
25// alive_    dead_  Set by
26// false     true   Constructor
27// true      false  Run() method entry
28// false     any    Run() method run_function failure
29// any       false  Run() method exit (happens only with alive_ false)
30// false     any    SetNotAlive
31// false     any    Stop         Stop waits for dead_ to become true.
32//
33// Summarized a different way:
34// Variable   Writer               Reader
35// alive_     Constructor(false)   Run.loop
36//            Run.start(true)
37//            Run.fail(false)
38//            SetNotAlive(false)
39//            Stop(false)
40//
41// dead_      Constructor(true)    Stop.loop
42//            Run.start(false)
43//            Run.exit(true)
44
45#include "webrtc/system_wrappers/source/thread_posix.h"
46
47#include <algorithm>
48
49#include <assert.h>
50#include <errno.h>
51#include <string.h>  // strncpy
52#include <unistd.h>
53#ifdef WEBRTC_LINUX
54#include <linux/unistd.h>
55#include <sched.h>
56#include <sys/prctl.h>
57#include <sys/syscall.h>
58#include <sys/types.h>
59#endif
60
61#include "webrtc/system_wrappers/interface/critical_section_wrapper.h"
62#include "webrtc/system_wrappers/interface/event_wrapper.h"
63#include "webrtc/system_wrappers/interface/sleep.h"
64#include "webrtc/system_wrappers/interface/trace.h"
65
66namespace webrtc {
67
68int ConvertToSystemPriority(ThreadPriority priority, int min_prio,
69                            int max_prio) {
70  assert(max_prio - min_prio > 2);
71  const int top_prio = max_prio - 1;
72  const int low_prio = min_prio + 1;
73
74  switch (priority) {
75    case kLowPriority:
76      return low_prio;
77    case kNormalPriority:
78      // The -1 ensures that the kHighPriority is always greater or equal to
79      // kNormalPriority.
80      return (low_prio + top_prio - 1) / 2;
81    case kHighPriority:
82      return std::max(top_prio - 2, low_prio);
83    case kHighestPriority:
84      return std::max(top_prio - 1, low_prio);
85    case kRealtimePriority:
86      return top_prio;
87  }
88  assert(false);
89  return low_prio;
90}
91
92extern "C"
93{
94  static void* StartThread(void* lp_parameter) {
95    static_cast<ThreadPosix*>(lp_parameter)->Run();
96    return 0;
97  }
98}
99
100ThreadWrapper* ThreadPosix::Create(ThreadRunFunction func, ThreadObj obj,
101                                   ThreadPriority prio,
102                                   const char* thread_name) {
103  ThreadPosix* ptr = new ThreadPosix(func, obj, prio, thread_name);
104  if (!ptr) {
105    return NULL;
106  }
107  const int error = ptr->Construct();
108  if (error) {
109    delete ptr;
110    return NULL;
111  }
112  return ptr;
113}
114
115ThreadPosix::ThreadPosix(ThreadRunFunction func, ThreadObj obj,
116                         ThreadPriority prio, const char* thread_name)
117    : run_function_(func),
118      obj_(obj),
119      crit_state_(CriticalSectionWrapper::CreateCriticalSection()),
120      alive_(false),
121      dead_(true),
122      prio_(prio),
123      event_(EventWrapper::Create()),
124      name_(),
125      set_thread_name_(false),
126#if (defined(WEBRTC_LINUX) || defined(WEBRTC_ANDROID))
127      pid_(-1),
128#endif
129      attr_(),
130      thread_(0) {
131  if (thread_name != NULL) {
132    set_thread_name_ = true;
133    strncpy(name_, thread_name, kThreadMaxNameLength);
134    name_[kThreadMaxNameLength - 1] = '\0';
135  }
136}
137
138uint32_t ThreadWrapper::GetThreadId() {
139#if defined(WEBRTC_ANDROID) || defined(WEBRTC_LINUX)
140  return static_cast<uint32_t>(syscall(__NR_gettid));
141#elif defined(WEBRTC_MAC) || defined(WEBRTC_IOS)
142  return pthread_mach_thread_np(pthread_self());
143#else
144  return reinterpret_cast<uint32_t>(pthread_self());
145#endif
146}
147
148int ThreadPosix::Construct() {
149  int result = 0;
150#if !defined(WEBRTC_ANDROID)
151  // Enable immediate cancellation if requested, see Shutdown().
152  result = pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
153  if (result != 0) {
154    return -1;
155  }
156  result = pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
157  if (result != 0) {
158    return -1;
159  }
160#endif
161  result = pthread_attr_init(&attr_);
162  if (result != 0) {
163    return -1;
164  }
165  return 0;
166}
167
168ThreadPosix::~ThreadPosix() {
169  pthread_attr_destroy(&attr_);
170  delete event_;
171  delete crit_state_;
172}
173
174#define HAS_THREAD_ID !defined(WEBRTC_IOS) && !defined(WEBRTC_MAC)
175
176bool ThreadPosix::Start(unsigned int& thread_id)
177{
178  int result = pthread_attr_setdetachstate(&attr_, PTHREAD_CREATE_DETACHED);
179  // Set the stack stack size to 1M.
180  result |= pthread_attr_setstacksize(&attr_, 1024 * 1024);
181#ifdef WEBRTC_THREAD_RR
182  const int policy = SCHED_RR;
183#else
184  const int policy = SCHED_FIFO;
185#endif
186  event_->Reset();
187  // If pthread_create was successful, a thread was created and is running.
188  // Don't return false if it was successful since if there are any other
189  // failures the state will be: thread was started but not configured as
190  // asked for. However, the caller of this API will assume that a false
191  // return value means that the thread never started.
192  result |= pthread_create(&thread_, &attr_, &StartThread, this);
193  if (result != 0) {
194    return false;
195  }
196  {
197    CriticalSectionScoped cs(crit_state_);
198    dead_ = false;
199  }
200
201  // Wait up to 10 seconds for the OS to call the callback function. Prevents
202  // race condition if Stop() is called too quickly after start.
203  if (kEventSignaled != event_->Wait(WEBRTC_EVENT_10_SEC)) {
204    WEBRTC_TRACE(kTraceError, kTraceUtility, -1,
205                 "posix thread event never triggered");
206    // Timed out. Something went wrong.
207    return true;
208  }
209
210#if HAS_THREAD_ID
211  thread_id = static_cast<unsigned int>(thread_);
212#endif
213  sched_param param;
214
215  const int min_prio = sched_get_priority_min(policy);
216  const int max_prio = sched_get_priority_max(policy);
217
218  if ((min_prio == EINVAL) || (max_prio == EINVAL)) {
219    WEBRTC_TRACE(kTraceError, kTraceUtility, -1,
220                 "unable to retreive min or max priority for threads");
221    return true;
222  }
223  if (max_prio - min_prio <= 2) {
224    // There is no room for setting priorities with any granularity.
225    return true;
226  }
227  param.sched_priority = ConvertToSystemPriority(prio_, min_prio, max_prio);
228  result = pthread_setschedparam(thread_, policy, &param);
229  if (result == EINVAL) {
230    WEBRTC_TRACE(kTraceError, kTraceUtility, -1,
231                 "unable to set thread priority");
232  }
233  return true;
234}
235
236// CPU_ZERO and CPU_SET are not available in NDK r7, so disable
237// SetAffinity on Android for now.
238#if (defined(WEBRTC_LINUX) && (!defined(WEBRTC_ANDROID)))
239bool ThreadPosix::SetAffinity(const int* processor_numbers,
240                              const unsigned int amount_of_processors) {
241  if (!processor_numbers || (amount_of_processors == 0)) {
242    return false;
243  }
244  cpu_set_t mask;
245  CPU_ZERO(&mask);
246
247  for (unsigned int processor = 0;
248       processor < amount_of_processors;
249       ++processor) {
250    CPU_SET(processor_numbers[processor], &mask);
251  }
252#if defined(WEBRTC_ANDROID)
253  // Android.
254  const int result = syscall(__NR_sched_setaffinity,
255                             pid_,
256                             sizeof(mask),
257                             &mask);
258#else
259  // "Normal" Linux.
260  const int result = sched_setaffinity(pid_,
261                                       sizeof(mask),
262                                       &mask);
263#endif
264  if (result != 0) {
265    return false;
266  }
267  return true;
268}
269
270#else
271// NOTE: On Mac OS X, use the Thread affinity API in
272// /usr/include/mach/thread_policy.h: thread_policy_set and mach_thread_self()
273// instead of Linux gettid() syscall.
274bool ThreadPosix::SetAffinity(const int* , const unsigned int) {
275  return false;
276}
277#endif
278
279void ThreadPosix::SetNotAlive() {
280  CriticalSectionScoped cs(crit_state_);
281  alive_ = false;
282}
283
284bool ThreadPosix::Stop() {
285  bool dead = false;
286  {
287    CriticalSectionScoped cs(crit_state_);
288    alive_ = false;
289    dead = dead_;
290  }
291
292  // TODO(hellner) why not use an event here?
293  // Wait up to 10 seconds for the thread to terminate
294  for (int i = 0; i < 1000 && !dead; ++i) {
295    SleepMs(10);
296    {
297      CriticalSectionScoped cs(crit_state_);
298      dead = dead_;
299    }
300  }
301  if (dead) {
302    return true;
303  } else {
304    return false;
305  }
306}
307
308void ThreadPosix::Run() {
309  {
310    CriticalSectionScoped cs(crit_state_);
311    alive_ = true;
312  }
313#if (defined(WEBRTC_LINUX) || defined(WEBRTC_ANDROID))
314  pid_ = GetThreadId();
315#endif
316  // The event the Start() is waiting for.
317  event_->Set();
318
319  if (set_thread_name_) {
320#ifdef WEBRTC_LINUX
321    prctl(PR_SET_NAME, (unsigned long)name_, 0, 0, 0);
322#endif
323    WEBRTC_TRACE(kTraceStateInfo, kTraceUtility, -1,
324                 "Thread with name:%s started ", name_);
325  } else {
326    WEBRTC_TRACE(kTraceStateInfo, kTraceUtility, -1,
327                 "Thread without name started");
328  }
329  bool alive = true;
330  bool run = true;
331  while (alive) {
332    run = run_function_(obj_);
333    CriticalSectionScoped cs(crit_state_);
334    if (!run) {
335      alive_ = false;
336    }
337    alive = alive_;
338  }
339
340  if (set_thread_name_) {
341    // Don't set the name for the trace thread because it may cause a
342    // deadlock. TODO(hellner) there should be a better solution than
343    // coupling the thread and the trace class like this.
344    if (strcmp(name_, "Trace")) {
345      WEBRTC_TRACE(kTraceStateInfo, kTraceUtility, -1,
346                   "Thread with name:%s stopped", name_);
347    }
348  } else {
349    WEBRTC_TRACE(kTraceStateInfo, kTraceUtility, -1,
350                 "Thread without name stopped");
351  }
352  {
353    CriticalSectionScoped cs(crit_state_);
354    dead_ = true;
355  }
356}
357
358}  // namespace webrtc
359