Ruby 2.7.0p0(2019-12-25revision647ee6f091eafcce70ffb75ddf7e121e192ab217)

Context.c
Go to the documentation of this file.
1 /*
2 * This file is part of the "Coroutine" project and released under the MIT License.
3 *
4 * Created by Samuel Williams on 24/6/2019.
5 * Copyright, 2019, by Samuel Williams. All rights reserved.
6 */
7
8 #include "Context.h"
9
10 // http://gcc.gnu.org/onlinedocs/gcc/Alternate-Keywords.html
11 #ifndef __GNUC__
12 #define __asm__ asm
13 #endif
14
15 #if defined(__sparc)
16 __attribute__((noinline))
17 // https://marc.info/?l=linux-sparc&m=131914569320660&w=2
18 static void coroutine_flush_register_windows() {
19 __asm__
20 #ifdef __GNUC__
21 __volatile__
22 #endif
23 #if defined(__sparcv9) || defined(__sparc_v9__) || defined(__arch64__)
24 #ifdef __GNUC__
25 ("flushw" : : : "%o7")
26 #else
27 ("flushw")
28 #endif
29 #else
30 ("ta 0x03")
31 #endif
32 ;
33 }
34 #else
35 static void coroutine_flush_register_windows() {}
36 #endif
37
38 int coroutine_save_stack(struct coroutine_context * context) {
39 void *stack_pointer = &stack_pointer;
40
41 assert(context->stack);
42 assert(context->base);
43
44 // At this point, you may need to ensure on architectures that use register windows, that all registers are flushed to the stack.
45 coroutine_flush_register_windows();
46
47 // Save stack to private area:
48 if (stack_pointer < context->base) {
49 size_t size = (char*)context->base - (char*)stack_pointer;
50 assert(size <= context->size);
51
52 memcpy(context->stack, stack_pointer, size);
53 context->used = size;
54 } else {
55 size_t size = (char*)stack_pointer - (char*)context->base;
56 assert(size <= context->size);
57
58 memcpy(context->stack, context->base, size);
59 context->used = size;
60 }
61
62 // Save registers / restore point:
63 return _setjmp(context->state);
64 }
65
66 __attribute__((noreturn, noinline))
67 static void coroutine_restore_stack_padded(struct coroutine_context *context, void * buffer) {
68 void *stack_pointer = &stack_pointer;
69
70 assert(context->base);
71
72 // Restore stack from private area:
73 if (stack_pointer < context->base) {
74 void * bottom = (char*)context->base - context->used;
75 assert(bottom > stack_pointer);
76
77 memcpy(bottom, context->stack, context->used);
78 } else {
79 void * top = (char*)context->base + context->used;
80 assert(top < stack_pointer);
81
82 memcpy(context->base, context->stack, context->used);
83 }
84
85 // Restore registers:
86 // The `| (int)buffer` is to force the compiler NOT to elide he buffer and `alloca`.
87 _longjmp(context->state, 1 | (int)buffer);
88 }
89
90 static const size_t GAP = 128;
91
92 // In order to swap between coroutines, we need to swap the stack and registers.
93 // `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.
94 // 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`.
95 __attribute__((noreturn))
96 void coroutine_restore_stack(struct coroutine_context *context) {
97 void *stack_pointer = &stack_pointer;
98 void *buffer = NULL;
99 ssize_t offset = 0;
100
101 // We must ensure that the next stack frame is BEYOND the stack we are restoring:
102 if (stack_pointer < context->base) {
103 offset = (char*)stack_pointer - ((char*)context->base - context->used) + GAP;
104 if (offset > 0) buffer = alloca(offset);
105 } else {
106 offset = ((char*)context->base + context->used) - (char*)stack_pointer + GAP;
107 if (offset > 0) buffer = alloca(offset);
108 }
109
110 assert(context->used > 0);
111
112 coroutine_restore_stack_padded(context, buffer);
113 }
114
115 struct coroutine_context *coroutine_transfer(struct coroutine_context *current, struct coroutine_context *target)
116 {
117 struct coroutine_context *previous = target->from;
118
119 // In theory, either this condition holds true, or we should assign the base address to target:
120 assert(current->base == target->base);
121 // If you are trying to copy the coroutine to a different thread
122 // target->base = current->base
123
124 target->from = current;
125
126 assert(current != target);
127
128 // 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.
129
130 if (current->stack) {
131 if (coroutine_save_stack(current) == 0) {
132 coroutine_restore_stack(target);
133 }
134 } else {
135 coroutine_restore_stack(target);
136 }
137
138 target->from = previous;
139
140 return target;
141 }
coroutine_context::stack
void * stack
Definition: Context.h:29
assert
#define assert(x)
Definition: dlmalloc.c:1176
coroutine_save_stack
int coroutine_save_stack(struct coroutine_context *context)
Definition: Context.c:38
coroutine_context::used
size_t used
Definition: Context.h:30
coroutine_context::state
jmp_buf state
Definition: Context.h:35
__attribute__
__attribute__((noreturn, noinline))
Definition: Context.c:66
ssize_t
_ssize_t ssize_t
Definition: rb_mjit_min_header-2.7.0.h:1329
NULL
#define NULL
Definition: _sdbm.c:101
Context.h
memcpy
void * memcpy(void *__restrict, const void *__restrict, size_t)
_longjmp
void _longjmp(jmp_buf, int) __attribute__((__noreturn__))
alloca
#define alloca(size)
Definition: rb_mjit_min_header-2.7.0.h:2487
coroutine_context::from
struct coroutine_context * from
Definition: Context.h:37
size
int size
Definition: encoding.c:58
coroutine_context
Definition: Context.h:17
_setjmp
int _setjmp(jmp_buf)
coroutine_restore_stack
COROUTINE coroutine_restore_stack(struct coroutine_context *context)
coroutine_transfer
struct coroutine_context * coroutine_transfer(struct coroutine_context *current, struct coroutine_context *target)
Definition: Context.c:115
__asm__
#define __asm__
Definition: Context.c:12
top
unsigned int top
Definition: nkf.c:4323
coroutine_context::stack_pointer
void ** stack_pointer
Definition: Context.h:19
coroutine_context::base
void * base
Definition: Context.h:33