/* * Copyright (c) 2012 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. */ // The state of a thread is controlled by the two member variables // _alive and _dead. // _alive represents the state the thread has been ordered to achieve. // It is set to true by the thread at startup, and is set to false by // other threads, using SetNotAlive() and Stop(). // _dead represents the state the thread has achieved. // It is written by the thread encapsulated by this class only // (except at init). It is read only by the Stop() method. // The Run() method fires _event when it's started; this ensures that the // Start() method does not continue until after _dead is false. // This protects against premature Stop() calls from the creator thread, but // not from other threads. // Their transitions and states: // _alive _dead Set by // false true Constructor // true false Run() method entry // false any Run() method runFunction failure // any false Run() method exit (happens only with _alive false) // false any SetNotAlive // false any Stop Stop waits for _dead to become true. // // Summarized a different way: // Variable Writer Reader // _alive Constructor(false) Run.loop // Run.start(true) // Run.fail(false) // SetNotAlive(false) // Stop(false) // // _dead Constructor(true) Stop.loop // Run.start(false) // Run.exit(true) #include "thread_posix.h" #include #include #include #include // strncpy #include // nanosleep #include #ifdef WEBRTC_LINUX #include #include #include #include #include #endif #include "system_wrappers/interface/critical_section_wrapper.h" #include "system_wrappers/interface/event_wrapper.h" #include "system_wrappers/interface/trace.h" namespace webrtc { int ConvertToSystemPriority(ThreadPriority priority, int minPrio, int maxPrio) { assert(maxPrio - minPrio > 2); const int topPrio = maxPrio - 1; const int lowPrio = minPrio + 1; switch (priority) { case kLowPriority: return lowPrio; case kNormalPriority: // The -1 ensures that the kHighPriority is always greater or equal to // kNormalPriority. return (lowPrio + topPrio - 1) / 2; case kHighPriority: return std::max(topPrio - 2, lowPrio); case kHighestPriority: return std::max(topPrio - 1, lowPrio); case kRealtimePriority: return topPrio; } assert(false); return lowPrio; } extern "C" { static void* StartThread(void* lpParameter) { static_cast(lpParameter)->Run(); return 0; } } ThreadWrapper* ThreadPosix::Create(ThreadRunFunction func, ThreadObj obj, ThreadPriority prio, const char* threadName) { ThreadPosix* ptr = new ThreadPosix(func, obj, prio, threadName); if (!ptr) { return NULL; } const int error = ptr->Construct(); if (error) { delete ptr; return NULL; } return ptr; } ThreadPosix::ThreadPosix(ThreadRunFunction func, ThreadObj obj, ThreadPriority prio, const char* threadName) : _runFunction(func), _obj(obj), _crit_state(CriticalSectionWrapper::CreateCriticalSection()), _alive(false), _dead(true), _prio(prio), _event(EventWrapper::Create()), _name(), _setThreadName(false), #if (defined(WEBRTC_LINUX) || defined(WEBRTC_ANDROID)) _pid(-1), #endif _attr(), _thread(0) { if (threadName != NULL) { _setThreadName = true; strncpy(_name, threadName, kThreadMaxNameLength); _name[kThreadMaxNameLength - 1] = '\0'; } } uint32_t ThreadWrapper::GetThreadId() { #if defined(WEBRTC_ANDROID) || defined(WEBRTC_LINUX) return static_cast(syscall(__NR_gettid)); #else return reinterpret_cast(pthread_self()); #endif } int ThreadPosix::Construct() { int result = 0; #if !defined(WEBRTC_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; } ThreadPosix::~ThreadPosix() { pthread_attr_destroy(&_attr); delete _event; delete _crit_state; } #define HAS_THREAD_ID !defined(WEBRTC_IOS) && !defined(WEBRTC_MAC) #if HAS_THREAD_ID bool ThreadPosix::Start(unsigned int& threadID) #else bool ThreadPosix::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(); // If pthread_create was successful, a thread was created and is running. // Don't return false if it was successful since if there are any other // failures the state will be: thread was started but not configured as // asked for. However, the caller of this API will assume that a false // return value means that the thread never started. 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)) { WEBRTC_TRACE(kTraceError, kTraceUtility, -1, "posix thread event never triggered"); // Timed out. Something went wrong. _runFunction = NULL; return true; } #if HAS_THREAD_ID threadID = static_cast(_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)) { WEBRTC_TRACE(kTraceError, kTraceUtility, -1, "unable to retreive min or max priority for threads"); return true; } if (maxPrio - minPrio <= 2) { // There is no room for setting priorities with any granularity. return true; } param.sched_priority = ConvertToSystemPriority(_prio, minPrio, maxPrio); result = pthread_setschedparam(_thread, policy, ¶m); if (result == EINVAL) { WEBRTC_TRACE(kTraceError, kTraceUtility, -1, "unable to set thread priority"); } return true; } // CPU_ZERO and CPU_SET are not available in NDK r7, so disable // SetAffinity on Android for now. #if (defined(WEBRTC_LINUX) && (!defined(WEBRTC_ANDROID))) bool ThreadPosix::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); } #if defined(WEBRTC_ANDROID) // Android. const int result = syscall(__NR_sched_setaffinity, _pid, sizeof(mask), &mask); #else // "Normal" Linux. const int result = sched_setaffinity(_pid, sizeof(mask), &mask); #endif 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 ThreadPosix::SetAffinity(const int* , const unsigned int) { return false; } #endif void ThreadPosix::SetNotAlive() { CriticalSectionScoped cs(_crit_state); _alive = false; } bool ThreadPosix::Stop() { bool dead = false; { CriticalSectionScoped cs(_crit_state); _alive = false; dead = _dead; } // 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); { CriticalSectionScoped cs(_crit_state); dead = _dead; } } if (dead) { return true; } else { return false; } } void ThreadPosix::Run() { { CriticalSectionScoped cs(_crit_state); _alive = true; _dead = false; } #if (defined(WEBRTC_LINUX) || defined(WEBRTC_ANDROID)) _pid = GetThreadId(); #endif // The event the Start() is waiting for. _event->Set(); if (_setThreadName) { #ifdef WEBRTC_LINUX prctl(PR_SET_NAME, (unsigned long)_name, 0, 0, 0); #endif WEBRTC_TRACE(kTraceStateInfo, kTraceUtility, -1, "Thread with name:%s started ", _name); } else { WEBRTC_TRACE(kTraceStateInfo, kTraceUtility, -1, "Thread without name started"); } bool alive = true; do { if (_runFunction) { if (!_runFunction(_obj)) { alive = false; } } else { alive = false; } { CriticalSectionScoped cs(_crit_state); if (!alive) { _alive = false; } alive = _alive; } } 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"); } { CriticalSectionScoped cs(_crit_state); _dead = true; } } } // namespace webrtc