blob: c821931973d6d9afa7953e1ac3f45f835eae593e [file] [log] [blame]
niklase@google.com470e71d2011-07-07 08:21:25 +00001/*
mflodman@webrtc.orgc80d9d92012-02-06 10:11:25 +00002 * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
niklase@google.com470e71d2011-07-07 08:21:25 +00003 *
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
hta@webrtc.orge1919f42012-05-22 15:57:34 +000011// The state of a thread is controlled by the two member variables
phoglund@webrtc.orgec9c9422013-01-02 08:45:03 +000012// alive_ and dead_.
13// alive_ represents the state the thread has been ordered to achieve.
hta@webrtc.orge1919f42012-05-22 15:57:34 +000014// It is set to true by the thread at startup, and is set to false by
15// other threads, using SetNotAlive() and Stop().
phoglund@webrtc.orgec9c9422013-01-02 08:45:03 +000016// dead_ represents the state the thread has achieved.
hta@webrtc.orge1919f42012-05-22 15:57:34 +000017// It is written by the thread encapsulated by this class only
18// (except at init). It is read only by the Stop() method.
phoglund@webrtc.orgec9c9422013-01-02 08:45:03 +000019// The Run() method fires event_ when it's started; this ensures that the
20// Start() method does not continue until after dead_ is false.
hta@webrtc.orge1919f42012-05-22 15:57:34 +000021// This protects against premature Stop() calls from the creator thread, but
22// not from other threads.
23
24// Their transitions and states:
phoglund@webrtc.orgec9c9422013-01-02 08:45:03 +000025// alive_ dead_ Set by
hta@webrtc.orge1919f42012-05-22 15:57:34 +000026// false true Constructor
27// true false Run() method entry
phoglund@webrtc.orgec9c9422013-01-02 08:45:03 +000028// false any Run() method run_function failure
29// any false Run() method exit (happens only with alive_ false)
hta@webrtc.orge1919f42012-05-22 15:57:34 +000030// false any SetNotAlive
phoglund@webrtc.orgec9c9422013-01-02 08:45:03 +000031// false any Stop Stop waits for dead_ to become true.
hta@webrtc.orge1919f42012-05-22 15:57:34 +000032//
33// Summarized a different way:
34// Variable Writer Reader
phoglund@webrtc.orgec9c9422013-01-02 08:45:03 +000035// alive_ Constructor(false) Run.loop
hta@webrtc.orge1919f42012-05-22 15:57:34 +000036// Run.start(true)
37// Run.fail(false)
38// SetNotAlive(false)
39// Stop(false)
40//
phoglund@webrtc.orgec9c9422013-01-02 08:45:03 +000041// dead_ Constructor(true) Stop.loop
hta@webrtc.orge1919f42012-05-22 15:57:34 +000042// Run.start(false)
43// Run.exit(true)
44
phoglund@webrtc.orgec9c9422013-01-02 08:45:03 +000045#include "webrtc/system_wrappers/source/thread_posix.h"
niklase@google.com470e71d2011-07-07 08:21:25 +000046
henrike@webrtc.org5ba44112012-10-05 14:36:54 +000047#include <algorithm>
48
49#include <assert.h>
niklase@google.com470e71d2011-07-07 08:21:25 +000050#include <errno.h>
phoglund@webrtc.orgec9c9422013-01-02 08:45:03 +000051#include <string.h> // strncpy
52#include <time.h> // nanosleep
niklase@google.com470e71d2011-07-07 08:21:25 +000053#include <unistd.h>
54#ifdef WEBRTC_LINUX
55#include <sys/types.h>
56#include <sched.h>
57#include <sys/syscall.h>
58#include <linux/unistd.h>
59#include <sys/prctl.h>
60#endif
61
phoglund@webrtc.orgec9c9422013-01-02 08:45:03 +000062#include "webrtc/system_wrappers/interface/critical_section_wrapper.h"
63#include "webrtc/system_wrappers/interface/event_wrapper.h"
64#include "webrtc/system_wrappers/interface/trace.h"
niklase@google.com470e71d2011-07-07 08:21:25 +000065
66namespace webrtc {
henrike@webrtc.org5ba44112012-10-05 14:36:54 +000067
phoglund@webrtc.orgec9c9422013-01-02 08:45:03 +000068int 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;
henrike@webrtc.org5ba44112012-10-05 14:36:54 +000073
phoglund@webrtc.orgec9c9422013-01-02 08:45:03 +000074 switch (priority) {
henrike@webrtc.org5ba44112012-10-05 14:36:54 +000075 case kLowPriority:
phoglund@webrtc.orgec9c9422013-01-02 08:45:03 +000076 return low_prio;
henrike@webrtc.org5ba44112012-10-05 14:36:54 +000077 case kNormalPriority:
phoglund@webrtc.orgec9c9422013-01-02 08:45:03 +000078 // The -1 ensures that the kHighPriority is always greater or equal to
79 // kNormalPriority.
80 return (low_prio + top_prio - 1) / 2;
henrike@webrtc.org5ba44112012-10-05 14:36:54 +000081 case kHighPriority:
phoglund@webrtc.orgec9c9422013-01-02 08:45:03 +000082 return std::max(top_prio - 2, low_prio);
henrike@webrtc.org5ba44112012-10-05 14:36:54 +000083 case kHighestPriority:
phoglund@webrtc.orgec9c9422013-01-02 08:45:03 +000084 return std::max(top_prio - 1, low_prio);
henrike@webrtc.org5ba44112012-10-05 14:36:54 +000085 case kRealtimePriority:
phoglund@webrtc.orgec9c9422013-01-02 08:45:03 +000086 return top_prio;
87 }
88 assert(false);
89 return low_prio;
henrike@webrtc.org5ba44112012-10-05 14:36:54 +000090}
91
niklase@google.com470e71d2011-07-07 08:21:25 +000092extern "C"
93{
phoglund@webrtc.orgec9c9422013-01-02 08:45:03 +000094 static void* StartThread(void* lp_parameter) {
95 static_cast<ThreadPosix*>(lp_parameter)->Run();
96 return 0;
97 }
niklase@google.com470e71d2011-07-07 08:21:25 +000098}
99
henrike@webrtc.orga3e6bec2013-01-18 16:39:21 +0000100ThreadWrapper* ThreadPosix::Create(ThreadRunFunction func, ThreadObj obj,
phoglund@webrtc.orgec9c9422013-01-02 08:45:03 +0000101 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;
niklase@google.com470e71d2011-07-07 08:21:25 +0000113}
114
ajm@google.comb5c49ff2011-08-01 17:04:04 +0000115ThreadPosix::ThreadPosix(ThreadRunFunction func, ThreadObj obj,
phoglund@webrtc.orgec9c9422013-01-02 08:45:03 +0000116 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),
pwestin@webrtc.orgdf9866f2012-01-11 08:57:47 +0000126#if (defined(WEBRTC_LINUX) || defined(WEBRTC_ANDROID))
phoglund@webrtc.orgec9c9422013-01-02 08:45:03 +0000127 pid_(-1),
pwestin@webrtc.orgdf9866f2012-01-11 08:57:47 +0000128#endif
phoglund@webrtc.orgec9c9422013-01-02 08:45:03 +0000129 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 }
niklase@google.com470e71d2011-07-07 08:21:25 +0000136}
137
pwestin@webrtc.orgb54d7272012-01-11 08:28:04 +0000138uint32_t ThreadWrapper::GetThreadId() {
139#if defined(WEBRTC_ANDROID) || defined(WEBRTC_LINUX)
140 return static_cast<uint32_t>(syscall(__NR_gettid));
pwestin@webrtc.orgb54d7272012-01-11 08:28:04 +0000141#else
142 return reinterpret_cast<uint32_t>(pthread_self());
143#endif
144}
145
phoglund@webrtc.orgec9c9422013-01-02 08:45:03 +0000146int ThreadPosix::Construct() {
147 int result = 0;
leozwang@google.comb3527002011-07-26 17:29:38 +0000148#if !defined(WEBRTC_ANDROID)
phoglund@webrtc.orgec9c9422013-01-02 08:45:03 +0000149 // Enable immediate cancellation if requested, see Shutdown().
150 result = pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
151 if (result != 0) {
152 return -1;
153 }
154 result = pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
155 if (result != 0) {
156 return -1;
157 }
niklase@google.com470e71d2011-07-07 08:21:25 +0000158#endif
phoglund@webrtc.orgec9c9422013-01-02 08:45:03 +0000159 result = pthread_attr_init(&attr_);
160 if (result != 0) {
161 return -1;
162 }
163 return 0;
niklase@google.com470e71d2011-07-07 08:21:25 +0000164}
165
phoglund@webrtc.orgec9c9422013-01-02 08:45:03 +0000166ThreadPosix::~ThreadPosix() {
167 pthread_attr_destroy(&attr_);
168 delete event_;
169 delete crit_state_;
niklase@google.com470e71d2011-07-07 08:21:25 +0000170}
171
sjlee@webrtc.org414fa7f2012-09-11 17:25:46 +0000172#define HAS_THREAD_ID !defined(WEBRTC_IOS) && !defined(WEBRTC_MAC)
phoglund@webrtc.orgec9c9422013-01-02 08:45:03 +0000173
174bool ThreadPosix::Start(unsigned int& thread_id)
niklase@google.com470e71d2011-07-07 08:21:25 +0000175{
phoglund@webrtc.orgec9c9422013-01-02 08:45:03 +0000176 int result = pthread_attr_setdetachstate(&attr_, PTHREAD_CREATE_DETACHED);
177 // Set the stack stack size to 1M.
178 result |= pthread_attr_setstacksize(&attr_, 1024 * 1024);
niklase@google.com470e71d2011-07-07 08:21:25 +0000179#ifdef WEBRTC_THREAD_RR
phoglund@webrtc.orgec9c9422013-01-02 08:45:03 +0000180 const int policy = SCHED_RR;
niklase@google.com470e71d2011-07-07 08:21:25 +0000181#else
phoglund@webrtc.orgec9c9422013-01-02 08:45:03 +0000182 const int policy = SCHED_FIFO;
niklase@google.com470e71d2011-07-07 08:21:25 +0000183#endif
phoglund@webrtc.orgec9c9422013-01-02 08:45:03 +0000184 event_->Reset();
185 // If pthread_create was successful, a thread was created and is running.
186 // Don't return false if it was successful since if there are any other
187 // failures the state will be: thread was started but not configured as
188 // asked for. However, the caller of this API will assume that a false
189 // return value means that the thread never started.
190 result |= pthread_create(&thread_, &attr_, &StartThread, this);
191 if (result != 0) {
192 return false;
193 }
henrike@webrtc.orga3e6bec2013-01-18 16:39:21 +0000194 {
195 CriticalSectionScoped cs(crit_state_);
196 dead_ = false;
197 }
niklase@google.com470e71d2011-07-07 08:21:25 +0000198
phoglund@webrtc.orgec9c9422013-01-02 08:45:03 +0000199 // Wait up to 10 seconds for the OS to call the callback function. Prevents
200 // race condition if Stop() is called too quickly after start.
201 if (kEventSignaled != event_->Wait(WEBRTC_EVENT_10_SEC)) {
202 WEBRTC_TRACE(kTraceError, kTraceUtility, -1,
203 "posix thread event never triggered");
phoglund@webrtc.orgec9c9422013-01-02 08:45:03 +0000204 // Timed out. Something went wrong.
phoglund@webrtc.orgec9c9422013-01-02 08:45:03 +0000205 return true;
206 }
niklase@google.com470e71d2011-07-07 08:21:25 +0000207
208#if HAS_THREAD_ID
phoglund@webrtc.orgec9c9422013-01-02 08:45:03 +0000209 thread_id = static_cast<unsigned int>(thread_);
niklase@google.com470e71d2011-07-07 08:21:25 +0000210#endif
phoglund@webrtc.orgec9c9422013-01-02 08:45:03 +0000211 sched_param param;
niklase@google.com470e71d2011-07-07 08:21:25 +0000212
phoglund@webrtc.orgec9c9422013-01-02 08:45:03 +0000213 const int min_prio = sched_get_priority_min(policy);
214 const int max_prio = sched_get_priority_max(policy);
215
216 if ((min_prio == EINVAL) || (max_prio == EINVAL)) {
217 WEBRTC_TRACE(kTraceError, kTraceUtility, -1,
218 "unable to retreive min or max priority for threads");
niklase@google.com470e71d2011-07-07 08:21:25 +0000219 return true;
phoglund@webrtc.orgec9c9422013-01-02 08:45:03 +0000220 }
221 if (max_prio - min_prio <= 2) {
222 // There is no room for setting priorities with any granularity.
223 return true;
224 }
225 param.sched_priority = ConvertToSystemPriority(prio_, min_prio, max_prio);
226 result = pthread_setschedparam(thread_, policy, &param);
227 if (result == EINVAL) {
228 WEBRTC_TRACE(kTraceError, kTraceUtility, -1,
229 "unable to set thread priority");
230 }
231 return true;
niklase@google.com470e71d2011-07-07 08:21:25 +0000232}
233
leozwang@webrtc.org0a272eb2012-02-15 22:35:29 +0000234// CPU_ZERO and CPU_SET are not available in NDK r7, so disable
235// SetAffinity on Android for now.
236#if (defined(WEBRTC_LINUX) && (!defined(WEBRTC_ANDROID)))
phoglund@webrtc.orgec9c9422013-01-02 08:45:03 +0000237bool ThreadPosix::SetAffinity(const int* processor_numbers,
238 const unsigned int amount_of_processors) {
239 if (!processor_numbers || (amount_of_processors == 0)) {
pwestin@webrtc.orgb54d7272012-01-11 08:28:04 +0000240 return false;
241 }
242 cpu_set_t mask;
243 CPU_ZERO(&mask);
niklase@google.com470e71d2011-07-07 08:21:25 +0000244
pwestin@webrtc.orgb54d7272012-01-11 08:28:04 +0000245 for (unsigned int processor = 0;
phoglund@webrtc.orgec9c9422013-01-02 08:45:03 +0000246 processor < amount_of_processors;
247 ++processor) {
248 CPU_SET(processor_numbers[processor], &mask);
pwestin@webrtc.orgb54d7272012-01-11 08:28:04 +0000249 }
250#if defined(WEBRTC_ANDROID)
251 // Android.
252 const int result = syscall(__NR_sched_setaffinity,
phoglund@webrtc.orgec9c9422013-01-02 08:45:03 +0000253 pid_,
pwestin@webrtc.orgb54d7272012-01-11 08:28:04 +0000254 sizeof(mask),
255 &mask);
256#else
257 // "Normal" Linux.
phoglund@webrtc.orgec9c9422013-01-02 08:45:03 +0000258 const int result = sched_setaffinity(pid_,
pwestin@webrtc.orgb54d7272012-01-11 08:28:04 +0000259 sizeof(mask),
260 &mask);
261#endif
262 if (result != 0) {
263 return false;
264 }
265 return true;
niklase@google.com470e71d2011-07-07 08:21:25 +0000266}
pwestin@webrtc.orgb54d7272012-01-11 08:28:04 +0000267
niklase@google.com470e71d2011-07-07 08:21:25 +0000268#else
269// NOTE: On Mac OS X, use the Thread affinity API in
270// /usr/include/mach/thread_policy.h: thread_policy_set and mach_thread_self()
271// instead of Linux gettid() syscall.
phoglund@webrtc.orgec9c9422013-01-02 08:45:03 +0000272bool ThreadPosix::SetAffinity(const int* , const unsigned int) {
273 return false;
274}
275#endif
276
277void ThreadPosix::SetNotAlive() {
278 CriticalSectionScoped cs(crit_state_);
279 alive_ = false;
280}
281
282bool ThreadPosix::Stop() {
283 bool dead = false;
284 {
285 CriticalSectionScoped cs(crit_state_);
286 alive_ = false;
287 dead = dead_;
288 }
289
290 // TODO(hellner) why not use an event here?
291 // Wait up to 10 seconds for the thread to terminate
henrike@webrtc.orga3e6bec2013-01-18 16:39:21 +0000292 for (int i = 0; i < 1000 && !dead; ++i) {
phoglund@webrtc.orgec9c9422013-01-02 08:45:03 +0000293 timespec t;
294 t.tv_sec = 0;
295 t.tv_nsec = 10 * 1000 * 1000;
296 nanosleep(&t, NULL);
297 {
298 CriticalSectionScoped cs(crit_state_);
299 dead = dead_;
300 }
301 }
302 if (dead) {
303 return true;
304 } else {
niklase@google.com470e71d2011-07-07 08:21:25 +0000305 return false;
phoglund@webrtc.orgec9c9422013-01-02 08:45:03 +0000306 }
niklase@google.com470e71d2011-07-07 08:21:25 +0000307}
308
phoglund@webrtc.orgec9c9422013-01-02 08:45:03 +0000309void ThreadPosix::Run() {
310 {
311 CriticalSectionScoped cs(crit_state_);
312 alive_ = true;
phoglund@webrtc.orgec9c9422013-01-02 08:45:03 +0000313 }
pwestin@webrtc.orgdf9866f2012-01-11 08:57:47 +0000314#if (defined(WEBRTC_LINUX) || defined(WEBRTC_ANDROID))
phoglund@webrtc.orgec9c9422013-01-02 08:45:03 +0000315 pid_ = GetThreadId();
pwestin@webrtc.orgdf9866f2012-01-11 08:57:47 +0000316#endif
phoglund@webrtc.orgec9c9422013-01-02 08:45:03 +0000317 // The event the Start() is waiting for.
318 event_->Set();
niklase@google.com470e71d2011-07-07 08:21:25 +0000319
phoglund@webrtc.orgec9c9422013-01-02 08:45:03 +0000320 if (set_thread_name_) {
niklase@google.com470e71d2011-07-07 08:21:25 +0000321#ifdef WEBRTC_LINUX
phoglund@webrtc.orgec9c9422013-01-02 08:45:03 +0000322 prctl(PR_SET_NAME, (unsigned long)name_, 0, 0, 0);
pwestin@webrtc.orgb54d7272012-01-11 08:28:04 +0000323#endif
phoglund@webrtc.orgec9c9422013-01-02 08:45:03 +0000324 WEBRTC_TRACE(kTraceStateInfo, kTraceUtility, -1,
325 "Thread with name:%s started ", name_);
326 } else {
327 WEBRTC_TRACE(kTraceStateInfo, kTraceUtility, -1,
328 "Thread without name started");
329 }
330 bool alive = true;
henrike@webrtc.orga3e6bec2013-01-18 16:39:21 +0000331 bool run = true;
332 while (alive) {
333 run = run_function_(obj_);
334 CriticalSectionScoped cs(crit_state_);
335 if (!run) {
336 alive_ = false;
niklase@google.com470e71d2011-07-07 08:21:25 +0000337 }
henrike@webrtc.orga3e6bec2013-01-18 16:39:21 +0000338 alive = alive_;
339 }
niklase@google.com470e71d2011-07-07 08:21:25 +0000340
phoglund@webrtc.orgec9c9422013-01-02 08:45:03 +0000341 if (set_thread_name_) {
342 // Don't set the name for the trace thread because it may cause a
343 // deadlock. TODO(hellner) there should be a better solution than
344 // coupling the thread and the trace class like this.
345 if (strcmp(name_, "Trace")) {
346 WEBRTC_TRACE(kTraceStateInfo, kTraceUtility, -1,
347 "Thread with name:%s stopped", name_);
niklase@google.com470e71d2011-07-07 08:21:25 +0000348 }
phoglund@webrtc.orgec9c9422013-01-02 08:45:03 +0000349 } else {
350 WEBRTC_TRACE(kTraceStateInfo, kTraceUtility, -1,
351 "Thread without name stopped");
352 }
353 {
354 CriticalSectionScoped cs(crit_state_);
355 dead_ = true;
356 }
niklase@google.com470e71d2011-07-07 08:21:25 +0000357}
phoglund@webrtc.orgec9c9422013-01-02 08:45:03 +0000358
niklase@google.com470e71d2011-07-07 08:21:25 +0000359} // namespace webrtc