rubidium@8934: /* $Id$ */ rubidium@8934: rubidium@8934: /** @file fiber_thread.cpp ThreadObject implementation of Fiber. */ rubidium@8934: rubidium@8934: #include "stdafx.h" rubidium@8934: #include "fiber.hpp" rubidium@8934: #include "thread.h" rubidium@8934: #include rubidium@8934: rubidium@8934: class Fiber_Thread : public Fiber { rubidium@8934: private: rubidium@8934: ThreadObject *m_thread; rubidium@8934: FiberFunc m_proc; rubidium@8934: void *m_param; rubidium@8934: bool m_attached; rubidium@8934: ThreadSemaphore *m_sem; rubidium@8934: bool m_kill; rubidium@8934: rubidium@8934: static Fiber_Thread *s_current; rubidium@8934: static Fiber_Thread *s_main; rubidium@8934: rubidium@8934: public: rubidium@8934: /** rubidium@8934: * Create a ThreadObject fiber and start it, calling proc(param). rubidium@8934: */ rubidium@8934: Fiber_Thread(FiberFunc proc, void *param) : rubidium@8934: m_thread(NULL), rubidium@8934: m_proc(proc), rubidium@8934: m_param(param), rubidium@8934: m_attached(false), rubidium@8934: m_kill(false) rubidium@8934: { rubidium@8934: this->m_sem = ThreadSemaphore::New(); rubidium@8934: /* Create a thread and start stFiberProc */ rubidium@9479: this->m_thread = ThreadObject::New(&stFiberProc, this); rubidium@8934: } rubidium@8934: rubidium@8934: /** rubidium@8934: * Create a ThreadObject fiber and attach current thread to it. rubidium@8934: */ rubidium@8934: Fiber_Thread(void *param) : rubidium@8934: m_thread(NULL), rubidium@8934: m_proc(NULL), rubidium@8934: m_param(param), rubidium@8934: m_attached(true), rubidium@8934: m_kill(false) rubidium@8934: { rubidium@8934: this->m_sem = ThreadSemaphore::New(); rubidium@8934: /* Attach the current thread to this Fiber */ rubidium@8934: this->m_thread = ThreadObject::AttachCurrent(); rubidium@8934: /* We are the current thread */ rubidium@8934: if (s_current == NULL) s_current = this; rubidium@8934: if (s_main == NULL) s_main = this; rubidium@8934: } rubidium@8934: rubidium@8934: ~Fiber_Thread() rubidium@8934: { rubidium@8934: /* Remove the thread if needed */ rubidium@8934: if (this->m_thread != NULL) { rubidium@8934: assert(this->m_attached || !this->m_thread->IsRunning()); rubidium@8934: delete this->m_thread; rubidium@8934: } rubidium@8934: /* Remove the semaphore */ rubidium@8934: delete this->m_sem; rubidium@8934: } rubidium@8934: rubidium@8934: /* virtual */ void SwitchToFiber() rubidium@8934: { rubidium@8934: /* You can't switch to yourself */ rubidium@8934: assert(s_current != this); rubidium@8934: Fiber_Thread *cur = s_current; rubidium@8934: rubidium@8934: /* Continue the execution of 'this' Fiber */ rubidium@8934: this->m_sem->Set(); rubidium@8934: /* Hold the execution of the current Fiber */ rubidium@8934: cur->m_sem->Wait(); rubidium@8934: if (this->m_kill) { rubidium@8934: /* If the thread we switched too was killed, join it so it can finish quiting */ rubidium@8934: this->m_thread->Join(); rubidium@8934: } rubidium@8934: /* If we continue, we are the current thread */ rubidium@8934: s_current = cur; rubidium@8934: } rubidium@8934: rubidium@8934: /* virtual */ void Exit() rubidium@8934: { rubidium@8934: /* Kill off our thread */ rubidium@8934: this->m_kill = true; rubidium@8934: this->m_thread->Exit(); rubidium@8934: } rubidium@8934: rubidium@8934: /* virtual */ bool IsRunning() rubidium@8934: { rubidium@8934: if (this->m_thread == NULL) return false; rubidium@8934: return this->m_thread->IsRunning(); rubidium@8934: } rubidium@8934: rubidium@8934: /* virtual */ void *GetFiberData() rubidium@8934: { rubidium@8934: return this->m_param; rubidium@8934: } rubidium@8934: rubidium@8934: static Fiber_Thread *GetCurrentFiber() rubidium@8934: { rubidium@8934: return s_current; rubidium@8934: } rubidium@8934: rubidium@8934: private: rubidium@8934: /** rubidium@8934: * First function which is called within the fiber. rubidium@8934: */ rubidium@9476: static void stFiberProc(void *fiber) rubidium@8934: { rubidium@8934: Fiber_Thread *cur = (Fiber_Thread *)fiber; rubidium@8934: /* Now suspend the thread until we get SwitchToFiber() for the first time */ rubidium@8934: cur->m_sem->Wait(); rubidium@8934: /* If we continue, we are the current thread */ rubidium@8934: s_current = cur; rubidium@8934: rubidium@8934: try { rubidium@8934: cur->m_proc(cur->m_param); rubidium@8934: } catch (...) { rubidium@8934: /* Unlock the main thread */ rubidium@8934: s_main->m_sem->Set(); rubidium@8934: throw; rubidium@8934: } rubidium@8934: } rubidium@8934: }; rubidium@8934: rubidium@8934: /* Initialize the static member of Fiber_Thread */ rubidium@8934: /* static */ Fiber_Thread *Fiber_Thread::s_current = NULL; rubidium@8934: /* static */ Fiber_Thread *Fiber_Thread::s_main = NULL; rubidium@8934: rubidium@8934: #ifndef WIN32 rubidium@8934: rubidium@8934: /* static */ Fiber *Fiber::New(FiberFunc proc, void *param) rubidium@8934: { rubidium@8934: return new Fiber_Thread(proc, param); rubidium@8934: } rubidium@8934: rubidium@8934: /* static */ Fiber *Fiber::AttachCurrent(void *param) rubidium@8934: { rubidium@8934: return new Fiber_Thread(param); rubidium@8934: } rubidium@8934: rubidium@8934: /* static */ void *Fiber::GetCurrentFiberData() rubidium@8934: { rubidium@8934: return Fiber_Thread::GetCurrentFiber()->GetFiberData(); rubidium@8934: } rubidium@8934: rubidium@8934: #endif /* WIN32 */