/* * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. * * Use of this source code is governed by a BSD-style license * that can be found in the LICENSE file in the root of the source * tree. An additional intellectual property rights grant can be found * in the file PATENTS. All contributing project authors may * be found in the AUTHORS file in the root of the source tree. */ #include "thread_linux.h" #include <errno.h> #include <string.h> // strncpy #include <time.h> // nanosleep #include <unistd.h> #ifdef WEBRTC_LINUX #include <sys/types.h> #include <sched.h> #include <sys/syscall.h> #include <linux/unistd.h> #include <sys/prctl.h> #endif #include "event_wrapper.h" #include "trace.h" namespace webrtc { extern "C" { static void* StartThread(void* lpParameter) { static_cast<ThreadLinux*>(lpParameter)->Run(); return 0; } } #if (defined(WEBRTC_LINUX) && !defined(ANDROID)) static pid_t gettid() { #if defined(__NR_gettid) return syscall(__NR_gettid); #else return -1; #endif } #endif ThreadWrapper* ThreadLinux::Create(ThreadRunFunction func, ThreadObj obj, ThreadPriority prio, const char* threadName) { ThreadLinux* ptr = new ThreadLinux(func, obj, prio, threadName); if (!ptr) { return NULL; } const int error = ptr->Construct(); if (error) { delete ptr; return NULL; } return ptr; } ThreadLinux::ThreadLinux(ThreadRunFunction func, ThreadObj obj, ThreadPriority prio, const char* threadName) : _runFunction(func), _obj(obj), _alive(false), _dead(true), _prio(prio), _event(EventWrapper::Create()), _setThreadName(false) { #ifdef WEBRTC_LINUX _linuxPid = -1; #endif if (threadName != NULL) { _setThreadName = true; strncpy(_name, threadName, kThreadMaxNameLength); } } int ThreadLinux::Construct() { int result = 0; #if !defined(ANDROID) // Enable immediate cancellation if requested, see Shutdown() result = pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); if (result != 0) { return -1; } result = pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL); if (result != 0) { return -1; } #endif result = pthread_attr_init(&_attr); if (result != 0) { return -1; } return 0; } ThreadLinux::~ThreadLinux() { pthread_attr_destroy(&_attr); delete _event; } #define HAS_THREAD_ID !defined(MAC_IPHONE) && !defined(MAC_IPHONE_SIM) && \ !defined(WEBRTC_MAC) && !defined(WEBRTC_MAC_INTEL) && \ !defined(MAC_DYLIB) && !defined(MAC_INTEL_DYLIB) #if HAS_THREAD_ID bool ThreadLinux::Start(unsigned int& threadID) #else bool ThreadLinux::Start(unsigned int& /*threadID*/) #endif { if (!_runFunction) { return false; } int result = pthread_attr_setdetachstate(&_attr, PTHREAD_CREATE_DETACHED); // Set the stack stack size to 1M. result |= pthread_attr_setstacksize(&_attr, 1024*1024); #ifdef WEBRTC_THREAD_RR const int policy = SCHED_RR; #else const int policy = SCHED_FIFO; #endif _event->Reset(); result |= pthread_create(&_thread, &_attr, &StartThread, this); if (result != 0) { return false; } // Wait up to 10 seconds for the OS to call the callback function. Prevents // race condition if Stop() is called too quickly after start. if (kEventSignaled != _event->Wait(WEBRTC_EVENT_10_SEC)) { // Timed out. Something went wrong. _runFunction = NULL; return false; } #if HAS_THREAD_ID threadID = static_cast<unsigned int>(_thread); #endif sched_param param; const int minPrio = sched_get_priority_min(policy); const int maxPrio = sched_get_priority_max(policy); if ((minPrio == EINVAL) || (maxPrio == EINVAL)) { return false; } switch (_prio) { case kLowPriority: param.sched_priority = minPrio + 1; break; case kNormalPriority: param.sched_priority = (minPrio + maxPrio) / 2; break; case kHighPriority: param.sched_priority = maxPrio - 3; break; case kHighestPriority: param.sched_priority = maxPrio - 2; break; case kRealtimePriority: param.sched_priority = maxPrio - 1; break; default: return false; } result = pthread_setschedparam(_thread, policy, ¶m); if (result == EINVAL) { return false; } return true; } #if (defined(WEBRTC_LINUX) && !defined(ANDROID)) bool ThreadLinux::SetAffinity(const int* processorNumbers, const unsigned int amountOfProcessors) { if (!processorNumbers || (amountOfProcessors == 0)) { return false; } cpu_set_t mask; CPU_ZERO(&mask); for(unsigned int processor = 0; processor < amountOfProcessors; processor++) { CPU_SET(processorNumbers[processor], &mask); } const int result = sched_setaffinity(_linuxPid, (unsigned int)sizeof(mask), &mask); if (result != 0) { return false; } return true; } #else // NOTE: On Mac OS X, use the Thread affinity API in // /usr/include/mach/thread_policy.h: thread_policy_set and mach_thread_self() // instead of Linux gettid() syscall. bool ThreadLinux::SetAffinity(const int* , const unsigned int) { return false; } #endif void ThreadLinux::SetNotAlive() { _alive = false; } bool ThreadLinux::Shutdown() { #if !defined(ANDROID) if (_thread && (0 != pthread_cancel(_thread))) { return false; } return true; #else return false; #endif } bool ThreadLinux::Stop() { _alive = false; // TODO (hellner) why not use an event here? // Wait up to 10 seconds for the thread to terminate for (int i = 0; i < 1000 && !_dead; i++) { timespec t; t.tv_sec = 0; t.tv_nsec = 10*1000*1000; nanosleep(&t, NULL); } if (_dead) { return true; } else { return false; } } void ThreadLinux::Run() { _alive = true; _dead = false; #ifdef WEBRTC_LINUX if(_linuxPid == -1) { _linuxPid = gettid(); } #endif // The event the Start() is waiting for. _event->Set(); if (_setThreadName) { #ifdef WEBRTC_LINUX WEBRTC_TRACE(kTraceStateInfo, kTraceUtility,-1, "Thread with id:%d name:%s started ", _linuxPid, _name); prctl(PR_SET_NAME, (unsigned long)_name, 0, 0, 0); #else WEBRTC_TRACE(kTraceStateInfo, kTraceUtility,-1, "Thread with name:%s started ", _name); #endif }else { #ifdef WEBRTC_LINUX WEBRTC_TRACE(kTraceStateInfo, kTraceUtility, -1, "Thread with id:%d without name started", _linuxPid); #else WEBRTC_TRACE(kTraceStateInfo, kTraceUtility, -1, "Thread without name started"); #endif } do { if (_runFunction) { if (!_runFunction(_obj)) { _alive = false; } } else { _alive = false; } } while (_alive); if (_setThreadName) { // Don't set the name for the trace thread because it may cause a // deadlock. TODO (hellner) there should be a better solution than // coupling the thread and the trace class like this. if (strcmp(_name, "Trace")) { WEBRTC_TRACE(kTraceStateInfo, kTraceUtility,-1, "Thread with name:%s stopped", _name); } } else { WEBRTC_TRACE(kTraceStateInfo, kTraceUtility,-1, "Thread without name stopped"); } _dead = true; } } // namespace webrtc