4 Originally based on Edgar Toernig's Minimalistic cooperative multitasking
5 http://www.goron.de/~froese/
6 reorg by Steve Dekorte and Chis Double
7 Symbian and Cygwin support by Chis Double
8 Linux/PCC, Linux/Opteron, Irix and FreeBSD/Alpha, ucontext support by Austin Kurahone
9 FreeBSD/Intel support by Faried Nawaz
10 Mingw support by Pit Capitain
11 Visual C support by Daniel Vollmer
12 Solaris support by Manpreet Singh
13 Fibers support by Jonas Eschenburg
14 Ucontext arg support by Olivier Ansaldi
18 This is the system dependent coro code.
19 Setup a jmp_buf so when we longjmp, it will invoke 'func' using 'stack'.
20 Important: 'func' must not return!
22 Usually done by setting the program counter and stack pointer of a new, empty stack.
23 If you're adding a new platform, look in the setjmp.h for PC and SP members
24 of the stack structure
26 If you don't see those members, Kentaro suggests writting a simple
27 test app that calls setjmp and dumps out the contents of the jmp_buf.
28 (The PC and SP should be in jmp_buf->__jmpbuf).
30 Using something like GDB to be able to peek into register contents right
31 before the setjmp occurs would be helpful also.
40 #include "PortableUContext.h"
43 #include <valgrind/valgrind.h>
44 #define STACK_REGISTER(coro) \
47 c->valgrindStackId = VALGRIND_STACK_REGISTER( \
49 c->stack + c->requestedStackSize); \
52 #define STACK_DEREGISTER(coro) \
53 VALGRIND_STACK_DEREGISTER((coro)->valgrindStackId)
56 #define STACK_REGISTER(coro)
57 #define STACK_DEREGISTER(coro)
60 typedef struct CallbackBlock
63 CoroStartCallback
*func
;
66 static CallbackBlock globalCallbackBlock
;
70 Coro
*self
= (Coro
*)io_calloc(1, sizeof(Coro
));
71 self
->requestedStackSize
= CORO_DEFAULT_STACK_SIZE
;
72 self
->allocatedStackSize
= 0;
82 void Coro_allocStackIfNeeded(Coro
*self
)
84 if (self
->stack
&& self
->requestedStackSize
< self
->allocatedStackSize
)
88 self
->requestedStackSize
= 0;
93 self
->stack
= (void *)io_calloc(1, self
->requestedStackSize
+ 16);
94 self
->allocatedStackSize
= self
->requestedStackSize
;
95 //printf("Coro_%p allocating stack size %i\n", (void *)self, self->requestedStackSize);
100 void Coro_free(Coro
*self
)
103 // If this coro has a fiber, delete it.
104 // Don't delete the main fiber. We don't want to commit suicide.
105 if (self
->fiber
&& !self
->isMain
)
107 DeleteFiber(self
->fiber
);
110 STACK_DEREGISTER(self
);
114 io_free(self
->stack
);
117 //printf("Coro_%p io_free\n", (void *)self);
124 void *Coro_stack(Coro
*self
)
129 size_t Coro_stackSize(Coro
*self
)
131 return self
->requestedStackSize
;
134 void Coro_setStackSize_(Coro
*self
, size_t sizeInBytes
)
136 self
->requestedStackSize
= sizeInBytes
;
137 //self->stack = (void *)io_realloc(self->stack, sizeInBytes);
138 //printf("Coro_%p io_reallocating stack size %i\n", (void *)self, sizeInBytes);
142 uint8_t *Coro_CurrentStackPointer(void) __attribute__ ((noinline
));
145 uint8_t *Coro_CurrentStackPointer(void)
148 uint8_t *b
= &a
; // to avoid compiler warning about unused variables
152 size_t Coro_bytesLeftOnStack(Coro
*self
)
155 ptrdiff_t p1
= (ptrdiff_t)(&dummy
);
156 ptrdiff_t p2
= (ptrdiff_t)Coro_CurrentStackPointer();
157 int stackMovesUp
= p2
> p1
;
158 ptrdiff_t start
= ((ptrdiff_t)self
->stack
);
159 ptrdiff_t end
= start
+ self
->requestedStackSize
;
161 if (stackMovesUp
) // like x86
165 else // like OSX on PPC
171 int Coro_stackSpaceAlmostGone(Coro
*self
)
173 return Coro_bytesLeftOnStack(self
) < CORO_STACK_SIZE_MIN
;
176 void Coro_initializeMainCoro(Coro
*self
)
180 // We must convert the current thread into a fiber if it hasn't already been done.
181 if ((LPVOID
) 0x1e00 == GetCurrentFiber()) // value returned when not a fiber
183 // Make this thread a fiber and set its data field to the main coro's address
184 ConvertThreadToFiber(self
);
186 // Make the main coro represent the current fiber
187 self
->fiber
= GetCurrentFiber();
191 void Coro_startCoro_(Coro
*self
, Coro
*other
, void *context
, CoroStartCallback
*callback
)
193 globalCallbackBlock
.context
= context
;
194 globalCallbackBlock
.func
= callback
;
195 Coro_allocStackIfNeeded(other
);
196 Coro_setup(other
, &globalCallbackBlock
);
197 Coro_switchTo_(self
, other
);
200 void Coro_StartWithArg(CallbackBlock
*block
)
202 (block
->func
)(block
->context
);
203 printf("Scheduler error: returned from coro start function\n");
207 void Coro_Start(void)
209 CallbackBlock block
= globalCallbackBlock
;
210 Coro_StartWithArg(&block
);
213 // --------------------------------------------------------------------
215 void Coro_UnsupportedPlatformError(void)
217 printf("Io Scheduler error: no Coro_setupJmpbuf entry for this platform\n.");
222 void Coro_switchTo_(Coro
*self
, Coro
*next
)
224 #if defined(__SYMBIAN32__)
226 #elif defined(HAS_FIBERS)
227 SwitchToFiber(next
->fiber
);
228 #elif defined(HAS_UCONTEXT) && !defined(__x86_64__)
229 swapcontext(&self
->env
, &next
->env
);
231 if (setjmp(self
->env
) == 0)
233 longjmp(next
->env
, 1);
238 // ---- setup ------------------------------------------
240 #if defined(__x86_64__)
242 void Coro_setup(Coro
*self
, void *arg
)
244 /* since ucontext seems to be broken on amg64 */
247 /* This is probably not nice in that it deals directly with
248 * something with __ in front of it.
250 * Anyhow, Coro.h makes the member env of a struct Coro a
251 * jmp_buf. A jmp_buf, as defined in the amd64 setjmp.h
252 * is an array of one struct that wraps the actual __jmp_buf type
253 * which is the array of longs (on a 64 bit machine) that
254 * the programmer below expected. This struct begins with
255 * the __jmp_buf array of longs, so I think it was supposed
256 * to work like he originally had it, but for some reason
257 * it didn't. I don't know why.
258 * - Bryce Schroeder, 16 December 2006
260 * Explaination of `magic' numbers: 6 is the stack pointer
261 * (RSP, the 64 bit equivalent of ESP), 7 is the program counter.
262 * This information came from this file on my Gentoo linux
264 * /usr/include/gento-multilib/amd64/bits/setjmp.h
265 * Which was ultimatly included from setjmp.h in /usr/include. */
266 self
->env
[0].__jmpbuf
[6] = ((unsigned long)(Coro_stack(self
)));
267 self
->env
[0].__jmpbuf
[7] = ((long)Coro_Start
);
270 #elif defined(HAS_UCONTEXT_ON_PRE_SOLARIS_10)
272 typedef void (*makecontext_func
)(void);
274 void Coro_setup(Coro
*self
, void *arg
)
276 ucontext_t
*ucp
= (ucontext_t
*) &self
->env
;
280 ucp
->uc_stack
.ss_sp
= Coro_stack(self
) + Coro_stackSize(self
) - 8;
281 ucp
->uc_stack
.ss_size
= Coro_stackSize(self
);
282 ucp
->uc_stack
.ss_flags
= 0;
285 makecontext(ucp
, (makecontext_func
)Coro_StartWithArg
, 1, arg
); }
288 #elif defined(HAS_UCONTEXT)
290 typedef void (*makecontext_func
)(void);
292 void Coro_setup(Coro
*self
, void *arg
)
294 ucontext_t
*ucp
= (ucontext_t
*) &self
->env
;
298 ucp
->uc_stack
.ss_sp
= Coro_stack(self
);
299 ucp
->uc_stack
.ss_size
= Coro_stackSize(self
);
300 #if !defined(__APPLE__)
301 ucp
->uc_stack
.ss_flags
= 0;
305 makecontext(ucp
, (makecontext_func
)Coro_StartWithArg
, 1, arg
);
308 #elif defined(HAS_FIBERS)
310 void Coro_setup(Coro
*self
, void *arg
)
312 // If this coro was recycled and already has a fiber, delete it.
313 // Don't delete the main fiber. We don't want to commit suicide.
315 if (self
->fiber
&& !self
->isMain
)
317 DeleteFiber(self
->fiber
);
320 self
->fiber
= CreateFiber(Coro_stackSize(self
),
321 (LPFIBER_START_ROUTINE
)Coro_StartWithArg
,
324 DWORD err
= GetLastError();
329 #elif defined(__CYGWIN__)
331 #define buf (self->env)
333 void Coro_setup(Coro
*self
, void *arg
)
336 buf
[7] = (long)(Coro_stack(self
) + Coro_stackSize(self
) - 16);
337 buf
[8] = (long)Coro_Start
;
340 #elif defined(__SYMBIAN32__)
342 void Coro_setup(Coro
*self
, void *arg
)
345 setjmp/longjmp is flakey under Symbian.
346 If the setjmp is done inside the call then a crash occurs.
347 Inlining it here solves the problem
354 self
->env
[3] = (unsigned long)(Coro_stack(self
))
355 + Coro_stackSize(self
) - 64;
356 self
->env
[9] = (long)Coro_Start
;
357 self
->env
[8] = self
->env
[3] + 32;
360 #elif defined(_BSD_PPC_SETJMP_H_)
362 #define buf (self->env)
363 #define setjmp _setjmp
364 #define longjmp _longjmp
366 void Coro_setup(Coro
*self
, void *arg
)
368 size_t *sp
= (size_t *)(((intptr_t)Coro_stack(self
)
369 + Coro_stackSize(self
) - 64 + 15) & ~15);
373 //printf("self = %p\n", self);
374 //printf("sp = %p\n", sp);
376 buf
[21] = (long)Coro_Start
;
377 //sp[-4] = (size_t)self; // for G5 10.3
378 //sp[-6] = (size_t)self; // for G4 10.4
380 //printf("self = %p\n", (void *)self);
381 //printf("sp = %p\n", sp);
384 #elif defined(__DragonFly__)
386 #define buf (self->env)
388 void Coro_setup(Coro
*self
, void *arg
)
390 void *stack
= Coro_stack(self
);
391 size_t stacksize
= Coro_stackSize(self
);
392 void *func
= (void *)Coro_Start
;
396 buf
->_jb
[2] = (long)(stack
+ stacksize
);
397 buf
->_jb
[0] = (long)func
;
401 #elif defined(__arm__)
402 // contributed by Peter van Hardenberg
404 #define buf (self->env)
406 void Coro_setup(Coro
*self
, void *arg
)
409 buf
[8] = (int)Coro_stack(self
) + (int)Coro_stackSize(self
) - 16;
410 buf
[9] = (int)Coro_Start
;
415 #error "Coro.c Error: Coro_setup() function needs to be defined for this platform."
423 // APPLE coros are handled by PortableUContext now
424 #elif defined(_BSD_PPC_SETJMP_H_)
426 #define buf (self->env)
427 #define setjmp _setjmp
428 #define longjmp _longjmp
430 void Coro_setup(Coro *self, void *arg)
432 size_t *sp = (size_t *)(((intptr_t)Coro_stack(self) + Coro_stackSize(self) - 64 + 15) & ~15);
436 //printf("self = %p\n", self);
437 //printf("sp = %p\n", sp);
439 buf[21] = (int)Coro_Start;
440 //sp[-4] = (size_t)self; // for G5 10.3
441 //sp[-6] = (size_t)self; // for G4 10.4
443 //printf("self = %p\n", (void *)self);
444 //printf("sp = %p\n", sp);
447 #elif defined(_BSD_I386_SETJMP_H)
449 #define buf (self->env)
451 void Coro_setup(Coro *self, void *arg)
453 size_t *sp = (size_t *)((intptr_t)Coro_stack(self) + Coro_stackSize(self));
457 buf[9] = (int)(sp); // esp
458 buf[12] = (int)Coro_Start; // eip
463 /* Solaris supports ucontext - so we don't need this stuff anymore
465 void Coro_setup(Coro *self, void *arg)
467 // this bit goes before the setjmp call
468 // Solaris 9 Sparc with GCC
469 #if defined(__SVR4) && defined (__sun)
470 #if defined(_JBLEN) && (_JBLEN == 12) && defined(__sparc)
471 #if defined(_LP64) || defined(_I32LPx)
477 asm("ta 3"); // flush register window
480 #define SUN_STACK_END_INDEX 1
481 #define SUN_PROGRAM_COUNTER 2
482 #define SUN_STACK_START_INDEX 3
484 // Solaris 9 i386 with GCC
485 #elif defined(_JBLEN) && (_JBLEN == 10) && defined(__i386)
486 #if defined(_LP64) || defined(_I32LPx)
493 #define SUN_PROGRAM_COUNTER 5
494 #define SUN_STACK_START_INDEX 3
495 #define SUN_STACK_END_INDEX 4
500 /* Irix supports ucontext - so we don't need this stuff anymore
502 #elif defined(sgi) && defined(_IRIX4_SIGJBLEN) // Irix/SGI
504 void Coro_setup(Coro *self, void *arg)
507 buf[JB_SP] = (__uint64_t)((char *)stack + stacksize - 8);
508 buf[JB_PC] = (__uint64_t)Coro_Start;
512 /* Linux supports ucontext - so we don't need this stuff anymore
515 // Various flavors of Linux.
518 buf->__jmpbuf[JB_GPR1] = ((int) stack + stacksize - 64 + 15) & ~15;
519 buf->__jmpbuf[JB_LR] = (int) Coro_Start;
522 #elif defined(JB_RBX)
524 buf->__jmpbuf[JB_RSP] = (long int )stack + stacksize;
525 buf->__jmpbuf[JB_PC] = Coro_Start;
530 // Linux/x86 with glibc2
531 buf->__jmpbuf[JB_SP] = (int)stack + stacksize;
532 buf->__jmpbuf[JB_PC] = (int)Coro_StartWithArg;
533 // Push the argument on the stack (stack grows downwards)
534 // note: stack is stacksize + 16 bytes long
535 ((int *)stack)[stacksize/sizeof(int) + 1] = (int)self;
538 #elif defined(_I386_JMP_BUF_H)
539 // x86-linux with libc5
540 buf->__sp = (int)stack + stacksize;
541 buf->__pc = Coro_Start;
544 #elif defined(__JMP_BUF_SP)
545 // arm-linux on the sharp zauras
546 buf->__jmpbuf[__JMP_BUF_SP] = (int)stack + stacksize;
547 buf->__jmpbuf[__JMP_BUF_SP+1] = (int)Coro_Start;
555 /* Windows supports fibers - so we don't need this stuff anymore
557 #elif defined(__MINGW32__)
559 void Coro_setup(Coro *self, void *arg)
562 buf[4] = (int)((unsigned char *)stack + stacksize - 16); // esp
563 buf[5] = (int)Coro_Start; // eip
566 #elif defined(_MSC_VER)
568 void Coro_setup(Coro *self, void *arg)
572 // should this be the same as __MINGW32__?
573 buf[4] = (int)((unsigned char *)stack + stacksize - 16); // esp
574 buf[5] = (int)Coro_Start; // eip
579 /* FreeBSD supports ucontext - so we don't need this stuff anymore
581 #elif defined(__FreeBSD__)
583 #if defined(_JBLEN) && (_JBLEN == 81)
585 buf->_jb[2] = (long)Coro_Start; // sc_pc
586 buf->_jb[26+4] = (long)Coro_Start; // sc_regs[R_RA]
587 buf->_jb[27+4] = (long)Coro_Start; // sc_regs[R_T12]
588 buf->_jb[30+4] = (long)(stack + stacksize); // sc_regs[R_SP]
591 #elif defined(_JBLEN)
593 buf->_jb[2] = (long)(stack + stacksize);
594 buf->_jb[0] = (long)Coro_Start;
598 Coro_UnsupportedPlatformError();
602 /* NetBSD supports ucontext - so we don't need this stuff anymore
604 #elif defined(__NetBSD__)
606 void Coro_setup(Coro *self, void *arg)
609 #if defined(_JB_ATTRIBUTES)
611 buf[2] = (long)(stack + stacksize);
612 buf[0] = (long)Coro_Start;
614 Coro_UnsupportedPlatformError();
619 /* Sun supports ucontext - so we don't need this stuff anymore
621 // Solaris supports ucontext - so we don't need this stuff anymore
623 void Coro_setup(Coro *self, void *arg)
625 // this bit goes before the setjmp call
626 // Solaris 9 Sparc with GCC
627 #if defined(__SVR4) && defined (__sun)
628 #if defined(_JBLEN) && (_JBLEN == 12) && defined(__sparc)
629 #if defined(_LP64) || defined(_I32LPx)
635 asm("ta 3"); // flush register window
638 #define SUN_STACK_END_INDEX 1
639 #define SUN_PROGRAM_COUNTER 2
640 #define SUN_STACK_START_INDEX 3
642 // Solaris 9 i386 with GCC
643 #elif defined(_JBLEN) && (_JBLEN == 10) && defined(__i386)
644 #if defined(_LP64) || defined(_I32LPx)
651 #define SUN_PROGRAM_COUNTER 5
652 #define SUN_STACK_START_INDEX 3
653 #define SUN_STACK_END_INDEX 4
658 #elif defined(__SVR4) && defined(__sun)
660 #if defined(SUN_PROGRAM_COUNTER)
662 buf[SUN_PROGRAM_COUNTER] = (JBTYPE)Coro_Start;
665 while ((x % 8) != 0) x --; // align on an even boundary
666 buf[SUN_STACK_START_INDEX] = (JBTYPE)x;
667 x = (JBTYPE)((JBTYPE)stack-stacksize / 2 + 15);
668 while ((x % 8) != 0) x ++; // align on an even boundary
669 buf[SUN_STACK_END_INDEX] = (JBTYPE)x;