Home | History | Annotate | Download | only in source
      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 
     28 namespace webrtc {
     29 extern "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))
     39 static 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 
     49 ThreadWrapper* 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 
     66 ThreadPosix::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 
     86 int 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 
    111 ThreadPosix::~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
    121 bool ThreadPosix::Start(unsigned int& threadID)
    122 #else
    123 bool 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))
    195 bool 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.
    225 bool ThreadPosix::SetAffinity(const int* , const unsigned int)
    226 {
    227     return false;
    228 }
    229 #endif
    230 
    231 void ThreadPosix::SetNotAlive()
    232 {
    233     _alive = false;
    234 }
    235 
    236 bool 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 
    250 bool 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 
    273 void 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