diff options
Diffstat (limited to 'coroutine/copy/Context.c')
| -rw-r--r-- | coroutine/copy/Context.c | 141 |
1 files changed, 0 insertions, 141 deletions
diff --git a/coroutine/copy/Context.c b/coroutine/copy/Context.c deleted file mode 100644 index c1b4144e98..0000000000 --- a/coroutine/copy/Context.c +++ /dev/null @@ -1,141 +0,0 @@ -/* - * This file is part of the "Coroutine" project and released under the MIT License. - * - * Created by Samuel Williams on 24/6/2019. - * Copyright, 2019, by Samuel Williams. -*/ - -#include "Context.h" - -// http://gcc.gnu.org/onlinedocs/gcc/Alternate-Keywords.html -#ifndef __GNUC__ -#define __asm__ asm -#endif - -#if defined(__sparc) -__attribute__((noinline)) -// https://marc.info/?l=linux-sparc&m=131914569320660&w=2 -static void coroutine_flush_register_windows() { - __asm__ -#ifdef __GNUC__ - __volatile__ -#endif -#if defined(__sparcv9) || defined(__sparc_v9__) || defined(__arch64__) -#ifdef __GNUC__ - ("flushw" : : : "%o7") -#else - ("flushw") -#endif -#else - ("ta 0x03") -#endif - ; -} -#else -static void coroutine_flush_register_windows() {} -#endif - -int coroutine_save_stack(struct coroutine_context * context) { - void *stack_pointer = &stack_pointer; - - assert(context->stack); - assert(context->base); - - // At this point, you may need to ensure on architectures that use register windows, that all registers are flushed to the stack. - coroutine_flush_register_windows(); - - // Save stack to private area: - if (stack_pointer < context->base) { - size_t size = (char*)context->base - (char*)stack_pointer; - assert(size <= context->size); - - memcpy(context->stack, stack_pointer, size); - context->used = size; - } else { - size_t size = (char*)stack_pointer - (char*)context->base; - assert(size <= context->size); - - memcpy(context->stack, context->base, size); - context->used = size; - } - - // Save registers / restore point: - return _setjmp(context->state); -} - -__attribute__((noreturn, noinline)) -static void coroutine_restore_stack_padded(struct coroutine_context *context, void * buffer) { - void *stack_pointer = &stack_pointer; - - assert(context->base); - - // Restore stack from private area: - if (stack_pointer < context->base) { - void * bottom = (char*)context->base - context->used; - assert(bottom > stack_pointer); - - memcpy(bottom, context->stack, context->used); - } else { - void * top = (char*)context->base + context->used; - assert(top < stack_pointer); - - memcpy(context->base, context->stack, context->used); - } - - // Restore registers: - // The `| (int)buffer` is to force the compiler NOT to elide he buffer and `alloca`. - _longjmp(context->state, 1 | (int)buffer); -} - -static const size_t GAP = 128; - -// In order to swap between coroutines, we need to swap the stack and registers. -// `setjmp` and `longjmp` are able to swap registers, but what about swapping stacks? You can use `memcpy` to copy the current stack to a private area and `memcpy` to copy the private stack of the next coroutine to the main stack. -// But if the stack yop are copying in to the main stack is bigger than the currently executing stack, the `memcpy` will clobber the current stack frame (including the context argument). So we use `alloca` to push the current stack frame *beyond* the stack we are about to copy in. This ensures the current stack frame in `coroutine_restore_stack_padded` remains valid for calling `longjmp`. -__attribute__((noreturn)) -void coroutine_restore_stack(struct coroutine_context *context) { - void *stack_pointer = &stack_pointer; - void *buffer = NULL; - ssize_t offset = 0; - - // We must ensure that the next stack frame is BEYOND the stack we are restoring: - if (stack_pointer < context->base) { - offset = (char*)stack_pointer - ((char*)context->base - context->used) + GAP; - if (offset > 0) buffer = alloca(offset); - } else { - offset = ((char*)context->base + context->used) - (char*)stack_pointer + GAP; - if (offset > 0) buffer = alloca(offset); - } - - assert(context->used > 0); - - coroutine_restore_stack_padded(context, buffer); -} - -struct coroutine_context *coroutine_transfer(struct coroutine_context *current, struct coroutine_context *target) -{ - struct coroutine_context *previous = target->from; - - // In theory, either this condition holds true, or we should assign the base address to target: - assert(current->base == target->base); - // If you are trying to copy the coroutine to a different thread - // target->base = current->base - - target->from = current; - - assert(current != target); - - // It's possible to come here, even thought the current fiber has been terminated. We are never going to return so we don't bother saving the stack. - - if (current->stack) { - if (coroutine_save_stack(current) == 0) { - coroutine_restore_stack(target); - } - } else { - coroutine_restore_stack(target); - } - - target->from = previous; - - return target; -} |
