153 |
155 |
154 void OTTDExitThread() |
156 void OTTDExitThread() |
155 { |
157 { |
156 ExitThread(0); |
158 ExitThread(0); |
157 } |
159 } |
|
160 |
|
161 |
|
162 #elif defined(MORPHOS) |
|
163 |
|
164 #include <exec/types.h> |
|
165 #include <exec/rawfmt.h> |
|
166 #include <dos/dostags.h> |
|
167 |
|
168 #include <proto/dos.h> |
|
169 #include <proto/exec.h> |
|
170 |
|
171 #include <setjmp.h> |
|
172 |
|
173 /* NOTE: this code heavily depends on latest libnix updates. So make |
|
174 * sure you link with new stuff which supports semaphore locking of |
|
175 * the IO resources, else it will just go foobar. */ |
|
176 |
|
177 struct OTTDThreadStartupMessage { |
|
178 struct Message msg; ///< standard exec.library message (MUST be the first thing in the message struct!) |
|
179 OTTDThreadFunc func; ///< function the thread will execute |
|
180 void *arg; ///< functions arguments for the thread function |
|
181 void *ret; ///< return value of the thread function |
|
182 jmp_buf jumpstore; ///< storage for the setjump state |
|
183 }; |
|
184 |
|
185 struct OTTDThread { |
|
186 struct MsgPort *replyport; |
|
187 struct OTTDThreadStartupMessage msg; |
|
188 }; |
|
189 |
|
190 |
|
191 /** |
|
192 * Default OpenTTD STDIO/ERR debug output is not very useful for this, so we |
|
193 * utilize serial/ramdebug instead. |
|
194 */ |
|
195 #ifndef NO_DEBUG_MESSAGES |
|
196 void KPutStr(CONST_STRPTR format) |
|
197 { |
|
198 RawDoFmt(format, NULL, (void (*)())RAWFMTFUNC_SERIAL, NULL); |
|
199 } |
|
200 #else |
|
201 #define KPutStr(x) |
158 #endif |
202 #endif |
|
203 |
|
204 static void Proxy(void) |
|
205 { |
|
206 struct Task *child = FindTask(NULL); |
|
207 struct OTTDThreadStartupMessage *msg; |
|
208 |
|
209 /* Make sure, we don't block the parent. */ |
|
210 SetTaskPri(child, -5); |
|
211 |
|
212 KPutStr("[Child] Progressing...\n"); |
|
213 |
|
214 if (NewGetTaskAttrs(NULL, &msg, sizeof(struct OTTDThreadStartupMessage *), TASKINFOTYPE_STARTUPMSG, TAG_DONE) && msg != NULL) { |
|
215 /* Make use of setjmp() here, so this point can be reached again from inside |
|
216 * OTTDExitThread() which can be called from anythere inside msg->func. |
|
217 * It's a bit ugly and in worst case it leaks some memory. */ |
|
218 if (setjmp(msg->jumpstore) == 0) { |
|
219 msg->ret = msg->func(msg->arg); |
|
220 } else { |
|
221 KPutStr("[Child] Returned to main()\n"); |
|
222 } |
|
223 } |
|
224 |
|
225 /* Quit the child, exec.library will reply the startup msg internally. */ |
|
226 KPutStr("[Child] Done.\n"); |
|
227 } |
|
228 |
|
229 OTTDThread* OTTDCreateThread(OTTDThreadFunc function, void *arg) |
|
230 { |
|
231 OTTDThread *t; |
|
232 struct Task *parent; |
|
233 |
|
234 KPutStr("[OpenTTD] Create thread...\n"); |
|
235 |
|
236 t = (struct OTTDThread *)AllocVecTaskPooled(sizeof(struct OTTDThread)); |
|
237 if (t == NULL) return NULL; |
|
238 |
|
239 parent = FindTask(NULL); |
|
240 |
|
241 /* Make sure main thread runs with sane priority */ |
|
242 SetTaskPri(parent, 0); |
|
243 |
|
244 /* Things we'll pass down to the child by utilizing NP_StartupMsg */ |
|
245 t->msg.func = function; |
|
246 t->msg.arg = arg; |
|
247 t->msg.ret = NULL; |
|
248 |
|
249 t->replyport = CreateMsgPort(); |
|
250 |
|
251 if (t->replyport != NULL) { |
|
252 struct Process *child; |
|
253 |
|
254 t->msg.msg.mn_Node.ln_Type = NT_MESSAGE; |
|
255 t->msg.msg.mn_ReplyPort = t->replyport; |
|
256 t->msg.msg.mn_Length = sizeof(struct OTTDThreadStartupMessage); |
|
257 |
|
258 child = CreateNewProcTags( |
|
259 NP_CodeType, CODETYPE_PPC, |
|
260 NP_Entry, Proxy, |
|
261 NP_StartupMsg, (ULONG)&t->msg, |
|
262 NP_Priority, 5UL, |
|
263 NP_Name, (ULONG)"OpenTTD Thread", |
|
264 NP_PPCStackSize, 131072UL, |
|
265 TAG_DONE); |
|
266 |
|
267 if (child != NULL) { |
|
268 KPutStr("[OpenTTD] Child process launched.\n"); |
|
269 return t; |
|
270 } |
|
271 DeleteMsgPort(t->replyport); |
|
272 } |
|
273 FreeVecTaskPooled(t); |
|
274 |
|
275 return NULL; |
|
276 } |
|
277 |
|
278 void* OTTDJoinThread(OTTDThread *t) |
|
279 { |
|
280 struct OTTDThreadStartupMessage *reply; |
|
281 void *ret; |
|
282 |
|
283 KPutStr("[OpenTTD] Join threads...\n"); |
|
284 |
|
285 if (t == NULL) return NULL; |
|
286 |
|
287 KPutStr("[OpenTTD] Wait for child to quit...\n"); |
|
288 WaitPort(t->replyport); |
|
289 |
|
290 reply = (struct OTTDThreadStartupMessage *)GetMsg(t->replyport); |
|
291 ret = reply->ret; |
|
292 |
|
293 DeleteMsgPort(t->replyport); |
|
294 FreeVecTaskPooled(t); |
|
295 |
|
296 return ret; |
|
297 } |
|
298 |
|
299 void OTTDExitThread() |
|
300 { |
|
301 struct OTTDThreadStartupMessage *msg; |
|
302 |
|
303 KPutStr("[Child] Aborting...\n"); |
|
304 |
|
305 if (NewGetTaskAttrs(NULL, &msg, sizeof(struct OTTDThreadStartupMessage *), TASKINFOTYPE_STARTUPMSG, TAG_DONE) && msg != NULL) { |
|
306 KPutStr("[Child] Jumping back...\n"); |
|
307 longjmp(msg->jumpstore, 0xBEAFCAFE); |
|
308 } |
|
309 |
|
310 NOT_REACHED(); |
|
311 } |
|
312 |
|
313 #endif |