blob: 1281c1b0d1c3296f6af7d5b57c312c7497f4265e [file] [log] [blame]
niklase@google.comf0779a22011-05-30 11:39:38 +00001/*
2 * Copyright (c) 2011 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 "thread_linux.h"
12
13#include <errno.h>
14#include <string.h> // strncpy
15#include <time.h> // nanosleep
16#include <unistd.h>
17#ifdef WEBRTC_LINUX
18#include <sys/types.h>
19#include <sched.h>
20#include <sys/syscall.h>
21#include <linux/unistd.h>
22#include <sys/prctl.h>
23#endif
24
25#include "event_wrapper.h"
26#include "trace.h"
27
28namespace webrtc {
29extern "C"
30{
31 static void* StartThread(void* lpParameter)
32 {
33 static_cast<ThreadLinux*>(lpParameter)->Run();
34 return 0;
35 }
36}
37
38#if (defined(WEBRTC_LINUX) && !defined(ANDROID))
39static pid_t gettid()
40{
41#if defined(__NR_gettid)
42 return syscall(__NR_gettid);
43#else
44 return -1;
45#endif
46}
47#endif
48
49ThreadWrapper* ThreadLinux::Create(ThreadRunFunction func, ThreadObj obj,
50 ThreadPriority prio, const char* threadName)
51{
52 ThreadLinux* ptr = new ThreadLinux(func, obj, prio, threadName);
53 if (!ptr)
54 {
55 return NULL;
56 }
57 const int error = ptr->Construct();
58 if (error)
59 {
60 delete ptr;
61 return NULL;
62 }
63 return ptr;
64}
65
66ThreadLinux::ThreadLinux(ThreadRunFunction func, ThreadObj obj,
67 ThreadPriority prio, const char* threadName)
68 : _runFunction(func),
69 _obj(obj),
70 _alive(false),
71 _dead(true),
72 _prio(prio),
73 _event(EventWrapper::Create()),
74 _setThreadName(false)
75{
76#ifdef WEBRTC_LINUX
77 _linuxPid = -1;
78#endif
79 if (threadName != NULL)
80 {
81 _setThreadName = true;
82 strncpy(_name, threadName, kThreadMaxNameLength);
83 }
84}
85
86int ThreadLinux::Construct()
87{
88 int result = 0;
89#if !defined(ANDROID)
90 // Enable immediate cancellation if requested, see Shutdown()
91 result = pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
92 if (result != 0)
93 {
94 return -1;
95 }
96 result = pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
97 if (result != 0)
98 {
99 return -1;
100 }
101#endif
102 result = pthread_attr_init(&_attr);
103 if (result != 0)
104 {
105 return -1;
106 }
107
108 return 0;
109}
110
111ThreadLinux::~ThreadLinux()
112{
113 pthread_attr_destroy(&_attr);
114 delete _event;
115}
116
117#define HAS_THREAD_ID !defined(MAC_IPHONE) && !defined(MAC_IPHONE_SIM) && \
118 !defined(WEBRTC_MAC) && !defined(WEBRTC_MAC_INTEL) && \
119 !defined(MAC_DYLIB) && !defined(MAC_INTEL_DYLIB)
120#if HAS_THREAD_ID
121bool ThreadLinux::Start(unsigned int& threadID)
122#else
123bool ThreadLinux::Start(unsigned int& /*threadID*/)
124#endif
125{
126 if (!_runFunction)
127 {
128 return false;
129 }
130 int result = pthread_attr_setdetachstate(&_attr, PTHREAD_CREATE_DETACHED);
131 // Set the stack stack size to 1M.
132 result |= pthread_attr_setstacksize(&_attr, 1024*1024);
133#ifdef WEBRTC_THREAD_RR
134 const int policy = SCHED_RR;
135#else
136 const int policy = SCHED_FIFO;
137#endif
138 _event->Reset();
139 result |= pthread_create(&_thread, &_attr, &StartThread, this);
140 if (result != 0)
141 {
142 return false;
143 }
144
145 // Wait up to 10 seconds for the OS to call the callback function. Prevents
146 // race condition if Stop() is called too quickly after start.
147 if (kEventSignaled != _event->Wait(WEBRTC_EVENT_10_SEC))
148 {
149 // Timed out. Something went wrong.
150 _runFunction = NULL;
151 return false;
152 }
153
154#if HAS_THREAD_ID
155 threadID = static_cast<unsigned int>(_thread);
156#endif
157 sched_param param;
158
159 const int minPrio = sched_get_priority_min(policy);
160 const int maxPrio = sched_get_priority_max(policy);
161 if ((minPrio == EINVAL) || (maxPrio == EINVAL))
162 {
163 return false;
164 }
165
166 switch (_prio)
167 {
168 case kLowPriority:
169 param.sched_priority = minPrio + 1;
170 break;
171 case kNormalPriority:
172 param.sched_priority = (minPrio + maxPrio) / 2;
173 break;
174 case kHighPriority:
175 param.sched_priority = maxPrio - 3;
176 break;
177 case kHighestPriority:
178 param.sched_priority = maxPrio - 2;
179 break;
180 case kRealtimePriority:
181 param.sched_priority = maxPrio - 1;
182 break;
183 default:
184 return false;
185 }
186 result = pthread_setschedparam(_thread, policy, &param);
187 if (result == EINVAL)
188 {
189 return false;
190 }
191 return true;
192}
193
194#if (defined(WEBRTC_LINUX) && !defined(ANDROID))
195bool ThreadLinux::SetAffinity(const int* processorNumbers,
196 const unsigned int amountOfProcessors)
197{
198 if (!processorNumbers || (amountOfProcessors == 0))
199 {
200 return false;
201 }
202
203 cpu_set_t mask;
204 CPU_ZERO(&mask);
205
206 for(unsigned int processor = 0;
207 processor < amountOfProcessors;
208 processor++)
209 {
210 CPU_SET(processorNumbers[processor], &mask);
211 }
212 const int result = sched_setaffinity(_linuxPid, (unsigned int)sizeof(mask),
213 &mask);
214 if (result != 0)
215 {
216 return false;
217
218 }
219 return true;
220}
221#else
222// NOTE: On Mac OS X, use the Thread affinity API in
223// /usr/include/mach/thread_policy.h: thread_policy_set and mach_thread_self()
224// instead of Linux gettid() syscall.
225bool ThreadLinux::SetAffinity(const int* , const unsigned int)
226{
227 return false;
228}
229#endif
230
231void ThreadLinux::SetNotAlive()
232{
233 _alive = false;
234}
235
236bool ThreadLinux::Shutdown()
237{
238#if !defined(ANDROID)
239 if (_thread && (0 != pthread_cancel(_thread)))
240 {
241 return false;
242 }
243
244 return true;
245#else
246 return false;
247#endif
248}
249
250bool ThreadLinux::Stop()
251{
252 _alive = false;
253
254 // TODO (hellner) why not use an event here?
255 // Wait up to 10 seconds for the thread to terminate
256 for (int i = 0; i < 1000 && !_dead; i++)
257 {
258 timespec t;
259 t.tv_sec = 0;
260 t.tv_nsec = 10*1000*1000;
261 nanosleep(&t, NULL);
262 }
263 if (_dead)
264 {
265 return true;
266 }
267 else
268 {
269 return false;
270 }
271}
272
273void ThreadLinux::Run()
274{
275 _alive = true;
276 _dead = false;
277#ifdef WEBRTC_LINUX
278 if(_linuxPid == -1)
279 {
280 _linuxPid = gettid();
281 }
282#endif
283 // The event the Start() is waiting for.
284 _event->Set();
285
286 if (_setThreadName)
287 {
288#ifdef WEBRTC_LINUX
289 WEBRTC_TRACE(kTraceStateInfo, kTraceUtility,-1,
290 "Thread with id:%d name:%s started ", _linuxPid, _name);
291 prctl(PR_SET_NAME, (unsigned long)_name, 0, 0, 0);
292#else
293 WEBRTC_TRACE(kTraceStateInfo, kTraceUtility,-1,
294 "Thread with name:%s started ", _name);
295#endif
296 }else
297 {
298#ifdef WEBRTC_LINUX
299 WEBRTC_TRACE(kTraceStateInfo, kTraceUtility, -1,
300 "Thread with id:%d without name started", _linuxPid);
301#else
302 WEBRTC_TRACE(kTraceStateInfo, kTraceUtility, -1,
303 "Thread without name started");
304#endif
305 }
306 do
307 {
308 if (_runFunction)
309 {
310 if (!_runFunction(_obj))
311 {
312 _alive = false;
313 }
314 }
315 else
316 {
317 _alive = false;
318 }
319 }
320 while (_alive);
321
322 if (_setThreadName)
323 {
324 // Don't set the name for the trace thread because it may cause a
325 // deadlock. TODO (hellner) there should be a better solution than
326 // coupling the thread and the trace class like this.
327 if (strcmp(_name, "Trace"))
328 {
329 WEBRTC_TRACE(kTraceStateInfo, kTraceUtility,-1,
330 "Thread with name:%s stopped", _name);
331 }
332 }
333 else
334 {
335 WEBRTC_TRACE(kTraceStateInfo, kTraceUtility,-1,
336 "Thread without name stopped");
337 }
338 _dead = true;
339}
340} // namespace webrtc