/*
* 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