285 lines
6.8 KiB
C++
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, ¶m);
|
|
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, ¶m);
|
|
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
|