# thread_morphos.cpp

Go to the documentation of this file.

    00001 /* $Id: thread_morphos.cpp 15158 2009-01-20 03:12:46Z rubidium $ */
    00002
    00005 #include "stdafx.h"
    00006 #include "thread.h"
    00007 #include "debug.h"
    00008 #include "core/alloc_func.hpp"
    00009 #include <stdlib.h>
    00010 #include <unistd.h>
    00011
    00012 #include <exec/types.h>
    00013 #include <exec/rawfmt.h>
    00014 #include <dos/dostags.h>
    00015
    00016 #include <proto/dos.h>
    00017 #include <proto/exec.h>
    00018
    00022 #undef Exit
    00023 #undef Wait
    00024
    00025
    00033 struct OTTDThreadStartupMessage {
    00034   struct Message msg;
    00035   OTTDThreadFunc func;
    00036   void *arg;
    00037 };
    00038
    00039
    00044 #ifndef NO_DEBUG_MESSAGES
    00045 void KPutStr(CONST_STRPTR format)
    00046 {
    00047   RawDoFmt(format, NULL, (void (*)())RAWFMTFUNC_SERIAL, NULL);
    00048 }
    00049 #else
    00050 #define KPutStr(x)
    00051 #endif
    00052
    00053
    00057 class ThreadObject_MorphOS : public ThreadObject {
    00058 private:
    00059   APTR m_thr;
    00060   struct MsgPort *m_replyport;
    00061   struct OTTDThreadStartupMessage m_msg;
    00062   bool self_destruct;
    00063
    00064 public:
    00068   ThreadObject_MorphOS(OTTDThreadFunc proc, void *param, self_destruct) :
    00069     m_thr(0), self_destruct(self_destruct)
    00070   {
    00071     struct Task *parent;
    00072
    00073     KPutStr("[OpenTTD] Create thread...\n");
    00074
    00075     parent = FindTask(NULL);
    00076
    00077     /* Make sure main thread runs with sane priority */
    00078     SetTaskPri(parent, 0);
    00079
    00080     /* Things we'll pass down to the child by utilizing NP_StartupMsg */
    00081     m_msg.func = proc;
    00082     m_msg.arg  = param;
    00083
    00084     m_replyport = CreateMsgPort();
    00085
    00086     if (m_replyport != NULL) {
    00087       struct Process *child;
    00088
    00089       m_msg.msg.mn_Node.ln_Type = NT_MESSAGE;
    00090       m_msg.msg.mn_ReplyPort    = m_replyport;
    00091       m_msg.msg.mn_Length       = sizeof(struct OTTDThreadStartupMessage);
    00092
    00093       child = CreateNewProcTags(
    00094         NP_CodeType,     CODETYPE_PPC,
    00095         NP_Entry,        ThreadObject_MorphOS::Proxy,
    00096         NP_StartupMsg,   (IPTR)&m_msg,
    00097         NP_Priority,     5UL,
    00098         NP_Name,         (IPTR)"OpenTTD Thread",
    00099         NP_PPCStackSize, 131072UL,
    00100         TAG_DONE);
    00101
    00102       m_thr = (APTR) child;
    00103
    00104       if (child != NULL) {
    00105         KPutStr("[OpenTTD] Child process launched.\n");
    00106       } else {
    00107         KPutStr("[OpenTTD] Couldn't create child process. (constructors never fail, yeah!)\n");
    00108         DeleteMsgPort(m_replyport);
    00109       }
    00110     }
    00111   }
    00112
    00113   /* virtual */ ~ThreadObject_MorphOS()
    00114   {
    00115   }
    00116
    00117   /* virtual */ bool Exit()
    00118   {
    00119     struct OTTDThreadStartupMessage *msg;
    00120
    00121     /* You can only exit yourself */
    00122     assert(IsCurrent());
    00123
    00124     KPutStr("[Child] Aborting...\n");
    00125
    00126     if (NewGetTaskAttrs(NULL, &msg, sizeof(struct OTTDThreadStartupMessage *), TASKINFOTYPE_STARTUPMSG, TAG_DONE) && msg != NULL) {
    00127       /* For now we terminate by throwing an error, gives much cleaner cleanup */
    00128       throw OTTDThreadExitSignal();
    00129     }
    00130
    00131     return true;
    00132   }
    00133
    00134   /* virtual */ void Join()
    00135   {
    00136     struct OTTDThreadStartupMessage *reply;
    00137
    00138     /* You cannot join yourself */
    00139     assert(!IsCurrent());
    00140
    00141     KPutStr("[OpenTTD] Join threads...\n");
    00142     KPutStr("[OpenTTD] Wait for child to quit...\n");
    00143     WaitPort(m_replyport);
    00144
    00145     GetMsg(m_replyport);
    00146     DeleteMsgPort(m_replyport);
    00147     m_thr = 0;
    00148   }
    00149
    00150   /* virtual */ bool IsCurrent()
    00151   {
    00152     return FindTask(NULL) == m_thr;
    00153   }
    00154
    00155 private:
    00160   static void Proxy(void)
    00161   {
    00162     struct Task *child = FindTask(NULL);
    00163     struct OTTDThreadStartupMessage *msg;
    00164
    00165     /* Make sure, we don't block the parent. */
    00166     SetTaskPri(child, -5);
    00167
    00168     KPutStr("[Child] Progressing...\n");
    00169
    00170     if (NewGetTaskAttrs(NULL, &msg, sizeof(struct OTTDThreadStartupMessage *), TASKINFOTYPE_STARTUPMSG, TAG_DONE) && msg != NULL) {
    00171       try {
    00172         msg->func(msg->arg);
    00173       } catch(OTTDThreadExitSignal e) {
    00174         KPutStr("[Child] Returned to main()\n");
    00175       } catch(...) {
    00176         NOT_REACHED();
    00177       }
    00178     }
    00179
    00180     /*  Quit the child, exec.library will reply the startup msg internally. */
    00181     KPutStr("[Child] Done.\n");
    00182
    00183     if (self_destruct) delete this;
    00184   }
    00185 };
    00186
    00187 /* static */ bool ThreadObject::New(OTTDThreadFunc proc, void *param, ThreadObject **thread)
    00188 {
    00189   ThreadObject *to = new ThreadObject_MorphOS(proc, param, thread == NULL);
    00190   if (thread != NULL) *thread = to;
    00191   return true;
    00192 }