Skip to content
Newer
Older
100644 217 lines (194 sloc) 5.4 KB
906b73a @laverdet Initial commit
authored
1 #include "coroutine.h"
2 #include <assert.h>
a41b3a9 @japj Tentative Windows support
japj authored
3 #ifdef USE_CORO
906b73a @laverdet Initial commit
authored
4 #include <pthread.h>
a41b3a9 @japj Tentative Windows support
japj authored
5 #endif
6 #ifdef USE_WINFIBER
7 #include <windows.h>
8 // Pretend Windows TLS is pthreads. Note that pthread_key_create() skips the dtor, but this doesn't
9 // matter for our application.
10 #define pthread_key_t DWORD
11 #define pthread_key_create(x,y) (*x)=TlsAlloc()
12 #define pthread_setspecific(x,y) TlsSetValue((x), (y))
13 #define pthread_getspecific(x) TlsGetValue((x))
14 #endif
906b73a @laverdet Initial commit
authored
15
16 #include <stdexcept>
17 #include <stack>
18 #include <vector>
19
20 #include <iostream>
21 using namespace std;
22
df58a46 @laverdet Compatibility with node-0.5.2
authored
23 static pthread_key_t floor_thread_key = NULL;
24 static pthread_key_t ceil_thread_key = NULL;
906b73a @laverdet Initial commit
authored
25
df58a46 @laverdet Compatibility with node-0.5.2
authored
26 static size_t stack_size = 0;
42f7111 @laverdet Add run-time information for fiber pool
authored
27 static size_t coroutines_created_ = 0;
df58a46 @laverdet Compatibility with node-0.5.2
authored
28 static vector<Coroutine*> fiber_pool;
29 static Coroutine* delete_me = NULL;
42f7111 @laverdet Add run-time information for fiber pool
authored
30 size_t Coroutine::pool_size = 120;
906b73a @laverdet Initial commit
authored
31
32 /**
33 * Coroutine class definition
34 */
df58a46 @laverdet Compatibility with node-0.5.2
authored
35 void Coroutine::init() {
36 pthread_key_create(&ceil_thread_key, NULL);
37 pthread_setspecific(ceil_thread_key, &current());
26b82d2 @laverdet Removal of node-fibers wrapper
authored
38 // Assume that v8 registered their TLS keys within the past 5 keys.. if not there's trouble.
39 if (ceil_thread_key > 5) {
40 floor_thread_key = ceil_thread_key - 5;
41 } else {
42 floor_thread_key = 0;
43 }
906b73a @laverdet Initial commit
authored
44 }
45
46 Coroutine& Coroutine::current() {
df58a46 @laverdet Compatibility with node-0.5.2
authored
47 Coroutine* current = static_cast<Coroutine*>(pthread_getspecific(ceil_thread_key));
48 if (!current) {
49 current = new Coroutine;
50 pthread_setspecific(ceil_thread_key, current);
51 }
52 return *current;
906b73a @laverdet Initial commit
authored
53 }
54
df58a46 @laverdet Compatibility with node-0.5.2
authored
55 void Coroutine::set_stack_size(size_t size) {
56 assert(!stack_size);
8b12067 @laverdet CORO_PTHREAD compatibility
authored
57 #ifdef CORO_PTHREAD
58 size += 1024 * 64;
59 #endif
df58a46 @laverdet Compatibility with node-0.5.2
authored
60 stack_size = size;
906b73a @laverdet Initial commit
authored
61 }
62
42f7111 @laverdet Add run-time information for fiber pool
authored
63 size_t Coroutine::coroutines_created() {
64 return coroutines_created_;
65 }
66
df58a46 @laverdet Compatibility with node-0.5.2
authored
67 void Coroutine::trampoline(void* that) {
8b12067 @laverdet CORO_PTHREAD compatibility
authored
68 #ifdef CORO_PTHREAD
69 pthread_setspecific(ceil_thread_key, that);
70 #endif
a41b3a9 @japj Tentative Windows support
japj authored
71 #ifdef USE_WINFIBER
e9a4d43 @laverdet Correct stack base calculations for Windows
authored
72 // I can't figure out how to get the precise base of the stack in Windows. Since CreateFiber
73 // creates the stack automatically we don't have access to the base. We can however grab the
74 // current esp position, and use that as an approximation. Padding is added for safety since the
75 // base is slightly different.
76 static_cast<Coroutine*>(that)->stack_base = (char*)_AddressOfReturnAddress() - stack_size + 128;
a41b3a9 @japj Tentative Windows support
japj authored
77 #endif
df58a46 @laverdet Compatibility with node-0.5.2
authored
78 while (true) {
79 static_cast<Coroutine*>(that)->entry(const_cast<void*>(static_cast<Coroutine*>(that)->arg));
80 }
7a06972 @laverdet Workaround v8 bugs with small stacks
authored
81 }
82
a41b3a9 @japj Tentative Windows support
japj authored
83 Coroutine::Coroutine() :
84 entry(NULL),
85 arg(NULL) {
86 #ifdef USE_CORO
df58a46 @laverdet Compatibility with node-0.5.2
authored
87 coro_create(&context, NULL, NULL, NULL, NULL);
a41b3a9 @japj Tentative Windows support
japj authored
88 #endif
89 #ifdef USE_WINFIBER
90 context = ConvertThreadToFiber(NULL);
91 #endif
df58a46 @laverdet Compatibility with node-0.5.2
authored
92 }
906b73a @laverdet Initial commit
authored
93
df58a46 @laverdet Compatibility with node-0.5.2
authored
94 Coroutine::Coroutine(entry_t& entry, void* arg) :
a41b3a9 @japj Tentative Windows support
japj authored
95 #ifndef USE_WINFIBER
7a06972 @laverdet Workaround v8 bugs with small stacks
authored
96 stack(stack_size),
a41b3a9 @japj Tentative Windows support
japj authored
97 #endif
ac01c73 @laverdet Spaces -> Tabs
authored
98 entry(entry),
99 arg(arg) {
a41b3a9 @japj Tentative Windows support
japj authored
100 #ifdef USE_CORO
f144c86 @laverdet Use libcoro to get away from ucontext for Lion
authored
101 coro_create(&context, trampoline, this, &stack[0], stack_size);
a41b3a9 @japj Tentative Windows support
japj authored
102 #endif
103 #ifdef USE_WINFIBER
104 context = CreateFiber(stack_size, trampoline, this);
105 #endif
f144c86 @laverdet Use libcoro to get away from ucontext for Lion
authored
106 }
107
108 Coroutine::~Coroutine() {
a41b3a9 @japj Tentative Windows support
japj authored
109 #ifdef USE_CORO
f144c86 @laverdet Use libcoro to get away from ucontext for Lion
authored
110 coro_destroy(&context);
a41b3a9 @japj Tentative Windows support
japj authored
111 #endif
112 #ifdef USE_WINFIBER
113 DeleteFiber(context);
114 #endif
fbef75c @laverdet Coroutine recycling
authored
115 }
116
117 Coroutine& Coroutine::create_fiber(entry_t* entry, void* arg) {
df58a46 @laverdet Compatibility with node-0.5.2
authored
118 if (!fiber_pool.empty()) {
119 Coroutine& fiber = *fiber_pool.back();
120 fiber_pool.pop_back();
121 fiber.reset(entry, arg);
122 return fiber;
123 }
42f7111 @laverdet Add run-time information for fiber pool
authored
124 ++coroutines_created_;
df58a46 @laverdet Compatibility with node-0.5.2
authored
125 return *new Coroutine(*entry, arg);
fbef75c @laverdet Coroutine recycling
authored
126 }
127
128 void Coroutine::reset(entry_t* entry, void* arg) {
df58a46 @laverdet Compatibility with node-0.5.2
authored
129 assert(entry != NULL);
ac01c73 @laverdet Spaces -> Tabs
authored
130 this->entry = entry;
131 this->arg = arg;
906b73a @laverdet Initial commit
authored
132 }
133
df58a46 @laverdet Compatibility with node-0.5.2
authored
134 void Coroutine::transfer(Coroutine& next) {
135 assert(this != &next);
8b12067 @laverdet CORO_PTHREAD compatibility
authored
136 #ifndef CORO_PTHREAD
df58a46 @laverdet Compatibility with node-0.5.2
authored
137 {
138 for (pthread_key_t ii = ceil_thread_key - 1; ii > floor_thread_key; --ii) {
139 // Capture current thread specifics
140 void* data = pthread_getspecific(ii);
141 if (fls_data.size() >= ii - floor_thread_key) {
142 fls_data[ii - floor_thread_key - 1] = data;
143 } else if (data) {
144 fls_data.resize(ii - floor_thread_key);
145 fls_data[ii - floor_thread_key - 1] = data;
146 }
de187c9 @laverdet Fix failure when multiple fibers finish together
authored
147
df58a46 @laverdet Compatibility with node-0.5.2
authored
148 // Replace current thread specifics
149 if (next.fls_data.size() >= ii - floor_thread_key) {
150 if (data != next.fls_data[ii - floor_thread_key - 1]) {
151 pthread_setspecific(ii, next.fls_data[ii - floor_thread_key - 1]);
152 }
153 } else if (data) {
154 pthread_setspecific(ii, NULL);
155 }
156 }
157 }
158 pthread_setspecific(ceil_thread_key, &next);
8b12067 @laverdet CORO_PTHREAD compatibility
authored
159 #endif
a41b3a9 @japj Tentative Windows support
japj authored
160 #ifdef USE_CORO
df58a46 @laverdet Compatibility with node-0.5.2
authored
161 coro_transfer(&context, &next.context);
a41b3a9 @japj Tentative Windows support
japj authored
162 #endif
163 #ifdef USE_WINFIBER
164 SwitchToFiber(next.context);
165 #endif
8b12067 @laverdet CORO_PTHREAD compatibility
authored
166 #ifndef CORO_PTHREAD
df58a46 @laverdet Compatibility with node-0.5.2
authored
167 pthread_setspecific(ceil_thread_key, this);
8b12067 @laverdet CORO_PTHREAD compatibility
authored
168 #endif
df58a46 @laverdet Compatibility with node-0.5.2
authored
169 }
de187c9 @laverdet Fix failure when multiple fibers finish together
authored
170
df58a46 @laverdet Compatibility with node-0.5.2
authored
171 void Coroutine::run() {
172 Coroutine& current = Coroutine::current();
173 assert(!delete_me);
174 assert(&current != this);
175 current.transfer(*this);
de187c9 @laverdet Fix failure when multiple fibers finish together
authored
176
df58a46 @laverdet Compatibility with node-0.5.2
authored
177 if (delete_me) {
de187c9 @laverdet Fix failure when multiple fibers finish together
authored
178 // This means finish() was called on the coroutine and the pool was full so this coroutine needs
179 // to be deleted. We can't delete from inside finish(), because that would deallocate the
180 // current stack. However we CAN delete here, we just have to be very careful.
df58a46 @laverdet Compatibility with node-0.5.2
authored
181 assert(delete_me == this);
de187c9 @laverdet Fix failure when multiple fibers finish together
authored
182 assert(&current != this);
df58a46 @laverdet Compatibility with node-0.5.2
authored
183 delete_me = NULL;
de187c9 @laverdet Fix failure when multiple fibers finish together
authored
184 delete this;
ac01c73 @laverdet Spaces -> Tabs
authored
185 }
906b73a @laverdet Initial commit
authored
186 }
187
fbef75c @laverdet Coroutine recycling
authored
188 void Coroutine::finish(Coroutine& next) {
df58a46 @laverdet Compatibility with node-0.5.2
authored
189 {
190 assert(&next != this);
191 assert(&current() == this);
42f7111 @laverdet Add run-time information for fiber pool
authored
192 if (fiber_pool.size() < pool_size) {
df58a46 @laverdet Compatibility with node-0.5.2
authored
193 fiber_pool.push_back(this);
194 } else {
195 // TODO?: This assumes that we didn't capture any keys with dtors. This may not always be
196 // true, and if it is not there will be memory leaks.
197
198 // Can't delete right now because we're currently on this stack!
199 assert(delete_me == NULL);
200 delete_me = this;
201 }
202 }
203 this->transfer(next);
906b73a @laverdet Initial commit
authored
204 }
205
206 void* Coroutine::bottom() const {
a41b3a9 @japj Tentative Windows support
japj authored
207 #ifndef USE_WINFIBER
cfcc64e @laverdet Fix that pesky stack bug
authored
208 return (char*)&stack[0];
a41b3a9 @japj Tentative Windows support
japj authored
209 #else
e9a4d43 @laverdet Correct stack base calculations for Windows
authored
210 return stack_base;
a41b3a9 @japj Tentative Windows support
japj authored
211 #endif
6a77a26 @laverdet Greatly improved memory management
authored
212 }
213
214 size_t Coroutine::size() const {
7a06972 @laverdet Workaround v8 bugs with small stacks
authored
215 return sizeof(Coroutine) + stack_size;
906b73a @laverdet Initial commit
authored
216 }
Something went wrong with that request. Please try again.