summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/thread.cpp157
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