(svn r9863) [0.5] -Backport from trunk (r9759, r9861): 0.5
authorrubidium
Thu, 17 May 2007 15:04:28 +0000
branch0.5
changeset 5502 7faea30b9be0
parent 5501 3d16ec62d17c
child 5503 207038be1302
(svn r9863) [0.5] -Backport from trunk (r9759, r9861):
- Feature: Add threading support for MorphOS (r9759)
- Fix: Null pointer dereference under MorphOS and AmigaOS (r9861)
thread.c
unix.c
--- a/thread.c	Wed May 16 21:41:34 2007 +0000
+++ b/thread.c	Thu May 17 15:04:28 2007 +0000
@@ -4,7 +4,7 @@
 #include "thread.h"
 #include <stdlib.h>
 
-#if defined(__AMIGA__) || defined(__MORPHOS__) || defined(NO_THREADS)
+#if defined(__AMIGA__) || defined(NO_THREADS)
 OTTDThread *OTTDCreateThread(OTTDThreadFunc function, void *arg) { return NULL; }
 void *OTTDJoinThread(OTTDThread *t) { return NULL; }
 void OTTDExitThread(void) { NOT_REACHED(); };
@@ -62,7 +62,7 @@
 	_endthread();
 }
 
-#elif defined(UNIX)
+#elif defined(UNIX) && !defined(__MORPHOS__)
 
 #include <pthread.h>
 
@@ -154,4 +154,156 @@
 {
 	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
--- a/unix.c	Wed May 16 21:41:34 2007 +0000
+++ b/unix.c	Thu May 17 15:04:28 2007 +0000
@@ -55,7 +55,7 @@
 #else
 	/* On MorphOS or AmigaOS paths look like: "Volume:directory/subdirectory" */
 	const char *s = strchr(path, ':');
-	return s[1] == '\0';
+	return s != NULL && s[1] == '\0';
 #endif
 }