diff options
-rw-r--r-- | src/thread.cpp | 157 |
1 files changed, 155 insertions, 2 deletions
diff --git a/src/thread.cpp b/src/thread.cpp index b17efa8f0..61b6ccabb 100644 --- a/src/thread.cpp +++ b/src/thread.cpp @@ -7,7 +7,7 @@ #include <stdlib.h> #include "helpers.hpp" -#if defined(__AMIGA__) || defined(__MORPHOS__) || defined(PSP) || defined(NO_THREADS) +#if defined(__AMIGA__) || defined(PSP) || defined(NO_THREADS) OTTDThread *OTTDCreateThread(OTTDThreadFunc function, void *arg) { return NULL; } void *OTTDJoinThread(OTTDThread *t) { return NULL; } void OTTDExitThread() { NOT_REACHED(); }; @@ -65,7 +65,7 @@ void OTTDExitThread() _endthread(); } -#elif defined(UNIX) +#elif defined(UNIX) && !defined(MORPHOS) #include <pthread.h> @@ -157,4 +157,157 @@ void OTTDExitThread() { ExitThread(0); } + + +#elif defined(MORPHOS) + +#include <exec/types.h> +#include <exec/rawfmt.h> +#include <dos/dostags.h> + +#include <proto/dos.h> +#include <proto/exec.h> + +#include <setjmp.h> + +/* 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 + void *ret; ///< return value of the thread function + jmp_buf jumpstore; ///< storage for the setjump state +}; + +struct OTTDThread { + struct MsgPort *replyport; + struct OTTDThreadStartupMessage msg; +}; + + +/** + * 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 + +static void Proxy(void) +{ + 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) { + /* Make use of setjmp() here, so this point can be reached again from inside + * OTTDExitThread() which can be called from anythere inside msg->func. + * It's a bit ugly and in worst case it leaks some memory. */ + if (setjmp(msg->jumpstore) == 0) { + msg->ret = msg->func(msg->arg); + } else { + KPutStr("[Child] Returned to main()\n"); + } + } + + /* Quit the child, exec.library will reply the startup msg internally. */ + KPutStr("[Child] Done.\n"); +} + +OTTDThread* OTTDCreateThread(OTTDThreadFunc function, void *arg) +{ + OTTDThread *t; + struct Task *parent; + + KPutStr("[OpenTTD] Create thread...\n"); + + t = (struct OTTDThread *)AllocVecTaskPooled(sizeof(struct OTTDThread)); + if (t == NULL) return NULL; + + 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 */ + t->msg.func = function; + t->msg.arg = arg; + t->msg.ret = NULL; + + t->replyport = CreateMsgPort(); + + if (t->replyport != NULL) { + struct Process *child; + + t->msg.msg.mn_Node.ln_Type = NT_MESSAGE; + t->msg.msg.mn_ReplyPort = t->replyport; + t->msg.msg.mn_Length = sizeof(struct OTTDThreadStartupMessage); + + child = CreateNewProcTags( + NP_CodeType, CODETYPE_PPC, + NP_Entry, Proxy, + NP_StartupMsg, (ULONG)&t->msg, + NP_Priority, 5UL, + NP_Name, (ULONG)"OpenTTD Thread", + NP_PPCStackSize, 131072UL, + TAG_DONE); + + if (child != NULL) { + KPutStr("[OpenTTD] Child process launched.\n"); + return t; + } + DeleteMsgPort(t->replyport); + } + FreeVecTaskPooled(t); + + return NULL; +} + +void* OTTDJoinThread(OTTDThread *t) +{ + struct OTTDThreadStartupMessage *reply; + void *ret; + + KPutStr("[OpenTTD] Join threads...\n"); + + if (t == NULL) return NULL; + + KPutStr("[OpenTTD] Wait for child to quit...\n"); + WaitPort(t->replyport); + + reply = (struct OTTDThreadStartupMessage *)GetMsg(t->replyport); + ret = reply->ret; + + DeleteMsgPort(t->replyport); + FreeVecTaskPooled(t); + + return ret; +} + +void OTTDExitThread() +{ + struct OTTDThreadStartupMessage *msg; + + KPutStr("[Child] Aborting...\n"); + + if (NewGetTaskAttrs(NULL, &msg, sizeof(struct OTTDThreadStartupMessage *), TASKINFOTYPE_STARTUPMSG, TAG_DONE) && msg != NULL) { + KPutStr("[Child] Jumping back...\n"); + longjmp(msg->jumpstore, 0xBEAFCAFE); + } + + NOT_REACHED(); +} + #endif |