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