src/thread_morphos.cpp
changeset 10438 b000673d2e44
child 10860 ae89867701fe
equal deleted inserted replaced
10437:0452e6a6feb7 10438:b000673d2e44
       
     1 /* $Id$ */
       
     2 
       
     3 /** @file thread_morphos.cpp MorphOS implementation of Threads. */
       
     4 
       
     5 #include "stdafx.h"
       
     6 #include "thread.h"
       
     7 #include "debug.h"
       
     8 #include "core/alloc_func.hpp"
       
     9 #include <stdlib.h>
       
    10 #include <unistd.h>
       
    11 
       
    12 #include <exec/types.h>
       
    13 #include <exec/rawfmt.h>
       
    14 #include <dos/dostags.h>
       
    15 
       
    16 #include <proto/dos.h>
       
    17 #include <proto/exec.h>
       
    18 
       
    19 /**
       
    20  *  avoid name clashes with MorphOS API functions
       
    21  */
       
    22 #undef Exit
       
    23 #undef Wait
       
    24 
       
    25 
       
    26 /**
       
    27  *  NOTE: this code heavily depends on latest libnix updates. So make
       
    28  *        sure you link with new stuff which supports semaphore locking of
       
    29  *        the IO resources, else it will just go foobar.
       
    30  */
       
    31 
       
    32 
       
    33 struct OTTDThreadStartupMessage {
       
    34 	struct Message msg;  ///< standard exec.library message (MUST be the first thing in the message struct!)
       
    35 	OTTDThreadFunc func; ///< function the thread will execute
       
    36 	void *arg;           ///< functions arguments for the thread function
       
    37 	void *ret;           ///< return value of the thread function
       
    38 };
       
    39 
       
    40 
       
    41 /**
       
    42  *  Default OpenTTD STDIO/ERR debug output is not very useful for this, so we
       
    43  *  utilize serial/ramdebug instead.
       
    44  */
       
    45 #ifndef NO_DEBUG_MESSAGES
       
    46 void KPutStr(CONST_STRPTR format)
       
    47 {
       
    48 	RawDoFmt(format, NULL, (void (*)())RAWFMTFUNC_SERIAL, NULL);
       
    49 }
       
    50 #else
       
    51 #define KPutStr(x)
       
    52 #endif
       
    53 
       
    54 
       
    55 /**
       
    56  * MorphOS version for ThreadObject.
       
    57  */
       
    58 class ThreadObject_MorphOS : public ThreadObject {
       
    59 private:
       
    60 	APTR m_thr;                  ///< System thread identifier.
       
    61 	struct MsgPort *m_replyport;
       
    62 	struct OTTDThreadStartupMessage m_msg;
       
    63 
       
    64 public:
       
    65 	/**
       
    66 	 * Create a sub process and start it, calling proc(param).
       
    67 	 */
       
    68 	ThreadObject_MorphOS(OTTDThreadFunc proc, void *param) : m_thr(0)
       
    69 	{
       
    70 		struct Task *parent;
       
    71 
       
    72 		KPutStr("[OpenTTD] Create thread...\n");
       
    73 
       
    74 		parent = FindTask(NULL);
       
    75 
       
    76 		/* Make sure main thread runs with sane priority */
       
    77 		SetTaskPri(parent, 0);
       
    78 
       
    79 		/* Things we'll pass down to the child by utilizing NP_StartupMsg */
       
    80 		m_msg.func = proc;
       
    81 		m_msg.arg  = param;
       
    82 		m_msg.ret  = NULL;
       
    83 
       
    84 		m_replyport = CreateMsgPort();
       
    85 
       
    86 		if (m_replyport != NULL) {
       
    87 			struct Process *child;
       
    88 
       
    89 			m_msg.msg.mn_Node.ln_Type = NT_MESSAGE;
       
    90 			m_msg.msg.mn_ReplyPort    = m_replyport;
       
    91 			m_msg.msg.mn_Length       = sizeof(struct OTTDThreadStartupMessage);
       
    92 
       
    93 			child = CreateNewProcTags(
       
    94 				NP_CodeType,     CODETYPE_PPC,
       
    95 				NP_Entry,        ThreadObject_MorphOS::Proxy,
       
    96 				NP_StartupMsg,   (IPTR)&m_msg,
       
    97 				NP_Priority,     5UL,
       
    98 				NP_Name,         (IPTR)"OpenTTD Thread",
       
    99 				NP_PPCStackSize, 131072UL,
       
   100 				TAG_DONE);
       
   101 
       
   102 			m_thr = (APTR) child;
       
   103 
       
   104 			if (child != NULL) {
       
   105 				KPutStr("[OpenTTD] Child process launched.\n");
       
   106 			} else {
       
   107 				KPutStr("[OpenTTD] Couldn't create child process. (constructors never fail, yeah!)\n");
       
   108 				DeleteMsgPort(m_replyport);
       
   109 			}
       
   110 		}
       
   111 	}
       
   112 
       
   113 	/**
       
   114 	 * Create a thread and attach current thread to it.
       
   115 	 */
       
   116 	ThreadObject_MorphOS() : m_thr(0)
       
   117 	{
       
   118 		m_thr = FindTask(NULL);
       
   119 	}
       
   120 
       
   121 	/* virtual */ ~ThreadObject_MorphOS()
       
   122 	{
       
   123 	}
       
   124 
       
   125 	/* virtual */ bool IsRunning()
       
   126 	{
       
   127 		return m_thr != 0;
       
   128 	}
       
   129 
       
   130 	/* virtual */ bool WaitForStop()
       
   131 	{
       
   132 		/* You can't wait on yourself */
       
   133 		assert(!IsCurrent());
       
   134 		/* If the thread is not running, waiting is over */
       
   135 		if (!IsRunning()) return true;
       
   136 
       
   137 		WaitPort(m_replyport);
       
   138 
       
   139 		GetMsg(m_replyport);
       
   140 		DeleteMsgPort(m_replyport);
       
   141 
       
   142 		return true;
       
   143 	}
       
   144 
       
   145 	/* virtual */ bool Exit()
       
   146 	{
       
   147 		struct OTTDThreadStartupMessage *msg;
       
   148 
       
   149 		/* You can only exit yourself */
       
   150 		assert(IsCurrent());
       
   151 		/* If the thread is not running, we are already closed */
       
   152 		if (!IsRunning()) return false;
       
   153 
       
   154 		KPutStr("[Child] Aborting...\n");
       
   155 
       
   156 		if (NewGetTaskAttrs(NULL, &msg, sizeof(struct OTTDThreadStartupMessage *), TASKINFOTYPE_STARTUPMSG, TAG_DONE) && msg != NULL) {
       
   157 			/* For now we terminate by throwing an error, gives much cleaner cleanup */
       
   158 			throw 0;
       
   159 		}
       
   160 
       
   161 		return true;
       
   162 	}
       
   163 
       
   164 	/* virtual */ void *Join()
       
   165 	{
       
   166 		struct OTTDThreadStartupMessage *reply;
       
   167 		void *ret;
       
   168 
       
   169 		/* You cannot join yourself */
       
   170 		assert(!IsCurrent());
       
   171 
       
   172 		KPutStr("[OpenTTD] Join threads...\n");
       
   173 		KPutStr("[OpenTTD] Wait for child to quit...\n");
       
   174 		WaitPort(m_replyport);
       
   175 
       
   176 		reply = (struct OTTDThreadStartupMessage *)GetMsg(m_replyport);
       
   177 		ret   = reply->ret;
       
   178 
       
   179 		DeleteMsgPort(m_replyport);
       
   180 		m_thr = 0;
       
   181 
       
   182 		return ret;
       
   183 	}
       
   184 
       
   185 	/* virtual */ bool IsCurrent()
       
   186 	{
       
   187 		return FindTask(NULL) == m_thr;
       
   188 	}
       
   189 
       
   190 	/* virtual */ uint GetId()
       
   191 	{
       
   192 		return (uint)m_thr;
       
   193 	}
       
   194 
       
   195 private:
       
   196 	/**
       
   197 	 * On thread creation, this function is called, which calls the real startup
       
   198 	 *  function. This to get back into the correct instance again.
       
   199 	 */
       
   200 	static void Proxy(void)
       
   201 	{
       
   202 		struct Task *child = FindTask(NULL);
       
   203 		struct OTTDThreadStartupMessage *msg;
       
   204 
       
   205 		/* Make sure, we don't block the parent. */
       
   206 		SetTaskPri(child, -5);
       
   207 
       
   208 		KPutStr("[Child] Progressing...\n");
       
   209 
       
   210 		if (NewGetTaskAttrs(NULL, &msg, sizeof(struct OTTDThreadStartupMessage *), TASKINFOTYPE_STARTUPMSG, TAG_DONE) && msg != NULL) {
       
   211 			try {
       
   212 				msg->ret = msg->func(msg->arg);
       
   213 			} catch(...) {
       
   214 				KPutStr("[Child] Returned to main()\n");
       
   215 			}
       
   216 		}
       
   217 
       
   218 		/*  Quit the child, exec.library will reply the startup msg internally. */
       
   219 		KPutStr("[Child] Done.\n");
       
   220 	}
       
   221 };
       
   222 
       
   223 /* static */ ThreadObject *ThreadObject::New(OTTDThreadFunc proc, void *param)
       
   224 {
       
   225 	return new ThreadObject_MorphOS(proc, param);
       
   226 }
       
   227 
       
   228 /* static */ ThreadObject *ThreadObject::AttachCurrent()
       
   229 {
       
   230 	return new ThreadObject_MorphOS();
       
   231 }
       
   232 
       
   233 /* static */ uint ThreadObject::CurrentId()
       
   234 {
       
   235 	return (uint) FindTask(NULL);
       
   236 }
       
   237 
       
   238 
       
   239 /**
       
   240  * MorphOS version of ThreadSemaphore.
       
   241  */
       
   242 class ThreadSemaphore_MorphOS : public ThreadSemaphore {
       
   243 private:
       
   244 	struct SignalSemaphore m_sem;
       
   245 
       
   246 public:
       
   247 	ThreadSemaphore_MorphOS()
       
   248 	{
       
   249 		InitSemaphore(&m_sem);
       
   250 	}
       
   251 
       
   252 	/* virtual */ ~ThreadSemaphore_MorphOS()
       
   253 	{
       
   254 
       
   255 	}
       
   256 
       
   257 	/* virtual */ void Set()
       
   258 	{
       
   259 		// Check if semaphore count is really important there.
       
   260 		ReleaseSemaphore(&m_sem);
       
   261 	}
       
   262 
       
   263 	/* virtual */ void Wait()
       
   264 	{
       
   265 		ObtainSemaphore(&m_sem);
       
   266 	}
       
   267 };
       
   268 
       
   269 /* static */ ThreadSemaphore *ThreadSemaphore::New()
       
   270 {
       
   271 	return new ThreadSemaphore_MorphOS();
       
   272 }