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