Fixing coro leaking bug when using GLUT callbacks
[io/quag.git] / libs / coroutine / source / Coro.c
blob812b21b492e813bfacf7f07406b98373692dbc5d
1 /*
2 Credits
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
16 Notes
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.
34 #include "Base.h"
35 #include "Coro.h"
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <string.h>
39 #include <stddef.h>
40 #include "PortableUContext.h"
42 #ifdef USE_VALGRIND
43 #include <valgrind/valgrind.h>
44 #define STACK_REGISTER(coro) \
45 { \
46 Coro *c = (coro); \
47 c->valgrindStackId = VALGRIND_STACK_REGISTER( \
48 c->stack, \
49 c->stack + c->requestedStackSize); \
52 #define STACK_DEREGISTER(coro) \
53 VALGRIND_STACK_DEREGISTER((coro)->valgrindStackId)
55 #else
56 #define STACK_REGISTER(coro)
57 #define STACK_DEREGISTER(coro)
58 #endif
60 typedef struct CallbackBlock
62 void *context;
63 CoroStartCallback *func;
64 } CallbackBlock;
66 static CallbackBlock globalCallbackBlock;
68 Coro *Coro_new(void)
70 Coro *self = (Coro *)io_calloc(1, sizeof(Coro));
71 self->requestedStackSize = CORO_DEFAULT_STACK_SIZE;
72 self->allocatedStackSize = 0;
74 #ifdef HAS_FIBERS
75 self->fiber = NULL;
76 #else
77 self->stack = NULL;
78 #endif
79 return self;
82 void Coro_allocStackIfNeeded(Coro *self)
84 if (self->stack && self->requestedStackSize < self->allocatedStackSize)
86 io_free(self->stack);
87 self->stack = NULL;
88 self->requestedStackSize = 0;
91 if (!self->stack)
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);
96 STACK_REGISTER(self);
100 void Coro_free(Coro *self)
102 #ifdef HAS_FIBERS
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);
109 #else
110 STACK_DEREGISTER(self);
111 #endif
112 if (self->stack)
114 io_free(self->stack);
117 //printf("Coro_%p io_free\n", (void *)self);
119 io_free(self);
122 // stack
124 void *Coro_stack(Coro *self)
126 return self->stack;
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);
141 #if __GNUC__ == 4
142 uint8_t *Coro_CurrentStackPointer(void) __attribute__ ((noinline));
143 #endif
145 uint8_t *Coro_CurrentStackPointer(void)
147 uint8_t a;
148 uint8_t *b = &a; // to avoid compiler warning about unused variables
149 return b;
152 size_t Coro_bytesLeftOnStack(Coro *self)
154 unsigned char dummy;
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
163 return end - p1;
165 else // like OSX on PPC
167 return p1 - start;
171 int Coro_stackSpaceAlmostGone(Coro *self)
173 return Coro_bytesLeftOnStack(self) < CORO_STACK_SIZE_MIN;
176 void Coro_initializeMainCoro(Coro *self)
178 self->isMain = 1;
179 #ifdef HAS_FIBERS
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();
188 #endif
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");
204 exit(-1);
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.");
218 exit(1);
222 void Coro_switchTo_(Coro *self, Coro *next)
224 #if defined(__SYMBIAN32__)
225 ProcessUIEvent();
226 #elif defined(HAS_FIBERS)
227 SwitchToFiber(next->fiber);
228 #elif defined(HAS_UCONTEXT) && !defined(__x86_64__)
229 swapcontext(&self->env, &next->env);
230 #else
231 if (setjmp(self->env) == 0)
233 longjmp(next->env, 1);
235 #endif
238 // ---- setup ------------------------------------------
240 #if defined(__x86_64__)
242 void Coro_setup(Coro *self, void *arg)
244 /* since ucontext seems to be broken on amg64 */
246 setjmp(self->env);
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
263 * amd64 computer:
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;
278 getcontext(ucp);
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;
283 ucp->uc_link = NULL;
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;
296 getcontext(ucp);
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;
302 ucp->uc_link = NULL;
303 #endif
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,
322 (LPVOID)arg);
323 if (!self->fiber) {
324 DWORD err = GetLastError();
325 exit(err);
329 #elif defined(__CYGWIN__)
331 #define buf (self->env)
333 void Coro_setup(Coro *self, void *arg)
335 setjmp(buf);
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
350 setjmp(self->env);
351 self->env[0] = 0;
352 self->env[1] = 0;
353 self->env[2] = 0;
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);
371 setjmp(buf);
373 //printf("self = %p\n", self);
374 //printf("sp = %p\n", sp);
375 buf[0] = (long)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;
394 setjmp(buf);
396 buf->_jb[2] = (long)(stack + stacksize);
397 buf->_jb[0] = (long)func;
398 return;
401 #elif defined(__arm__)
402 // contributed by Peter van Hardenberg
404 #define buf (self->env)
406 void Coro_setup(Coro *self, void *arg)
408 setjmp(buf);
409 buf[8] = (int)Coro_stack(self) + (int)Coro_stackSize(self) - 16;
410 buf[9] = (int)Coro_Start;
413 #else
415 #error "Coro.c Error: Coro_setup() function needs to be defined for this platform."
417 #endif
420 // old code
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);
434 setjmp(buf);
436 //printf("self = %p\n", self);
437 //printf("sp = %p\n", sp);
438 buf[0] = (int)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));
455 setjmp(buf);
457 buf[9] = (int)(sp); // esp
458 buf[12] = (int)Coro_Start; // eip
459 //buf[8] = 0; // ebp
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)
472 #define JBTYPE long
473 JBTYPE x;
474 #else
475 #define JBTYPE int
476 JBTYPE x;
477 asm("ta 3"); // flush register window
478 #endif
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)
487 #define JBTYPE long
488 JBTYPE x;
489 #else
490 #define JBTYPE int
491 JBTYPE x;
492 #endif
493 #define SUN_PROGRAM_COUNTER 5
494 #define SUN_STACK_START_INDEX 3
495 #define SUN_STACK_END_INDEX 4
496 #endif
497 #endif
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)
506 setjmp(buf);
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
514 #elif defined(linux)
515 // Various flavors of Linux.
516 #if defined(JB_GPR1)
517 // Linux/PPC
518 buf->__jmpbuf[JB_GPR1] = ((int) stack + stacksize - 64 + 15) & ~15;
519 buf->__jmpbuf[JB_LR] = (int) Coro_Start;
520 return;
522 #elif defined(JB_RBX)
523 // Linux/Opteron
524 buf->__jmpbuf[JB_RSP] = (long int )stack + stacksize;
525 buf->__jmpbuf[JB_PC] = Coro_Start;
526 return;
528 #elif defined(JB_SP)
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;
536 return;
538 #elif defined(_I386_JMP_BUF_H)
539 // x86-linux with libc5
540 buf->__sp = (int)stack + stacksize;
541 buf->__pc = Coro_Start;
542 return;
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;
548 return;
550 #else
555 /* Windows supports fibers - so we don't need this stuff anymore
557 #elif defined(__MINGW32__)
559 void Coro_setup(Coro *self, void *arg)
561 setjmp(buf);
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)
570 setjmp(buf);
571 // win32 visual c
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__)
582 // FreeBSD.
583 #if defined(_JBLEN) && (_JBLEN == 81)
584 // FreeBSD/Alpha
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]
589 return;
591 #elif defined(_JBLEN)
592 // FreeBSD on IA32
593 buf->_jb[2] = (long)(stack + stacksize);
594 buf->_jb[0] = (long)Coro_Start;
595 return;
597 #else
598 Coro_UnsupportedPlatformError();
599 #endif
602 /* NetBSD supports ucontext - so we don't need this stuff anymore
604 #elif defined(__NetBSD__)
606 void Coro_setup(Coro *self, void *arg)
608 setjmp(buf);
609 #if defined(_JB_ATTRIBUTES)
610 // NetBSD i386
611 buf[2] = (long)(stack + stacksize);
612 buf[0] = (long)Coro_Start;
613 #else
614 Coro_UnsupportedPlatformError();
615 #endif
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)
630 #define JBTYPE long
631 JBTYPE x;
632 #else
633 #define JBTYPE int
634 JBTYPE x;
635 asm("ta 3"); // flush register window
636 #endif
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)
645 #define JBTYPE long
646 JBTYPE x;
647 #else
648 #define JBTYPE int
649 JBTYPE x;
650 #endif
651 #define SUN_PROGRAM_COUNTER 5
652 #define SUN_STACK_START_INDEX 3
653 #define SUN_STACK_END_INDEX 4
654 #endif
655 #endif
658 #elif defined(__SVR4) && defined(__sun)
659 // Solaris
660 #if defined(SUN_PROGRAM_COUNTER)
661 // SunOS 9
662 buf[SUN_PROGRAM_COUNTER] = (JBTYPE)Coro_Start;
664 x = (JBTYPE)stack;
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;