OpenTTD
thread_morphos.cpp
Go to the documentation of this file.
1 /* $Id: thread_morphos.cpp 27670 2016-10-30 17:29:33Z frosch $ */
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 #ifndef NO_DEBUG_MESSAGES
54 void KPutStr(CONST_STRPTR format)
55 {
56  RawDoFmt(format, NULL, (void (*)())RAWFMTFUNC_SERIAL, NULL);
57 }
58 #else
59 #define KPutStr(x)
60 #endif
61 
62 
67 private:
68  APTR m_thr;
69  struct MsgPort *m_replyport;
70  struct OTTDThreadStartupMessage m_msg;
71  bool self_destruct;
72 
73 public:
77  ThreadObject_MorphOS(OTTDThreadFunc proc, void *param, self_destruct) :
78  m_thr(0), self_destruct(self_destruct)
79  {
80  struct Task *parent;
81 
82  KPutStr("[OpenTTD] Create thread...\n");
83 
84  parent = FindTask(NULL);
85 
86  /* Make sure main thread runs with sane priority */
87  SetTaskPri(parent, 0);
88 
89  /* Things we'll pass down to the child by utilizing NP_StartupMsg */
90  m_msg.func = proc;
91  m_msg.arg = param;
92 
93  m_replyport = CreateMsgPort();
94 
95  if (m_replyport != NULL) {
96  struct Process *child;
97 
98  m_msg.msg.mn_Node.ln_Type = NT_MESSAGE;
99  m_msg.msg.mn_ReplyPort = m_replyport;
100  m_msg.msg.mn_Length = sizeof(struct OTTDThreadStartupMessage);
101 
102  child = CreateNewProcTags(
103  NP_CodeType, CODETYPE_PPC,
104  NP_Entry, ThreadObject_MorphOS::Proxy,
105  NP_StartupMsg, (IPTR)&m_msg,
106  NP_Priority, 5UL,
107  NP_Name, (IPTR)"OpenTTD Thread",
108  NP_PPCStackSize, 131072UL,
109  TAG_DONE);
110 
111  m_thr = (APTR) child;
112 
113  if (child != NULL) {
114  KPutStr("[OpenTTD] Child process launched.\n");
115  } else {
116  KPutStr("[OpenTTD] Couldn't create child process. (constructors never fail, yeah!)\n");
117  DeleteMsgPort(m_replyport);
118  }
119  }
120  }
121 
122  /* virtual */ ~ThreadObject_MorphOS()
123  {
124  }
125 
126  /* virtual */ bool Exit()
127  {
129 
130  /* You can only exit yourself */
131  assert(IsCurrent());
132 
133  KPutStr("[Child] Aborting...\n");
134 
135  if (NewGetTaskAttrs(NULL, &msg, sizeof(struct OTTDThreadStartupMessage *), TASKINFOTYPE_STARTUPMSG, TAG_DONE) && msg != NULL) {
136  /* For now we terminate by throwing an error, gives much cleaner cleanup */
137  throw OTTDThreadExitSignal();
138  }
139 
140  return true;
141  }
142 
143  /* virtual */ void Join()
144  {
145  struct OTTDThreadStartupMessage *reply;
146 
147  /* You cannot join yourself */
148  assert(!IsCurrent());
149 
150  KPutStr("[OpenTTD] Join threads...\n");
151  KPutStr("[OpenTTD] Wait for child to quit...\n");
152  WaitPort(m_replyport);
153 
154  GetMsg(m_replyport);
155  DeleteMsgPort(m_replyport);
156  m_thr = 0;
157  }
158 
159  /* virtual */ bool IsCurrent()
160  {
161  return FindTask(NULL) == m_thr;
162  }
163 
164 private:
169  static void Proxy()
170  {
171  struct Task *child = FindTask(NULL);
173 
174  /* Make sure, we don't block the parent. */
175  SetTaskPri(child, -5);
176 
177  KPutStr("[Child] Progressing...\n");
178 
179  if (NewGetTaskAttrs(NULL, &msg, sizeof(struct OTTDThreadStartupMessage *), TASKINFOTYPE_STARTUPMSG, TAG_DONE) && msg != NULL) {
180  try {
181  msg->func(msg->arg);
182  } catch(OTTDThreadExitSignal e) {
183  KPutStr("[Child] Returned to main()\n");
184  } catch(...) {
185  NOT_REACHED();
186  }
187  }
188 
189  /* Quit the child, exec.library will reply the startup msg internally. */
190  KPutStr("[Child] Done.\n");
191 
192  if (self_destruct) delete this;
193  }
194 };
195 
196 /* static */ bool ThreadObject::New(OTTDThreadFunc proc, void *param, ThreadObject **thread, const char *name)
197 {
198  ThreadObject *to = new ThreadObject_MorphOS(proc, param, thread == NULL);
199  if (thread != NULL) *thread = to;
200  return true;
201 }
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...