|
1 /* $Id$ */ |
|
2 |
|
3 /** @file fiber_thread.cpp ThreadObject implementation of Fiber. */ |
|
4 |
|
5 #include "stdafx.h" |
|
6 #include "fiber.hpp" |
|
7 #include "thread.h" |
|
8 #include <stdlib.h> |
|
9 |
|
10 class Fiber_Thread : public Fiber { |
|
11 private: |
|
12 ThreadObject *m_thread; |
|
13 FiberFunc m_proc; |
|
14 void *m_param; |
|
15 bool m_attached; |
|
16 ThreadSemaphore *m_sem; |
|
17 bool m_kill; |
|
18 |
|
19 static Fiber_Thread *s_current; |
|
20 static Fiber_Thread *s_main; |
|
21 |
|
22 public: |
|
23 /** |
|
24 * Create a ThreadObject fiber and start it, calling proc(param). |
|
25 */ |
|
26 Fiber_Thread(FiberFunc proc, void *param) : |
|
27 m_thread(NULL), |
|
28 m_proc(proc), |
|
29 m_param(param), |
|
30 m_attached(false), |
|
31 m_kill(false) |
|
32 { |
|
33 this->m_sem = ThreadSemaphore::New(); |
|
34 /* Create a thread and start stFiberProc */ |
|
35 this->m_thread = ThreadObject::New(&stFiberProc, this); |
|
36 } |
|
37 |
|
38 /** |
|
39 * Create a ThreadObject fiber and attach current thread to it. |
|
40 */ |
|
41 Fiber_Thread(void *param) : |
|
42 m_thread(NULL), |
|
43 m_proc(NULL), |
|
44 m_param(param), |
|
45 m_attached(true), |
|
46 m_kill(false) |
|
47 { |
|
48 this->m_sem = ThreadSemaphore::New(); |
|
49 /* Attach the current thread to this Fiber */ |
|
50 this->m_thread = ThreadObject::AttachCurrent(); |
|
51 /* We are the current thread */ |
|
52 if (s_current == NULL) s_current = this; |
|
53 if (s_main == NULL) s_main = this; |
|
54 } |
|
55 |
|
56 ~Fiber_Thread() |
|
57 { |
|
58 /* Remove the thread if needed */ |
|
59 if (this->m_thread != NULL) { |
|
60 assert(this->m_attached || !this->m_thread->IsRunning()); |
|
61 delete this->m_thread; |
|
62 } |
|
63 /* Remove the semaphore */ |
|
64 delete this->m_sem; |
|
65 } |
|
66 |
|
67 /* virtual */ void SwitchToFiber() |
|
68 { |
|
69 /* You can't switch to yourself */ |
|
70 assert(s_current != this); |
|
71 Fiber_Thread *cur = s_current; |
|
72 |
|
73 /* Continue the execution of 'this' Fiber */ |
|
74 this->m_sem->Set(); |
|
75 /* Hold the execution of the current Fiber */ |
|
76 cur->m_sem->Wait(); |
|
77 if (this->m_kill) { |
|
78 /* If the thread we switched too was killed, join it so it can finish quiting */ |
|
79 this->m_thread->Join(); |
|
80 } |
|
81 /* If we continue, we are the current thread */ |
|
82 s_current = cur; |
|
83 } |
|
84 |
|
85 /* virtual */ void Exit() |
|
86 { |
|
87 /* Kill off our thread */ |
|
88 this->m_kill = true; |
|
89 this->m_thread->Exit(); |
|
90 } |
|
91 |
|
92 /* virtual */ bool IsRunning() |
|
93 { |
|
94 if (this->m_thread == NULL) return false; |
|
95 return this->m_thread->IsRunning(); |
|
96 } |
|
97 |
|
98 /* virtual */ void *GetFiberData() |
|
99 { |
|
100 return this->m_param; |
|
101 } |
|
102 |
|
103 static Fiber_Thread *GetCurrentFiber() |
|
104 { |
|
105 return s_current; |
|
106 } |
|
107 |
|
108 private: |
|
109 /** |
|
110 * First function which is called within the fiber. |
|
111 */ |
|
112 static void * CDECL stFiberProc(void *fiber) |
|
113 { |
|
114 Fiber_Thread *cur = (Fiber_Thread *)fiber; |
|
115 /* Now suspend the thread until we get SwitchToFiber() for the first time */ |
|
116 cur->m_sem->Wait(); |
|
117 /* If we continue, we are the current thread */ |
|
118 s_current = cur; |
|
119 |
|
120 try { |
|
121 cur->m_proc(cur->m_param); |
|
122 } catch (...) { |
|
123 /* Unlock the main thread */ |
|
124 s_main->m_sem->Set(); |
|
125 throw; |
|
126 } |
|
127 |
|
128 return NULL; |
|
129 } |
|
130 }; |
|
131 |
|
132 /* Initialize the static member of Fiber_Thread */ |
|
133 /* static */ Fiber_Thread *Fiber_Thread::s_current = NULL; |
|
134 /* static */ Fiber_Thread *Fiber_Thread::s_main = NULL; |
|
135 |
|
136 #ifndef WIN32 |
|
137 |
|
138 /* static */ Fiber *Fiber::New(FiberFunc proc, void *param) |
|
139 { |
|
140 return new Fiber_Thread(proc, param); |
|
141 } |
|
142 |
|
143 /* static */ Fiber *Fiber::AttachCurrent(void *param) |
|
144 { |
|
145 return new Fiber_Thread(param); |
|
146 } |
|
147 |
|
148 /* static */ void *Fiber::GetCurrentFiberData() |
|
149 { |
|
150 return Fiber_Thread::GetCurrentFiber()->GetFiberData(); |
|
151 } |
|
152 |
|
153 #endif /* WIN32 */ |