mitsuba/src/libcore/thread.cpp

285 lines
6.8 KiB
C++

/*
This file is part of Mitsuba, a physically based rendering system.
Copyright (c) 2007-2010 by Wenzel Jakob and others.
Mitsuba is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License Version 3
as published by the Free Software Foundation.
Mitsuba is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <mitsuba/core/lock.h>
#include <mitsuba/core/fresolver.h>
#include <errno.h>
MTS_NAMESPACE_BEGIN
/**
* Dummy class to associate a thread identity with the main thread
*/
class MainThread : public Thread {
public:
MainThread() : Thread("main") {
}
virtual void run() {
Log(EError, "The main thread is already running!");
}
MTS_DECLARE_CLASS()
protected:
virtual ~MainThread() { }
};
ThreadLocal<Thread> *Thread::m_self = NULL;
#if defined(__LINUX__) || defined(__OSX__)
int Thread::m_idCounter;
ref<Mutex> Thread::m_idMutex;
#endif
#if defined(__LINUX__)
int __thread Thread::m_id;
#endif
Thread::Thread(const std::string &name, unsigned int stackSize)
: m_name(name), m_stackSize(stackSize), m_running(false), m_joined(false),
m_priority(ENormalPriority), m_critical(false) {
m_joinMutex = new Mutex();
memset(&m_thread, 0, sizeof(pthread_t));
}
void Thread::start() {
if (m_running)
Log(EError, "Thread is already running!");
if (!m_self)
Log(EError, "Threading has not been initialized!");
Log(EDebug, "Spawning thread \"%s\"", m_name.c_str());
/* Configure thread attributes */
pthread_attr_t attr;
pthread_attr_init(&attr);
if (m_stackSize != 0)
pthread_attr_setstacksize(&attr, m_stackSize);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
m_parent = Thread::getThread();
/* Inherit the parent thread's logger if none was set */
if (!m_logger)
m_logger = m_parent->getLogger();
/* Inherit the parent thread's file resolver if none was set */
if (!m_fresolver)
m_fresolver = m_parent->getFileResolver();
m_running = true;
m_joined = false;
incRef();
if (pthread_create(&m_thread, &attr, &Thread::dispatch, this))
Log(EError, "Could not create thread!");
}
bool Thread::setPriority(EThreadPriority priority) {
m_priority = priority;
if (!m_running)
return true;
Float factor;
switch (priority) {
case EIdlePriority: factor = 0.0f; break;
case ELowestPriority: factor = 0.2f; break;
case ELowPriority: factor = 0.4f; break;
case EHighPriority: factor = 0.6f; break;
case EHighestPriority: factor = 0.8f; break;
case ERealtimePriority: factor = 1.0f; break;
default: factor = 0.0f; break;
}
struct sched_param param;
int policy;
int retval = pthread_getschedparam(m_thread, &policy, &param);
if (retval) {
Log(EWarn, "Error in pthread_getschedparam(): %s!", strerror(retval));
return false;
}
int min = sched_get_priority_min(policy);
int max = sched_get_priority_max(policy);
if (min == max) {
Log(EWarn, "Could not adjust the thread priority -- valid range is zero!");
return false;
}
param.sched_priority = (int) (min + (max-min)*factor);
retval = pthread_setschedparam(m_thread, policy, &param);
if (retval) {
Log(EWarn, "Could not adjust the thread priority to %i: %s!", param.sched_priority, strerror(retval));
return false;
}
return true;
}
void *Thread::dispatch(void *par) {
Thread *thread = static_cast<Thread *>(par);
Thread::m_self->set(thread);
if (thread->getPriority() != ENormalPriority)
thread->setPriority(thread->getPriority());
#if defined(__OSX__)
m_idMutex->lock();
thread->m_id = ++m_idCounter;
m_idMutex->unlock();
#elif defined(__LINUX__)
m_idMutex->lock();
m_id = ++m_idCounter;
m_idMutex->unlock();
#endif
try {
thread->run();
} catch (std::exception &e) {
Log(EWarn, "Fatal error: uncaught exception: \"%s\"", e.what());
if (thread->m_critical)
_exit(-1);
} catch (...) {
Log(EWarn, "Fatal error - uncaught exception (unknown type)");
if (thread->m_critical)
_exit(-1);
}
thread->exit();
return NULL;
}
void Thread::join() {
/* Only one call to join() */
m_joinMutex->lock();
if (m_joined) {
m_joinMutex->unlock();
return;
}
m_joinMutex->unlock();
int retval = pthread_join(m_thread, NULL);
switch (retval) {
case EINVAL: Log(EError, "Thread::join() - the thread is not joinable");
case ESRCH:
Log(EWarn, "Thread::join() - the thread does not exist");
break;
case EDEADLK: Log(EError, "Thread::join() - a deadlock was detected / "
"thread tried to join on itself");
default: break;
}
m_joined = true;
}
void Thread::detach() {
int retval = pthread_detach(m_thread);
switch (retval) {
case EINVAL: Log(EError, "Thread::detach() - the thread is not joinable");
case ESRCH: Log(EError, "Thread::detach() - the thread does not exist");
default: break;
}
}
void Thread::sleep(unsigned int ms) {
#if defined(WIN32)
SleepEx(ms, FALSE);
#else
struct timeval tv;
tv.tv_sec = ms/1000;
tv.tv_usec = (ms * 1000) % 1000000;
select(0, NULL, NULL, NULL, &tv);
#endif
}
void Thread::yield() {
#if defined(WIN32)
Yield();
#else
sched_yield();
#endif
}
void Thread::exit() {
Log(EDebug, "Thread \"%s\" has finished", m_name.c_str());
m_running = false;
decRef();
m_self->set(NULL);
pthread_exit(NULL);
}
std::string Thread::toString() const {
std::ostringstream oss;
oss << "Thread[" << endl
<< " name=\"" << m_name << "\"," << endl
<< " running=" << m_running << "," << endl
<< " joined=" << m_joined << "," << endl
<< " priority=" << m_priority << "," << endl
<< " critical=" << m_critical << "," << endl
<< " stackSize=" << m_stackSize << endl
<< "]";
return oss.str();
}
void Thread::staticInitialization() {
#if defined(__OSX__)
__ubi_autorelease_init();
#endif
m_self = new ThreadLocal<Thread>();
#if defined(__LINUX__) || defined(__OSX__)
m_idMutex = new Mutex();
m_idCounter = 0;
#endif
Thread *mainThread = new MainThread();
mainThread->m_running = true;
mainThread->m_thread = pthread_self();
mainThread->m_joinMutex = new Mutex();
mainThread->m_joined = false;
mainThread->m_fresolver = new FileResolver();
m_self->set(mainThread);
#if defined(__OSX__)
mainThread->m_id = 0;
#elif defined(__LINUX__)
m_id = 0;
#endif
}
void Thread::staticShutdown() {
getThread()->m_running = false;
m_self->set(NULL);
delete m_self;
m_self = NULL;
#if defined(__LINUX__) || defined(__OSX__)
m_idMutex = NULL;
#endif
#if defined(__OSX__)
__ubi_autorelease_shutdown();
#endif
}
Thread::~Thread() {
if (m_running)
Log(EWarn, "Destructor called while Thread '%s' is still running", m_name.c_str());
}
MTS_IMPLEMENT_CLASS(Thread, true, Object)
MTS_IMPLEMENT_CLASS(MainThread, false, Thread)
MTS_NAMESPACE_END