rubidium@10438: /* $Id$ */ rubidium@10438: rubidium@10438: /** @file thread_morphos.cpp MorphOS implementation of Threads. */ rubidium@10438: rubidium@10438: #include "stdafx.h" rubidium@10438: #include "thread.h" rubidium@10438: #include "debug.h" rubidium@10438: #include "core/alloc_func.hpp" rubidium@10438: #include rubidium@10438: #include rubidium@10438: rubidium@10438: #include rubidium@10438: #include rubidium@10438: #include rubidium@10438: rubidium@10438: #include rubidium@10438: #include rubidium@10438: rubidium@10438: /** rubidium@10438: * avoid name clashes with MorphOS API functions rubidium@10438: */ rubidium@10438: #undef Exit rubidium@10438: #undef Wait rubidium@10438: rubidium@10438: rubidium@10438: /** rubidium@10438: * NOTE: this code heavily depends on latest libnix updates. So make rubidium@10438: * sure you link with new stuff which supports semaphore locking of rubidium@10438: * the IO resources, else it will just go foobar. rubidium@10438: */ rubidium@10438: rubidium@10438: rubidium@10438: struct OTTDThreadStartupMessage { rubidium@10438: struct Message msg; ///< standard exec.library message (MUST be the first thing in the message struct!) rubidium@10438: OTTDThreadFunc func; ///< function the thread will execute rubidium@10438: void *arg; ///< functions arguments for the thread function rubidium@10438: }; rubidium@10438: rubidium@10438: rubidium@10438: /** rubidium@10438: * Default OpenTTD STDIO/ERR debug output is not very useful for this, so we rubidium@10438: * utilize serial/ramdebug instead. rubidium@10438: */ rubidium@10438: #ifndef NO_DEBUG_MESSAGES rubidium@10438: void KPutStr(CONST_STRPTR format) rubidium@10438: { rubidium@10438: RawDoFmt(format, NULL, (void (*)())RAWFMTFUNC_SERIAL, NULL); rubidium@10438: } rubidium@10438: #else rubidium@10438: #define KPutStr(x) rubidium@10438: #endif rubidium@10438: rubidium@10438: rubidium@10438: /** rubidium@10438: * MorphOS version for ThreadObject. rubidium@10438: */ rubidium@10438: class ThreadObject_MorphOS : public ThreadObject { rubidium@10438: private: rubidium@10438: APTR m_thr; ///< System thread identifier. rubidium@10438: struct MsgPort *m_replyport; rubidium@10438: struct OTTDThreadStartupMessage m_msg; rubidium@10438: rubidium@10438: public: rubidium@10438: /** rubidium@10438: * Create a sub process and start it, calling proc(param). rubidium@10438: */ rubidium@10866: ThreadObject_MorphOS(OTTDThreadFunc proc, void *param) : m_thr(0) rubidium@10438: { rubidium@10438: struct Task *parent; rubidium@10438: rubidium@10438: KPutStr("[OpenTTD] Create thread...\n"); rubidium@10438: rubidium@10438: parent = FindTask(NULL); rubidium@10438: rubidium@10438: /* Make sure main thread runs with sane priority */ rubidium@10438: SetTaskPri(parent, 0); rubidium@10438: rubidium@10438: /* Things we'll pass down to the child by utilizing NP_StartupMsg */ rubidium@10438: m_msg.func = proc; rubidium@10438: m_msg.arg = param; rubidium@10438: rubidium@10438: m_replyport = CreateMsgPort(); rubidium@10438: rubidium@10438: if (m_replyport != NULL) { rubidium@10438: struct Process *child; rubidium@10438: rubidium@10438: m_msg.msg.mn_Node.ln_Type = NT_MESSAGE; rubidium@10438: m_msg.msg.mn_ReplyPort = m_replyport; rubidium@10438: m_msg.msg.mn_Length = sizeof(struct OTTDThreadStartupMessage); rubidium@10438: rubidium@10438: child = CreateNewProcTags( rubidium@10438: NP_CodeType, CODETYPE_PPC, rubidium@10438: NP_Entry, ThreadObject_MorphOS::Proxy, rubidium@10438: NP_StartupMsg, (IPTR)&m_msg, rubidium@10438: NP_Priority, 5UL, rubidium@10438: NP_Name, (IPTR)"OpenTTD Thread", rubidium@10438: NP_PPCStackSize, 131072UL, rubidium@10438: TAG_DONE); rubidium@10438: rubidium@10438: m_thr = (APTR) child; rubidium@10438: rubidium@10438: if (child != NULL) { rubidium@10438: KPutStr("[OpenTTD] Child process launched.\n"); rubidium@10438: } else { rubidium@10438: KPutStr("[OpenTTD] Couldn't create child process. (constructors never fail, yeah!)\n"); rubidium@10438: DeleteMsgPort(m_replyport); rubidium@10438: } rubidium@10438: } rubidium@10438: } rubidium@10438: rubidium@10438: /** rubidium@10438: * Create a thread and attach current thread to it. rubidium@10438: */ rubidium@10866: ThreadObject_MorphOS() : m_thr(0) rubidium@10438: { rubidium@10438: m_thr = FindTask(NULL); rubidium@10438: } rubidium@10438: rubidium@10438: /* virtual */ ~ThreadObject_MorphOS() rubidium@10438: { rubidium@10438: } rubidium@10438: rubidium@10438: /* virtual */ bool IsRunning() rubidium@10438: { rubidium@10438: return m_thr != 0; rubidium@10438: } rubidium@10438: rubidium@10438: /* virtual */ bool WaitForStop() rubidium@10438: { rubidium@10438: /* You can't wait on yourself */ rubidium@10438: assert(!IsCurrent()); rubidium@10438: /* If the thread is not running, waiting is over */ rubidium@10438: if (!IsRunning()) return true; rubidium@10438: rubidium@10438: WaitPort(m_replyport); rubidium@10438: rubidium@10438: GetMsg(m_replyport); rubidium@10438: DeleteMsgPort(m_replyport); rubidium@10438: rubidium@10438: return true; rubidium@10438: } rubidium@10438: rubidium@10438: /* virtual */ bool Exit() rubidium@10438: { rubidium@10438: struct OTTDThreadStartupMessage *msg; rubidium@10438: rubidium@10438: /* You can only exit yourself */ rubidium@10438: assert(IsCurrent()); rubidium@10438: /* If the thread is not running, we are already closed */ rubidium@10438: if (!IsRunning()) return false; rubidium@10438: rubidium@10438: KPutStr("[Child] Aborting...\n"); rubidium@10438: rubidium@10438: if (NewGetTaskAttrs(NULL, &msg, sizeof(struct OTTDThreadStartupMessage *), TASKINFOTYPE_STARTUPMSG, TAG_DONE) && msg != NULL) { rubidium@10438: /* For now we terminate by throwing an error, gives much cleaner cleanup */ rubidium@10438: throw 0; rubidium@10438: } rubidium@10438: rubidium@10438: return true; rubidium@10438: } rubidium@10438: rubidium@10860: /* virtual */ void Join() rubidium@10438: { rubidium@10438: struct OTTDThreadStartupMessage *reply; rubidium@10438: rubidium@10438: /* You cannot join yourself */ rubidium@10438: assert(!IsCurrent()); rubidium@10438: rubidium@10438: KPutStr("[OpenTTD] Join threads...\n"); rubidium@10438: KPutStr("[OpenTTD] Wait for child to quit...\n"); rubidium@10438: WaitPort(m_replyport); rubidium@10438: rubidium@10860: GetMsg(m_replyport); rubidium@10438: DeleteMsgPort(m_replyport); rubidium@10438: m_thr = 0; rubidium@10438: } rubidium@10438: rubidium@10438: /* virtual */ bool IsCurrent() rubidium@10438: { rubidium@10438: return FindTask(NULL) == m_thr; rubidium@10438: } rubidium@10438: rubidium@10438: /* virtual */ uint GetId() rubidium@10438: { rubidium@10438: return (uint)m_thr; rubidium@10438: } rubidium@10438: rubidium@10438: private: rubidium@10438: /** rubidium@10438: * On thread creation, this function is called, which calls the real startup rubidium@10438: * function. This to get back into the correct instance again. rubidium@10438: */ rubidium@10438: static void Proxy(void) rubidium@10438: { rubidium@10438: struct Task *child = FindTask(NULL); rubidium@10438: struct OTTDThreadStartupMessage *msg; rubidium@10438: rubidium@10438: /* Make sure, we don't block the parent. */ rubidium@10438: SetTaskPri(child, -5); rubidium@10438: rubidium@10438: KPutStr("[Child] Progressing...\n"); rubidium@10438: rubidium@10438: if (NewGetTaskAttrs(NULL, &msg, sizeof(struct OTTDThreadStartupMessage *), TASKINFOTYPE_STARTUPMSG, TAG_DONE) && msg != NULL) { rubidium@10438: try { rubidium@10860: msg->func(msg->arg); rubidium@10438: } catch(...) { rubidium@10438: KPutStr("[Child] Returned to main()\n"); rubidium@10438: } rubidium@10438: } rubidium@10438: rubidium@10438: /* Quit the child, exec.library will reply the startup msg internally. */ rubidium@10438: KPutStr("[Child] Done.\n"); rubidium@10438: } rubidium@10438: }; rubidium@10438: rubidium@10866: /* static */ ThreadObject *ThreadObject::New(OTTDThreadFunc proc, void *param) rubidium@10438: { rubidium@10866: return new ThreadObject_MorphOS(proc, param); rubidium@10438: } rubidium@10438: rubidium@10438: /* static */ ThreadObject *ThreadObject::AttachCurrent() rubidium@10438: { rubidium@10438: return new ThreadObject_MorphOS(); rubidium@10438: } rubidium@10438: rubidium@10438: /* static */ uint ThreadObject::CurrentId() rubidium@10438: { rubidium@10438: return (uint) FindTask(NULL); rubidium@10438: } rubidium@10438: rubidium@10438: rubidium@10438: /** rubidium@10438: * MorphOS version of ThreadSemaphore. rubidium@10438: */ rubidium@10438: class ThreadSemaphore_MorphOS : public ThreadSemaphore { rubidium@10438: private: rubidium@10438: struct SignalSemaphore m_sem; rubidium@10438: rubidium@10438: public: rubidium@10438: ThreadSemaphore_MorphOS() rubidium@10438: { rubidium@10438: InitSemaphore(&m_sem); rubidium@10438: } rubidium@10438: rubidium@10438: /* virtual */ ~ThreadSemaphore_MorphOS() rubidium@10438: { rubidium@10438: rubidium@10438: } rubidium@10438: rubidium@10438: /* virtual */ void Set() rubidium@10438: { rubidium@10860: /* Check if semaphore count is really important there. */ rubidium@10438: ReleaseSemaphore(&m_sem); rubidium@10438: } rubidium@10438: rubidium@10438: /* virtual */ void Wait() rubidium@10438: { rubidium@10438: ObtainSemaphore(&m_sem); rubidium@10438: } rubidium@10438: }; rubidium@10438: rubidium@10438: /* static */ ThreadSemaphore *ThreadSemaphore::New() rubidium@10438: { rubidium@10438: return new ThreadSemaphore_MorphOS(); rubidium@10438: }