diff options
author | rubidium <rubidium@openttd.org> | 2009-09-01 10:07:22 +0000 |
---|---|---|
committer | rubidium <rubidium@openttd.org> | 2009-09-01 10:07:22 +0000 |
commit | 07d2af338e237889ff453a3a774522b9b0021439 (patch) | |
tree | 373ccc2eecf1aa917f24008e4d25535413dc4eb2 /src/thread | |
parent | 5a3b2f0d02ddb7fedbca29a3e5e68dec28c887af (diff) | |
download | openttd-07d2af338e237889ff453a3a774522b9b0021439.tar.xz |
(svn r17339) -Codechange: move thread related files to their own directory (like done for video, music, sound, etc)
Diffstat (limited to 'src/thread')
-rw-r--r-- | src/thread/thread.h | 73 | ||||
-rw-r--r-- | src/thread/thread_morphos.cpp | 199 | ||||
-rw-r--r-- | src/thread/thread_none.cpp | 31 | ||||
-rw-r--r-- | src/thread/thread_os2.cpp | 123 | ||||
-rw-r--r-- | src/thread/thread_pthread.cpp | 124 | ||||
-rw-r--r-- | src/thread/thread_win32.cpp | 136 |
6 files changed, 686 insertions, 0 deletions
diff --git a/src/thread/thread.h b/src/thread/thread.h new file mode 100644 index 000000000..22873bee5 --- /dev/null +++ b/src/thread/thread.h @@ -0,0 +1,73 @@ +/* $Id$ */ + +/* + * This file is part of OpenTTD. + * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2. + * OpenTTD 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 OpenTTD. If not, see <http://www.gnu.org/licenses/>. + */ + +/** @file thread.h Base of all threads. */ + +#ifndef THREAD_H +#define THREAD_H + +typedef void (*OTTDThreadFunc)(void *); + +class OTTDThreadExitSignal { }; + +/** + * A Thread Object which works on all our supported OSes. + */ +class ThreadObject { +public: + /** + * Virtual destructor to allow 'delete' operator to work properly. + */ + virtual ~ThreadObject() {}; + + /** + * Exit this thread. + */ + virtual bool Exit() = 0; + + /** + * Join this thread. + */ + virtual void Join() = 0; + + /** + * Create a thread; proc will be called as first function inside the thread, + * with optinal params. + * @param proc The procedure to call inside the thread. + * @param param The params to give with 'proc'. + * @param thread Place to store a pointer to the thread in. May be NULL. + * @return True if the thread was started correctly. + */ + static bool New(OTTDThreadFunc proc, void *param, ThreadObject **thread = NULL); +}; + +/** + * Cross-platform Mutex + */ +class ThreadMutex { +public: + static ThreadMutex *New(); + + /** + * Virtual Destructor to avoid compiler warnings. + */ + virtual ~ThreadMutex() {}; + + /** + * Begin the critical section + */ + virtual void BeginCritical() = 0; + + /** + * End of the critical section + */ + virtual void EndCritical() = 0; +}; + +#endif /* THREAD_H */ diff --git a/src/thread/thread_morphos.cpp b/src/thread/thread_morphos.cpp new file mode 100644 index 000000000..abf2cf0cc --- /dev/null +++ b/src/thread/thread_morphos.cpp @@ -0,0 +1,199 @@ +/* $Id$ */ + +/* + * This file is part of OpenTTD. + * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2. + * OpenTTD 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 OpenTTD. If not, see <http://www.gnu.org/licenses/>. + */ + +/** @file thread_morphos.cpp MorphOS implementation of Threads. */ + +#include "../stdafx.h" +#include "thread.h" +#include "../debug.h" +#include "../core/alloc_func.hpp" +#include <stdlib.h> +#include <unistd.h> + +#include <exec/types.h> +#include <exec/rawfmt.h> +#include <dos/dostags.h> + +#include <proto/dos.h> +#include <proto/exec.h> + +/** + * avoid name clashes with MorphOS API functions + */ +#undef Exit +#undef Wait + + +/** + * NOTE: this code heavily depends on latest libnix updates. So make + * sure you link with new stuff which supports semaphore locking of + * the IO resources, else it will just go foobar. + */ + + +struct OTTDThreadStartupMessage { + struct Message msg; ///< standard exec.library message (MUST be the first thing in the message struct!) + OTTDThreadFunc func; ///< function the thread will execute + void *arg; ///< functions arguments for the thread function +}; + + +/** + * Default OpenTTD STDIO/ERR debug output is not very useful for this, so we + * utilize serial/ramdebug instead. + */ +#ifndef NO_DEBUG_MESSAGES +void KPutStr(CONST_STRPTR format) +{ + RawDoFmt(format, NULL, (void (*)())RAWFMTFUNC_SERIAL, NULL); +} +#else +#define KPutStr(x) +#endif + + +/** + * MorphOS version for ThreadObject. + */ +class ThreadObject_MorphOS : public ThreadObject { +private: + APTR m_thr; ///< System thread identifier. + struct MsgPort *m_replyport; + struct OTTDThreadStartupMessage m_msg; + bool self_destruct; + +public: + /** + * Create a sub process and start it, calling proc(param). + */ + ThreadObject_MorphOS(OTTDThreadFunc proc, void *param, self_destruct) : + m_thr(0), self_destruct(self_destruct) + { + struct Task *parent; + + KPutStr("[OpenTTD] Create thread...\n"); + + parent = FindTask(NULL); + + /* Make sure main thread runs with sane priority */ + SetTaskPri(parent, 0); + + /* Things we'll pass down to the child by utilizing NP_StartupMsg */ + m_msg.func = proc; + m_msg.arg = param; + + m_replyport = CreateMsgPort(); + + if (m_replyport != NULL) { + struct Process *child; + + m_msg.msg.mn_Node.ln_Type = NT_MESSAGE; + m_msg.msg.mn_ReplyPort = m_replyport; + m_msg.msg.mn_Length = sizeof(struct OTTDThreadStartupMessage); + + child = CreateNewProcTags( + NP_CodeType, CODETYPE_PPC, + NP_Entry, ThreadObject_MorphOS::Proxy, + NP_StartupMsg, (IPTR)&m_msg, + NP_Priority, 5UL, + NP_Name, (IPTR)"OpenTTD Thread", + NP_PPCStackSize, 131072UL, + TAG_DONE); + + m_thr = (APTR) child; + + if (child != NULL) { + KPutStr("[OpenTTD] Child process launched.\n"); + } else { + KPutStr("[OpenTTD] Couldn't create child process. (constructors never fail, yeah!)\n"); + DeleteMsgPort(m_replyport); + } + } + } + + /* virtual */ ~ThreadObject_MorphOS() + { + } + + /* virtual */ bool Exit() + { + struct OTTDThreadStartupMessage *msg; + + /* You can only exit yourself */ + assert(IsCurrent()); + + KPutStr("[Child] Aborting...\n"); + + if (NewGetTaskAttrs(NULL, &msg, sizeof(struct OTTDThreadStartupMessage *), TASKINFOTYPE_STARTUPMSG, TAG_DONE) && msg != NULL) { + /* For now we terminate by throwing an error, gives much cleaner cleanup */ + throw OTTDThreadExitSignal(); + } + + return true; + } + + /* virtual */ void Join() + { + struct OTTDThreadStartupMessage *reply; + + /* You cannot join yourself */ + assert(!IsCurrent()); + + KPutStr("[OpenTTD] Join threads...\n"); + KPutStr("[OpenTTD] Wait for child to quit...\n"); + WaitPort(m_replyport); + + GetMsg(m_replyport); + DeleteMsgPort(m_replyport); + m_thr = 0; + } + + /* virtual */ bool IsCurrent() + { + return FindTask(NULL) == 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 Proxy() + { + struct Task *child = FindTask(NULL); + struct OTTDThreadStartupMessage *msg; + + /* Make sure, we don't block the parent. */ + SetTaskPri(child, -5); + + KPutStr("[Child] Progressing...\n"); + + if (NewGetTaskAttrs(NULL, &msg, sizeof(struct OTTDThreadStartupMessage *), TASKINFOTYPE_STARTUPMSG, TAG_DONE) && msg != NULL) { + try { + msg->func(msg->arg); + } catch(OTTDThreadExitSignal e) { + KPutStr("[Child] Returned to main()\n"); + } catch(...) { + NOT_REACHED(); + } + } + + /* Quit the child, exec.library will reply the startup msg internally. */ + KPutStr("[Child] Done.\n"); + + if (self_destruct) delete this; + } +}; + +/* static */ bool ThreadObject::New(OTTDThreadFunc proc, void *param, ThreadObject **thread) +{ + ThreadObject *to = new ThreadObject_MorphOS(proc, param, thread == NULL); + if (thread != NULL) *thread = to; + return true; +} diff --git a/src/thread/thread_none.cpp b/src/thread/thread_none.cpp new file mode 100644 index 000000000..861f2cfd6 --- /dev/null +++ b/src/thread/thread_none.cpp @@ -0,0 +1,31 @@ +/* $Id$ */ + +/* + * This file is part of OpenTTD. + * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2. + * OpenTTD 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 OpenTTD. If not, see <http://www.gnu.org/licenses/>. + */ + +/** @file thread_none.cpp No-Threads-Available implementation of Threads */ + +#include "../stdafx.h" +#include "thread.h" + +/* static */ bool ThreadObject::New(OTTDThreadFunc proc, void *param, ThreadObject **thread) +{ + if (thread != NULL) *thread = NULL; + return false; +} + +/** Mutex that doesn't do locking because it ain't needed when there're no threads */ +class ThreadMutex_None : public ThreadMutex { +public: + virtual void BeginCritical() {} + virtual void EndCritical() {} +}; + +/* static */ ThreadMutex *ThreadMutex::New() +{ + return new ThreadMutex_None(); +} diff --git a/src/thread/thread_os2.cpp b/src/thread/thread_os2.cpp new file mode 100644 index 000000000..1145e8e41 --- /dev/null +++ b/src/thread/thread_os2.cpp @@ -0,0 +1,123 @@ +/* $Id$ */ + +/* + * This file is part of OpenTTD. + * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2. + * OpenTTD 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 OpenTTD. If not, see <http://www.gnu.org/licenses/>. + */ + +/** @file thread_os2.cpp OS/2 implementation of Threads. */ + +#include "../stdafx.h" +#include "thread.h" + +#define INCL_DOS +#include <os2.h> +#include <process.h> + +/** + * OS/2 version for ThreadObject. + */ +class ThreadObject_OS2 : public ThreadObject { +private: + TID thread; ///< System thread identifier. + OTTDThreadFunc proc; ///< External thread procedure. + void *param; ///< Parameter for the external thread procedure. + bool self_destruct; ///< Free ourselves when done? + +public: + /** + * Create a thread and start it, calling proc(param). + */ + ThreadObject_OS2(OTTDThreadFunc proc, void *param, bool self_destruct) : + thread(0), + proc(proc), + param(param), + self_destruct(self_destruct) + { + thread = _beginthread(stThreadProc, NULL, 32768, this); + } + + /* virtual */ bool Exit() + { + _endthread(); + return true; + } + + /* virtual */ void Join() + { + DosWaitThread(&this->thread, DCWW_WAIT); + this->thread = 0; + } +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_OS2 *)thr)->ThreadProc(); + } + + /** + * A new thread is created, and this function is called. Call the custom + * function of the creator of the thread. + */ + void ThreadProc() + { + /* Call the proc of the creator to continue this thread */ + try { + this->proc(this->param); + } catch (OTTDThreadExitSignal e) { + } catch (...) { + NOT_REACHED(); + } + + if (self_destruct) { + this->Exit(); + delete this; + } + } +}; + +/* static */ bool ThreadObject::New(OTTDThreadFunc proc, void *param, ThreadObject **thread) +{ + ThreadObject *to = new ThreadObject_OS2(proc, param, thread == NULL); + if (thread != NULL) *thread = to; + return true; +} + +/** + * OS/2 version of ThreadMutex. + */ +class ThreadMutex_OS2 : public ThreadMutex { +private: + HMTX mutex; + +public: + ThreadMutex_OS2() + { + DosCreateMutexSem(NULL, &mutex, 0, FALSE); + } + + /* virtual */ ~ThreadMutex_OS2() + { + DosCloseMutexSem(mutex); + } + + /* virtual */ void BeginCritical() + { + DosRequestMutexSem(mutex, (unsigned long) SEM_INDEFINITE_WAIT); + } + + /* virtual */ void EndCritical() + { + DosReleaseMutexSem(mutex); + } +}; + +/* static */ ThreadMutex *ThreadMutex::New() +{ + return new ThreadMutex_OS2(); +} diff --git a/src/thread/thread_pthread.cpp b/src/thread/thread_pthread.cpp new file mode 100644 index 000000000..483b71c0f --- /dev/null +++ b/src/thread/thread_pthread.cpp @@ -0,0 +1,124 @@ +/* $Id$ */ + +/* + * This file is part of OpenTTD. + * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2. + * OpenTTD 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 OpenTTD. If not, see <http://www.gnu.org/licenses/>. + */ + +/** @file thread_pthread.cpp POSIX pthread implementation of Threads. */ + +#include "../stdafx.h" +#include "thread.h" +#include <pthread.h> + +/** + * POSIX pthread version for ThreadObject. + */ +class ThreadObject_pthread : public ThreadObject { +private: + pthread_t thread; ///< System thread identifier. + OTTDThreadFunc proc; ///< External thread procedure. + void *param; ///< Parameter for the external thread procedure. + bool self_destruct; ///< Free ourselves when done? + +public: + /** + * Create a pthread and start it, calling proc(param). + */ + ThreadObject_pthread(OTTDThreadFunc proc, void *param, bool self_destruct) : + thread(0), + proc(proc), + param(param), + self_destruct(self_destruct) + { + pthread_create(&this->thread, NULL, &stThreadProc, this); + } + + /* virtual */ bool Exit() + { + assert(pthread_self() == this->thread); + /* For now we terminate by throwing an error, gives much cleaner cleanup */ + throw OTTDThreadExitSignal(); + } + + /* virtual */ void Join() + { + /* You cannot join yourself */ + assert(pthread_self() != this->thread); + pthread_join(this->thread, NULL); + this->thread = 0; + } +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() + { + /* Call the proc of the creator to continue this thread */ + try { + this->proc(this->param); + } catch (OTTDThreadExitSignal e) { + } catch (...) { + NOT_REACHED(); + } + + if (self_destruct) { + pthread_detach(pthread_self()); + delete this; + } + } +}; + +/* static */ bool ThreadObject::New(OTTDThreadFunc proc, void *param, ThreadObject **thread) +{ + ThreadObject *to = new ThreadObject_pthread(proc, param, thread == NULL); + if (thread != NULL) *thread = to; + return true; +} + +/** + * POSIX pthread version of ThreadMutex. + */ +class ThreadMutex_pthread : public ThreadMutex { +private: + pthread_mutex_t mutex; + +public: + ThreadMutex_pthread() + { + pthread_mutex_init(&this->mutex, NULL); + } + + /* virtual */ ~ThreadMutex_pthread() + { + pthread_mutex_destroy(&this->mutex); + } + + /* virtual */ void BeginCritical() + { + pthread_mutex_lock(&this->mutex); + } + + /* virtual */ void EndCritical() + { + pthread_mutex_unlock(&this->mutex); + } +}; + +/* static */ ThreadMutex *ThreadMutex::New() +{ + return new ThreadMutex_pthread(); +} diff --git a/src/thread/thread_win32.cpp b/src/thread/thread_win32.cpp new file mode 100644 index 000000000..e752de1ae --- /dev/null +++ b/src/thread/thread_win32.cpp @@ -0,0 +1,136 @@ +/* $Id$ */ + +/* + * This file is part of OpenTTD. + * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2. + * OpenTTD 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 OpenTTD. If not, see <http://www.gnu.org/licenses/>. + */ + +/** @file thread_win32.cpp Win32 thread implementation of Threads. */ + +#include "../stdafx.h" +#include "thread.h" +#include "../debug.h" +#include "../core/alloc_func.hpp" +#include <stdlib.h> +#include <windows.h> +#include <process.h> + +/** + * Win32 thread version for ThreadObject. + */ +class ThreadObject_Win32 : public ThreadObject { +private: + HANDLE thread; ///< System thread identifier. + uint id; ///< Thread identifier. + OTTDThreadFunc proc; ///< External thread procedure. + void *param; ///< Parameter for the external thread procedure. + bool self_destruct; ///< Free ourselves when done? + +public: + /** + * Create a win32 thread and start it, calling proc(param). + */ + ThreadObject_Win32(OTTDThreadFunc proc, void *param, bool self_destruct) : + thread(NULL), + id(0), + proc(proc), + param(param), + self_destruct(self_destruct) + { + this->thread = (HANDLE)_beginthreadex(NULL, 0, &stThreadProc, this, CREATE_SUSPENDED, &this->id); + if (this->thread == NULL) return; + ResumeThread(this->thread); + } + + /* virtual */ ~ThreadObject_Win32() + { + if (this->thread != NULL) { + CloseHandle(this->thread); + this->thread = NULL; + } + } + + /* virtual */ bool Exit() + { + assert(GetCurrentThreadId() == this->id); + /* For now we terminate by throwing an error, gives much cleaner cleanup */ + throw OTTDThreadExitSignal(); + } + + /* virtual */ void Join() + { + /* You cannot join yourself */ + assert(GetCurrentThreadId() != this->id); + WaitForSingleObject(this->thread, INFINITE); + } + +private: + /** + * On thread creation, this function is called, which calls the real startup + * function. This to get back into the correct instance again. + */ + static uint CALLBACK stThreadProc(void *thr) + { + ((ThreadObject_Win32 *)thr)->ThreadProc(); + return 0; + } + + /** + * A new thread is created, and this function is called. Call the custom + * function of the creator of the thread. + */ + void ThreadProc() + { + try { + this->proc(this->param); + } catch (OTTDThreadExitSignal) { + } catch (...) { + NOT_REACHED(); + } + + if (self_destruct) delete this; + } +}; + +/* static */ bool ThreadObject::New(OTTDThreadFunc proc, void *param, ThreadObject **thread) +{ + ThreadObject *to = new ThreadObject_Win32(proc, param, thread == NULL); + if (thread != NULL) *thread = to; + return true; +} + +/** + * Win32 thread version of ThreadMutex. + */ +class ThreadMutex_Win32 : public ThreadMutex { +private: + CRITICAL_SECTION critical_section; + +public: + ThreadMutex_Win32() + { + InitializeCriticalSection(&this->critical_section); + } + + /* virtual */ ~ThreadMutex_Win32() + { + DeleteCriticalSection(&this->critical_section); + } + + /* virtual */ void BeginCritical() + { + EnterCriticalSection(&this->critical_section); + } + + /* virtual */ void EndCritical() + { + LeaveCriticalSection(&this->critical_section); + } +}; + +/* static */ ThreadMutex *ThreadMutex::New() +{ + return new ThreadMutex_Win32(); +} |