Ruby  2.7.2p137(2020-10-01revision5445e0435260b449decf2ac16f9d09bae3cafe72)
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.
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) {
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) {
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;
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) {
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 
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
alloca
#define alloca(size)
Definition: rb_mjit_min_header-2.7.2.h:2525
coroutine_context::state
jmp_buf state
Definition: Context.h:35
__attribute__
__attribute__((noreturn, noinline))
Definition: Context.c:66
NULL
#define NULL
Definition: _sdbm.c:101
coroutine_context::from
struct coroutine_context * from
Definition: Context.h:37
_setjmp
int _setjmp(jmp_buf)
size
int size
Definition: encoding.c:58
_longjmp
void _longjmp(jmp_buf, int) __attribute__((__noreturn__))
coroutine_context
Definition: Context.h:18
coroutine_restore_stack
COROUTINE coroutine_restore_stack(struct coroutine_context *context)
memcpy
void * memcpy(void *__restrict, const void *__restrict, size_t)
ssize_t
_ssize_t ssize_t
Definition: rb_mjit_min_header-2.7.2.h:1332
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
Context.h