OpenTTD
thread_morphos.cpp
Go to the documentation of this file.
1 /* $Id$ */
2 
3 /*
4  * This file is part of OpenTTD.
5  * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
6  * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
7  * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
8  */
9 
12 #include "../stdafx.h"
13 #include "thread.h"
14 #include "../debug.h"
15 #include "../core/alloc_func.hpp"
16 #include <stdlib.h>
17 #include <unistd.h>
18 
19 #include <exec/types.h>
20 #include <exec/rawfmt.h>
21 #include <dos/dostags.h>
22 
23 #include <proto/dos.h>
24 #include <proto/exec.h>
25 
26 #include "../safeguards.h"
27 
31 #undef Exit
32 #undef Wait
33 
34 
43  struct Message msg;
45  void *arg;
46 };
47 
48 
53 void KPutStr(CONST_STRPTR format)
54 {
55  RawDoFmt(format, NULL, (void (*)())RAWFMTFUNC_SERIAL, NULL);
56 }
57 
58 
63 private:
64  APTR m_thr;
65  struct MsgPort *m_replyport;
66  struct OTTDThreadStartupMessage m_msg;
67  bool self_destruct;
68 
69 public:
73  ThreadObject_MorphOS(OTTDThreadFunc proc, void *param, self_destruct) :
74  m_thr(0), self_destruct(self_destruct)
75  {
76  struct Task *parent;
77 
78  KPutStr("[OpenTTD] Create thread...\n");
79 
80  parent = FindTask(NULL);
81 
82  /* Make sure main thread runs with sane priority */
83  SetTaskPri(parent, 0);
84 
85  /* Things we'll pass down to the child by utilizing NP_StartupMsg */
86  m_msg.func = proc;
87  m_msg.arg = param;
88 
89  m_replyport = CreateMsgPort();
90 
91  if (m_replyport != NULL) {
92  struct Process *child;
93 
94  m_msg.msg.mn_Node.ln_Type = NT_MESSAGE;
95  m_msg.msg.mn_ReplyPort = m_replyport;
96  m_msg.msg.mn_Length = sizeof(struct OTTDThreadStartupMessage);
97 
98  child = CreateNewProcTags(
99  NP_CodeType, CODETYPE_PPC,
100  NP_Entry, ThreadObject_MorphOS::Proxy,
101  NP_StartupMsg, (IPTR)&m_msg,
102  NP_Priority, 5UL,
103  NP_Name, (IPTR)"OpenTTD Thread",
104  NP_PPCStackSize, 131072UL,
105  TAG_DONE);
106 
107  m_thr = (APTR) child;
108 
109  if (child != NULL) {
110  KPutStr("[OpenTTD] Child process launched.\n");
111  } else {
112  KPutStr("[OpenTTD] Couldn't create child process. (constructors never fail, yeah!)\n");
113  DeleteMsgPort(m_replyport);
114  }
115  }
116  }
117 
118  /* virtual */ ~ThreadObject_MorphOS()
119  {
120  }
121 
122  /* virtual */ bool Exit()
123  {
125 
126  /* You can only exit yourself */
127  assert(IsCurrent());
128 
129  KPutStr("[Child] Aborting...\n");
130 
131  if (NewGetTaskAttrs(NULL, &msg, sizeof(struct OTTDThreadStartupMessage *), TASKINFOTYPE_STARTUPMSG, TAG_DONE) && msg != NULL) {
132  /* For now we terminate by throwing an error, gives much cleaner cleanup */
133  throw OTTDThreadExitSignal();
134  }
135 
136  return true;
137  }
138 
139  /* virtual */ void Join()
140  {
141  struct OTTDThreadStartupMessage *reply;
142 
143  /* You cannot join yourself */
144  assert(!IsCurrent());
145 
146  KPutStr("[OpenTTD] Join threads...\n");
147  KPutStr("[OpenTTD] Wait for child to quit...\n");
148  WaitPort(m_replyport);
149 
150  GetMsg(m_replyport);
151  DeleteMsgPort(m_replyport);
152  m_thr = 0;
153  }
154 
155  /* virtual */ bool IsCurrent()
156  {
157  return FindTask(NULL) == m_thr;
158  }
159 
160 private:
165  static void Proxy()
166  {
167  struct Task *child = FindTask(NULL);
169 
170  /* Make sure, we don't block the parent. */
171  SetTaskPri(child, -5);
172 
173  KPutStr("[Child] Progressing...\n");
174 
175  if (NewGetTaskAttrs(NULL, &msg, sizeof(struct OTTDThreadStartupMessage *), TASKINFOTYPE_STARTUPMSG, TAG_DONE) && msg != NULL) {
176  try {
177  msg->func(msg->arg);
178  } catch(OTTDThreadExitSignal e) {
179  KPutStr("[Child] Returned to main()\n");
180  } catch(...) {
181  NOT_REACHED();
182  }
183  }
184 
185  /* Quit the child, exec.library will reply the startup msg internally. */
186  KPutStr("[Child] Done.\n");
187 
188  if (self_destruct) delete this;
189  }
190 };
191 
192 /* static */ bool ThreadObject::New(OTTDThreadFunc proc, void *param, ThreadObject **thread, const char *name)
193 {
194  ThreadObject *to = new ThreadObject_MorphOS(proc, param, thread == NULL);
195  if (thread != NULL) *thread = to;
196  return true;
197 }
void * arg
functions arguments for the thread function
void(* OTTDThreadFunc)(void *)
Definition of all thread entry functions.
Definition: thread.h:16
struct Message msg
standard exec.library message (MUST be the first thing in the message struct!)
Base of all threads.
MorphOS version for ThreadObject.
bool Exit()
Exit this thread.
APTR m_thr
System thread identifier.
OTTDThreadFunc func
function the thread will execute
avoid name clashes with MorphOS API functions
void Join()
Join this thread.
static void Proxy()
On thread creation, this function is called, which calls the real startup function.
ThreadObject_MorphOS(OTTDThreadFunc proc, void *param, self_destruct)
Create a sub process and start it, calling proc(param).
A Thread Object which works on all our supported OSes.
Definition: thread.h:24
Signal used for signalling we knowingly want to end the thread.
Definition: thread.h:19
static bool New(OTTDThreadFunc proc, void *param, ThreadObject **thread=NULL, const char *name=NULL)
Create a thread; proc will be called as first function inside the thread, with optional params...
void KPutStr(CONST_STRPTR format)
Default OpenTTD STDIO/ERR debug output is not very useful for this, so we utilize serial/ramdebug ins...