|
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 } |