blob: e7fe2fecf2070be7cebfaba1ec00760b9481889f [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);
phoglund@webrtc.orgec9c9422013-01-02 08:45:03 +0000181 event_->Reset();
182 // If pthread_create was successful, a thread was created and is running.
183 // Don't return false if it was successful since if there are any other
184 // failures the state will be: thread was started but not configured as
185 // asked for. However, the caller of this API will assume that a false
186 // return value means that the thread never started.
187 result |= pthread_create(&thread_, &attr_, &StartThread, this);
188 if (result != 0) {
189 return false;
190 }
henrike@webrtc.orga3e6bec2013-01-18 16:39:21 +0000191 {
192 CriticalSectionScoped cs(crit_state_);
193 dead_ = false;
194 }
niklase@google.com470e71d2011-07-07 08:21:25 +0000195
phoglund@webrtc.orgec9c9422013-01-02 08:45:03 +0000196 // Wait up to 10 seconds for the OS to call the callback function. Prevents
197 // race condition if Stop() is called too quickly after start.
198 if (kEventSignaled != event_->Wait(WEBRTC_EVENT_10_SEC)) {
199 WEBRTC_TRACE(kTraceError, kTraceUtility, -1,
200 "posix thread event never triggered");
phoglund@webrtc.orgec9c9422013-01-02 08:45:03 +0000201 // Timed out. Something went wrong.
phoglund@webrtc.orgec9c9422013-01-02 08:45:03 +0000202 return true;
203 }
niklase@google.com470e71d2011-07-07 08:21:25 +0000204
205#if HAS_THREAD_ID
phoglund@webrtc.orgec9c9422013-01-02 08:45:03 +0000206 thread_id = static_cast<unsigned int>(thread_);
niklase@google.com470e71d2011-07-07 08:21:25 +0000207#endif
phoglund@webrtc.orgec9c9422013-01-02 08:45:03 +0000208 return true;
niklase@google.com470e71d2011-07-07 08:21:25 +0000209}
210
phoglund@webrtc.orgec9c9422013-01-02 08:45:03 +0000211void ThreadPosix::SetNotAlive() {
212 CriticalSectionScoped cs(crit_state_);
213 alive_ = false;
214}
215
216bool ThreadPosix::Stop() {
217 bool dead = false;
218 {
219 CriticalSectionScoped cs(crit_state_);
220 alive_ = false;
221 dead = dead_;
222 }
223
224 // TODO(hellner) why not use an event here?
225 // Wait up to 10 seconds for the thread to terminate
henrike@webrtc.orga3e6bec2013-01-18 16:39:21 +0000226 for (int i = 0; i < 1000 && !dead; ++i) {
hta@webrtc.org2cec0b12013-03-21 14:02:29 +0000227 SleepMs(10);
phoglund@webrtc.orgec9c9422013-01-02 08:45:03 +0000228 {
229 CriticalSectionScoped cs(crit_state_);
230 dead = dead_;
231 }
232 }
233 if (dead) {
234 return true;
235 } else {
niklase@google.com470e71d2011-07-07 08:21:25 +0000236 return false;
phoglund@webrtc.orgec9c9422013-01-02 08:45:03 +0000237 }
niklase@google.com470e71d2011-07-07 08:21:25 +0000238}
239
phoglund@webrtc.orgec9c9422013-01-02 08:45:03 +0000240void ThreadPosix::Run() {
241 {
242 CriticalSectionScoped cs(crit_state_);
243 alive_ = true;
phoglund@webrtc.orgec9c9422013-01-02 08:45:03 +0000244 }
pwestin@webrtc.orgdf9866f2012-01-11 08:57:47 +0000245#if (defined(WEBRTC_LINUX) || defined(WEBRTC_ANDROID))
phoglund@webrtc.orgec9c9422013-01-02 08:45:03 +0000246 pid_ = GetThreadId();
pwestin@webrtc.orgdf9866f2012-01-11 08:57:47 +0000247#endif
phoglund@webrtc.orgec9c9422013-01-02 08:45:03 +0000248 // The event the Start() is waiting for.
249 event_->Set();
niklase@google.com470e71d2011-07-07 08:21:25 +0000250
phoglund@webrtc.orgec9c9422013-01-02 08:45:03 +0000251 if (set_thread_name_) {
niklase@google.com470e71d2011-07-07 08:21:25 +0000252#ifdef WEBRTC_LINUX
phoglund@webrtc.orgec9c9422013-01-02 08:45:03 +0000253 prctl(PR_SET_NAME, (unsigned long)name_, 0, 0, 0);
pwestin@webrtc.orgb54d7272012-01-11 08:28:04 +0000254#endif
phoglund@webrtc.orgec9c9422013-01-02 08:45:03 +0000255 WEBRTC_TRACE(kTraceStateInfo, kTraceUtility, -1,
256 "Thread with name:%s started ", name_);
257 } else {
258 WEBRTC_TRACE(kTraceStateInfo, kTraceUtility, -1,
259 "Thread without name started");
260 }
henrike@webrtc.orgea6c12e2014-09-29 18:25:27 +0000261
262#ifdef WEBRTC_THREAD_RR
263 const int policy = SCHED_RR;
264#else
265 const int policy = SCHED_FIFO;
266#endif
267 const int min_prio = sched_get_priority_min(policy);
268 const int max_prio = sched_get_priority_max(policy);
269 if ((min_prio == -1) || (max_prio == -1)) {
270 WEBRTC_TRACE(kTraceError, kTraceUtility, -1,
271 "unable to retreive min or max priority for threads");
272 }
273 if (max_prio - min_prio > 2) {
274 sched_param param;
275 param.sched_priority = ConvertToSystemPriority(prio_, min_prio, max_prio);
276 if (pthread_setschedparam(pthread_self(), policy, &param) != 0) {
277 WEBRTC_TRACE(
278 kTraceError, kTraceUtility, -1, "unable to set thread priority");
279 }
280 }
281
phoglund@webrtc.orgec9c9422013-01-02 08:45:03 +0000282 bool alive = true;
henrike@webrtc.orga3e6bec2013-01-18 16:39:21 +0000283 bool run = true;
284 while (alive) {
285 run = run_function_(obj_);
286 CriticalSectionScoped cs(crit_state_);
287 if (!run) {
288 alive_ = false;
niklase@google.com470e71d2011-07-07 08:21:25 +0000289 }
henrike@webrtc.orga3e6bec2013-01-18 16:39:21 +0000290 alive = alive_;
291 }
niklase@google.com470e71d2011-07-07 08:21:25 +0000292
phoglund@webrtc.orgec9c9422013-01-02 08:45:03 +0000293 if (set_thread_name_) {
294 // Don't set the name for the trace thread because it may cause a
295 // deadlock. TODO(hellner) there should be a better solution than
296 // coupling the thread and the trace class like this.
297 if (strcmp(name_, "Trace")) {
298 WEBRTC_TRACE(kTraceStateInfo, kTraceUtility, -1,
299 "Thread with name:%s stopped", name_);
niklase@google.com470e71d2011-07-07 08:21:25 +0000300 }
phoglund@webrtc.orgec9c9422013-01-02 08:45:03 +0000301 } else {
302 WEBRTC_TRACE(kTraceStateInfo, kTraceUtility, -1,
303 "Thread without name stopped");
304 }
305 {
306 CriticalSectionScoped cs(crit_state_);
307 dead_ = true;
308 }
niklase@google.com470e71d2011-07-07 08:21:25 +0000309}
phoglund@webrtc.orgec9c9422013-01-02 08:45:03 +0000310
pbos@webrtc.orgd900e8b2013-07-03 15:12:26 +0000311} // namespace webrtc