1/*
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_posix.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<ThreadPosix*>(lpParameter)->Run();
34        return 0;
35    }
36}
37
38#if (defined(WEBRTC_LINUX) && !defined(WEBRTC_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* ThreadPosix::Create(ThreadRunFunction func, ThreadObj obj,
50                                   ThreadPriority prio, const char* threadName)
51{
52    ThreadPosix* ptr = new ThreadPosix(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
66ThreadPosix::ThreadPosix(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 ThreadPosix::Construct()
87{
88    int result = 0;
89#if !defined(WEBRTC_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
111ThreadPosix::~ThreadPosix()
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 ThreadPosix::Start(unsigned int& threadID)
122#else
123bool ThreadPosix::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(WEBRTC_ANDROID))
195bool ThreadPosix::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 ThreadPosix::SetAffinity(const int* , const unsigned int)
226{
227    return false;
228}
229#endif
230
231void ThreadPosix::SetNotAlive()
232{
233    _alive = false;
234}
235
236bool ThreadPosix::Shutdown()
237{
238#if !defined(WEBRTC_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 ThreadPosix::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 ThreadPosix::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
341