OpenTTD/src/thread_pthread.cpp

198 lines
4.0 KiB
C++

/* $Id$ */
/** @file thread_pthread.cpp POSIX pthread implementation of Threads. */
#include "stdafx.h"
#include "thread.h"
#include "debug.h"
#include "core/alloc_func.hpp"
#include <stdlib.h>
#include <pthread.h>
#include <semaphore.h>
#include <unistd.h>
/**
* POSIX pthread version for ThreadObject.
*/
class ThreadObject_pthread : public ThreadObject {
private:
pthread_t m_thr; ///< System thread identifier.
OTTDThreadFunc m_proc; ///< External thread procedure.
void *m_param; ///< Parameter for the external thread procedure.
bool m_attached; ///< True if the ThreadObject was attached to an existing thread.
sem_t m_sem_start; ///< Here the new thread waits before it starts.
sem_t m_sem_stop; ///< Here the other thread can wait for this thread to end.
public:
/**
* Create a pthread and start it, calling proc(param).
*/
ThreadObject_pthread(OTTDThreadFunc proc, void *param) :
m_thr(0),
m_proc(proc),
m_param(param),
m_attached(false)
{
sem_init(&m_sem_start, 0, 0);
sem_init(&m_sem_stop, 0, 0);
pthread_create(&m_thr, NULL, &stThreadProc, this);
sem_post(&m_sem_start);
}
/**
* Create a pthread and attach current thread to it.
*/
ThreadObject_pthread() :
m_thr(0),
m_proc(NULL),
m_param(0),
m_attached(true)
{
sem_init(&m_sem_start, 0, 0);
sem_init(&m_sem_stop, 0, 0);
m_thr = pthread_self();
}
/* virtual */ ~ThreadObject_pthread()
{
sem_destroy(&m_sem_stop);
sem_destroy(&m_sem_start);
};
/* virtual */ bool IsRunning()
{
int sval;
sem_getvalue(&m_sem_stop, &sval);
return sval == 0;
}
/* virtual */ bool WaitForStop()
{
/* You can't wait on yourself */
assert(!IsCurrent());
/* If the thread is not running, waiting is over */
if (!IsRunning()) return true;
int ret = sem_wait(&m_sem_stop);
if (ret == 0) {
/* We have passed semaphore so increment it again */
sem_post(&m_sem_stop);
return true;
}
return false;
}
/* virtual */ bool Exit()
{
/* You can only exit yourself */
assert(IsCurrent());
/* If the thread is not running, we are already closed */
if (!IsRunning()) return false;
/* For now we terminate by throwing an error, gives much cleaner cleanup */
throw 0;
}
/* virtual */ void Join()
{
/* You cannot join yourself */
assert(!IsCurrent());
pthread_join(m_thr, NULL);
m_thr = 0;
}
/* virtual */ bool IsCurrent()
{
return pthread_self() == m_thr;
}
/* virtual */ uint GetId()
{
return (uint)m_thr;
}
private:
/**
* On thread creation, this function is called, which calls the real startup
* function. This to get back into the correct instance again.
*/
static void *stThreadProc(void *thr)
{
((ThreadObject_pthread *)thr)->ThreadProc();
pthread_exit(NULL);
}
/**
* A new thread is created, and this function is called. Call the custom
* function of the creator of the thread.
*/
void ThreadProc()
{
/* The new thread stops here so the calling thread can complete pthread_create() call */
sem_wait(&m_sem_start);
/* Call the proc of the creator to continue this thread */
try {
m_proc(m_param);
} catch (...) {
}
/* Notify threads waiting for our completion */
sem_post(&m_sem_stop);
}
};
/* static */ ThreadObject *ThreadObject::New(OTTDThreadFunc proc, void *param)
{
return new ThreadObject_pthread(proc, param);
}
/* static */ ThreadObject *ThreadObject::AttachCurrent()
{
return new ThreadObject_pthread();
}
/* static */ uint ThreadObject::CurrentId()
{
return (uint)pthread_self();
}
/**
* POSIX pthread version of ThreadSemaphore.
*/
class ThreadSemaphore_pthread : public ThreadSemaphore {
private:
sem_t m_sem;
public:
ThreadSemaphore_pthread()
{
sem_init(&m_sem, 0, 0);
}
/* virtual */ ~ThreadSemaphore_pthread()
{
sem_destroy(&m_sem);
}
/* virtual */ void Set()
{
int val = 0;
if (sem_getvalue(&m_sem, &val) == 0 && val == 0) sem_post(&m_sem);
}
/* virtual */ void Wait()
{
sem_wait(&m_sem);
}
};
/* static */ ThreadSemaphore *ThreadSemaphore::New()
{
return new ThreadSemaphore_pthread();
}