blob: 8a24ae58229540f92b83817677e8c4e583ad8f48 [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
niklase@google.com470e71d2011-07-07 08:21:25 +000052#include <unistd.h>
53#ifdef WEBRTC_LINUX
niklase@google.com470e71d2011-07-07 08:21:25 +000054#include <linux/unistd.h>
pbos@webrtc.orgacaf3a12013-05-27 15:07:45 +000055#include <sched.h>
niklase@google.com470e71d2011-07-07 08:21:25 +000056#include <sys/prctl.h>
pbos@webrtc.orgacaf3a12013-05-27 15:07:45 +000057#include <sys/syscall.h>
58#include <sys/types.h>
niklase@google.com470e71d2011-07-07 08:21:25 +000059#endif
60
phoglund@webrtc.orgec9c9422013-01-02 08:45:03 +000061#include "webrtc/system_wrappers/interface/critical_section_wrapper.h"
62#include "webrtc/system_wrappers/interface/event_wrapper.h"
hta@webrtc.org2cec0b12013-03-21 14:02:29 +000063#include "webrtc/system_wrappers/interface/sleep.h"
phoglund@webrtc.orgec9c9422013-01-02 08:45:03 +000064#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));
henrike@webrtc.org57e6b812013-01-29 15:08:29 +0000141#elif defined(WEBRTC_MAC) || defined(WEBRTC_IOS)
142 return pthread_mach_thread_np(pthread_self());
pwestin@webrtc.orgb54d7272012-01-11 08:28:04 +0000143#else
144 return reinterpret_cast<uint32_t>(pthread_self());
145#endif
146}
147
phoglund@webrtc.orgec9c9422013-01-02 08:45:03 +0000148int ThreadPosix::Construct() {
149 int result = 0;
leozwang@google.comb3527002011-07-26 17:29:38 +0000150#if !defined(WEBRTC_ANDROID)
phoglund@webrtc.orgec9c9422013-01-02 08:45:03 +0000151 // 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 }
niklase@google.com470e71d2011-07-07 08:21:25 +0000160#endif
phoglund@webrtc.orgec9c9422013-01-02 08:45:03 +0000161 result = pthread_attr_init(&attr_);
162 if (result != 0) {
163 return -1;
164 }
165 return 0;
niklase@google.com470e71d2011-07-07 08:21:25 +0000166}
167
phoglund@webrtc.orgec9c9422013-01-02 08:45:03 +0000168ThreadPosix::~ThreadPosix() {
169 pthread_attr_destroy(&attr_);
170 delete event_;
171 delete crit_state_;
niklase@google.com470e71d2011-07-07 08:21:25 +0000172}
173
sjlee@webrtc.org414fa7f2012-09-11 17:25:46 +0000174#define HAS_THREAD_ID !defined(WEBRTC_IOS) && !defined(WEBRTC_MAC)
phoglund@webrtc.orgec9c9422013-01-02 08:45:03 +0000175
176bool ThreadPosix::Start(unsigned int& thread_id)
niklase@google.com470e71d2011-07-07 08:21:25 +0000177{
phoglund@webrtc.orgec9c9422013-01-02 08:45:03 +0000178 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);
niklase@google.com470e71d2011-07-07 08:21:25 +0000181#ifdef WEBRTC_THREAD_RR
phoglund@webrtc.orgec9c9422013-01-02 08:45:03 +0000182 const int policy = SCHED_RR;
niklase@google.com470e71d2011-07-07 08:21:25 +0000183#else
phoglund@webrtc.orgec9c9422013-01-02 08:45:03 +0000184 const int policy = SCHED_FIFO;
niklase@google.com470e71d2011-07-07 08:21:25 +0000185#endif
phoglund@webrtc.orgec9c9422013-01-02 08:45:03 +0000186 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 }
henrike@webrtc.orga3e6bec2013-01-18 16:39:21 +0000196 {
197 CriticalSectionScoped cs(crit_state_);
198 dead_ = false;
199 }
niklase@google.com470e71d2011-07-07 08:21:25 +0000200
phoglund@webrtc.orgec9c9422013-01-02 08:45:03 +0000201 // 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");
phoglund@webrtc.orgec9c9422013-01-02 08:45:03 +0000206 // Timed out. Something went wrong.
phoglund@webrtc.orgec9c9422013-01-02 08:45:03 +0000207 return true;
208 }
niklase@google.com470e71d2011-07-07 08:21:25 +0000209
210#if HAS_THREAD_ID
phoglund@webrtc.orgec9c9422013-01-02 08:45:03 +0000211 thread_id = static_cast<unsigned int>(thread_);
niklase@google.com470e71d2011-07-07 08:21:25 +0000212#endif
phoglund@webrtc.orgec9c9422013-01-02 08:45:03 +0000213 sched_param param;
niklase@google.com470e71d2011-07-07 08:21:25 +0000214
phoglund@webrtc.orgec9c9422013-01-02 08:45:03 +0000215 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");
niklase@google.com470e71d2011-07-07 08:21:25 +0000221 return true;
phoglund@webrtc.orgec9c9422013-01-02 08:45:03 +0000222 }
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;
niklase@google.com470e71d2011-07-07 08:21:25 +0000234}
235
leozwang@webrtc.org0a272eb2012-02-15 22:35:29 +0000236// 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)))
phoglund@webrtc.orgec9c9422013-01-02 08:45:03 +0000239bool ThreadPosix::SetAffinity(const int* processor_numbers,
240 const unsigned int amount_of_processors) {
241 if (!processor_numbers || (amount_of_processors == 0)) {
pwestin@webrtc.orgb54d7272012-01-11 08:28:04 +0000242 return false;
243 }
244 cpu_set_t mask;
245 CPU_ZERO(&mask);
niklase@google.com470e71d2011-07-07 08:21:25 +0000246
pwestin@webrtc.orgb54d7272012-01-11 08:28:04 +0000247 for (unsigned int processor = 0;
phoglund@webrtc.orgec9c9422013-01-02 08:45:03 +0000248 processor < amount_of_processors;
249 ++processor) {
250 CPU_SET(processor_numbers[processor], &mask);
pwestin@webrtc.orgb54d7272012-01-11 08:28:04 +0000251 }
252#if defined(WEBRTC_ANDROID)
253 // Android.
254 const int result = syscall(__NR_sched_setaffinity,
phoglund@webrtc.orgec9c9422013-01-02 08:45:03 +0000255 pid_,
pwestin@webrtc.orgb54d7272012-01-11 08:28:04 +0000256 sizeof(mask),
257 &mask);
258#else
259 // "Normal" Linux.
phoglund@webrtc.orgec9c9422013-01-02 08:45:03 +0000260 const int result = sched_setaffinity(pid_,
pwestin@webrtc.orgb54d7272012-01-11 08:28:04 +0000261 sizeof(mask),
262 &mask);
263#endif
264 if (result != 0) {
265 return false;
266 }
267 return true;
niklase@google.com470e71d2011-07-07 08:21:25 +0000268}
pwestin@webrtc.orgb54d7272012-01-11 08:28:04 +0000269
niklase@google.com470e71d2011-07-07 08:21:25 +0000270#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.
phoglund@webrtc.orgec9c9422013-01-02 08:45:03 +0000274bool 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
henrike@webrtc.orga3e6bec2013-01-18 16:39:21 +0000294 for (int i = 0; i < 1000 && !dead; ++i) {
hta@webrtc.org2cec0b12013-03-21 14:02:29 +0000295 SleepMs(10);
phoglund@webrtc.orgec9c9422013-01-02 08:45:03 +0000296 {
297 CriticalSectionScoped cs(crit_state_);
298 dead = dead_;
299 }
300 }
301 if (dead) {
302 return true;
303 } else {
niklase@google.com470e71d2011-07-07 08:21:25 +0000304 return false;
phoglund@webrtc.orgec9c9422013-01-02 08:45:03 +0000305 }
niklase@google.com470e71d2011-07-07 08:21:25 +0000306}
307
phoglund@webrtc.orgec9c9422013-01-02 08:45:03 +0000308void ThreadPosix::Run() {
309 {
310 CriticalSectionScoped cs(crit_state_);
311 alive_ = true;
phoglund@webrtc.orgec9c9422013-01-02 08:45:03 +0000312 }
pwestin@webrtc.orgdf9866f2012-01-11 08:57:47 +0000313#if (defined(WEBRTC_LINUX) || defined(WEBRTC_ANDROID))
phoglund@webrtc.orgec9c9422013-01-02 08:45:03 +0000314 pid_ = GetThreadId();
pwestin@webrtc.orgdf9866f2012-01-11 08:57:47 +0000315#endif
phoglund@webrtc.orgec9c9422013-01-02 08:45:03 +0000316 // The event the Start() is waiting for.
317 event_->Set();
niklase@google.com470e71d2011-07-07 08:21:25 +0000318
phoglund@webrtc.orgec9c9422013-01-02 08:45:03 +0000319 if (set_thread_name_) {
niklase@google.com470e71d2011-07-07 08:21:25 +0000320#ifdef WEBRTC_LINUX
phoglund@webrtc.orgec9c9422013-01-02 08:45:03 +0000321 prctl(PR_SET_NAME, (unsigned long)name_, 0, 0, 0);
pwestin@webrtc.orgb54d7272012-01-11 08:28:04 +0000322#endif
phoglund@webrtc.orgec9c9422013-01-02 08:45:03 +0000323 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;
henrike@webrtc.orga3e6bec2013-01-18 16:39:21 +0000330 bool run = true;
331 while (alive) {
332 run = run_function_(obj_);
333 CriticalSectionScoped cs(crit_state_);
334 if (!run) {
335 alive_ = false;
niklase@google.com470e71d2011-07-07 08:21:25 +0000336 }
henrike@webrtc.orga3e6bec2013-01-18 16:39:21 +0000337 alive = alive_;
338 }
niklase@google.com470e71d2011-07-07 08:21:25 +0000339
phoglund@webrtc.orgec9c9422013-01-02 08:45:03 +0000340 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_);
niklase@google.com470e71d2011-07-07 08:21:25 +0000347 }
phoglund@webrtc.orgec9c9422013-01-02 08:45:03 +0000348 } else {
349 WEBRTC_TRACE(kTraceStateInfo, kTraceUtility, -1,
350 "Thread without name stopped");
351 }
352 {
353 CriticalSectionScoped cs(crit_state_);
354 dead_ = true;
355 }
niklase@google.com470e71d2011-07-07 08:21:25 +0000356}
phoglund@webrtc.orgec9c9422013-01-02 08:45:03 +0000357
pbos@webrtc.orgd900e8b2013-07-03 15:12:26 +0000358} // namespace webrtc