minicoro.h
Go to the documentation of this file.
1 /*
2 Minimal asymmetric stackful cross-platform coroutine library in pure C.
3 minicoro - v0.1.3 - 27/Jan/2022
4 Eduardo Bart - edub4rt@gmail.com
5 https://github.com/edubart/minicoro
6 
7 Minicoro is single file library for using asymmetric coroutines in C.
8 The API is inspired by Lua coroutines but with C use in mind.
9 
10 # Features
11 
12 - Stackful asymmetric coroutines.
13 - Supports nesting coroutines (resuming a coroutine from another coroutine).
14 - Supports custom allocators.
15 - Storage system to allow passing values between yield and resume.
16 - Customizable stack size.
17 - Coroutine API design inspired by Lua with C use in mind.
18 - Yield across any C function.
19 - Made to work in multithread applications.
20 - Cross platform.
21 - Minimal, self contained and no external dependencies.
22 - Readable sources and documented.
23 - Implemented via assembly, ucontext or fibers.
24 - Lightweight and very efficient.
25 - Works in most C89 compilers.
26 - Error prone API, returning proper error codes on misuse.
27 - Support running with Valgrind, ASan (AddressSanitizer) and TSan (ThreadSanitizer).
28 
29 # Supported Platforms
30 
31 Most platforms are supported through different methods:
32 
33 | Platform | Assembly Method | Fallback Method |
34 |--------------|------------------|-------------------|
35 | Android | ARM/ARM64 | N/A |
36 | iOS | ARM/ARM64 | N/A |
37 | Windows | x86_64 | Windows fibers |
38 | Linux | x86_64/i686 | ucontext |
39 | Mac OS X | x86_64/ARM/ARM64 | ucontext |
40 | WebAssembly | N/A | Emscripten fibers / Binaryen asyncify |
41 | Raspberry Pi | ARM | ucontext |
42 | RISC-V | rv64/rv32 | ucontext |
43 
44 The assembly method is used by default if supported by the compiler and CPU,
45 otherwise ucontext or fiber method is used as a fallback.
46 
47 The assembly method is very efficient, it just take a few cycles
48 to create, resume, yield or destroy a coroutine.
49 
50 # Caveats
51 
52 - Don't use coroutines with C++ exceptions, this is not supported.
53 - When using C++ RAII (i.e. destructors) you must resume the coroutine until it dies to properly execute all destructors.
54 - To use in multithread applications, you must compile with C compiler that supports `thread_local` qualifier.
55 - Some unsupported sanitizers for C may trigger false warnings when using coroutines.
56 - The `mco_coro` object is not thread safe, you should lock each coroutine into a thread.
57 - Stack space is fixed, it cannot grow. By default it has about 56KB of space, this can be changed on coroutine creation.
58 - Take care to not cause stack overflows (run out of stack space), otherwise your program may crash or not, the behavior is undefined.
59 - On WebAssembly you must compile with Emscripten flag `-s ASYNCIFY=1`.
60 - The WebAssembly Binaryen asyncify method can be used when explicitly enabled,
61 you may want to do this only to use minicoro with WebAssembly native interpreters
62 (no Web browser). This method is confirmed to work well with Emscripten toolchain,
63 however it fails on other WebAssembly toolchains like WASI SDK.
64 
65 # Introduction
66 
67 A coroutine represents an independent "green" thread of execution.
68 Unlike threads in multithread systems, however,
69 a coroutine only suspends its execution by explicitly calling a yield function.
70 
71 You create a coroutine by calling `mco_create`.
72 Its sole argument is a `mco_desc` structure with a description for the coroutine.
73 The `mco_create` function only creates a new coroutine and returns a handle to it, it does not start the coroutine.
74 
75 You execute a coroutine by calling `mco_resume`.
76 When calling a resume function the coroutine starts its execution by calling its body function.
77 After the coroutine starts running, it runs until it terminates or yields.
78 
79 A coroutine yields by calling `mco_yield`.
80 When a coroutine yields, the corresponding resume returns immediately,
81 even if the yield happens inside nested function calls (that is, not in the main function).
82 The next time you resume the same coroutine, it continues its execution from the point where it yielded.
83 
84 To associate a persistent value with the coroutine,
85 you can optionally set `user_data` on its creation and later retrieve with `mco_get_user_data`.
86 
87 To pass values between resume and yield,
88 you can optionally use `mco_push` and `mco_pop` APIs,
89 they are intended to pass temporary values using a LIFO style buffer.
90 The storage system can also be used to send and receive initial values on coroutine creation or before it finishes.
91 
92 # Usage
93 
94 To use minicoro, do the following in one .c file:
95 
96 ```c
97 #define MINICORO_IMPL
98 #include "minicoro.h"
99 ```
100 
101 You can do `#include "minicoro.h"` in other parts of the program just like any other header.
102 
103 ## Minimal Example
104 
105 The following simple example demonstrates on how to use the library:
106 
107 ```c
108 #define MINICORO_IMPL
109 #include "minicoro.h"
110 #include <stdio.h>
111 
112 // Coroutine entry function.
113 void coro_entry(mco_coro* co) {
114  printf("coroutine 1\n");
115  mco_yield(co);
116  printf("coroutine 2\n");
117 }
118 
119 int main() {
120  // First initialize a `desc` object through `mco_desc_init`.
121  mco_desc desc = mco_desc_init(coro_entry, 0);
122  // Configure `desc` fields when needed (e.g. customize user_data or allocation functions).
123  desc.user_data = NULL;
124  // Call `mco_create` with the output coroutine pointer and `desc` pointer.
125  mco_coro* co;
126  mco_result res = mco_create(&co, &desc);
127  assert(res == MCO_SUCCESS);
128  // The coroutine should be now in suspended state.
129  assert(mco_status(co) == MCO_SUSPENDED);
130  // Call `mco_resume` to start for the first time, switching to its context.
131  res = mco_resume(co); // Should print "coroutine 1".
132  assert(res == MCO_SUCCESS);
133  // We get back from coroutine context in suspended state (because it's unfinished).
134  assert(mco_status(co) == MCO_SUSPENDED);
135  // Call `mco_resume` to resume for a second time.
136  res = mco_resume(co); // Should print "coroutine 2".
137  assert(res == MCO_SUCCESS);
138  // The coroutine finished and should be now dead.
139  assert(mco_status(co) == MCO_DEAD);
140  // Call `mco_destroy` to destroy the coroutine.
141  res = mco_destroy(co);
142  assert(res == MCO_SUCCESS);
143  return 0;
144 }
145 ```
146 
147 _NOTE_: In case you don't want to use the minicoro allocator system you should
148 allocate a coroutine object yourself using `mco_desc.coro_size` and call `mco_init`,
149 then later to destroy call `mco_uninit` and deallocate it.
150 
151 ## Yielding from anywhere
152 
153 You can yield the current running coroutine from anywhere
154 without having to pass `mco_coro` pointers around,
155 to this just use `mco_yield(mco_running())`.
156 
157 ## Passing data between yield and resume
158 
159 The library has the storage interface to assist passing data between yield and resume.
160 It's usage is straightforward,
161 use `mco_push` to send data before a `mco_resume` or `mco_yield`,
162 then later use `mco_pop` after a `mco_resume` or `mco_yield` to receive data.
163 Take care to not mismatch a push and pop, otherwise these functions will return
164 an error.
165 
166 ## Error handling
167 
168 The library return error codes in most of its API in case of misuse or system error,
169 the user is encouraged to handle them properly.
170 
171 ## Library customization
172 
173 The following can be defined to change the library behavior:
174 
175 - `MCO_API` - Public API qualifier. Default is `extern`.
176 - `MCO_MIN_STACK_SIZE` - Minimum stack size when creating a coroutine. Default is 32768.
177 - `MCO_DEFAULT_STORAGE_SIZE` - Size of coroutine storage buffer. Default is 1024.
178 - `MCO_DEFAULT_STACK_SIZE` - Default stack size when creating a coroutine. Default is 57344.
179 - `MCO_MALLOC` - Default allocation function. Default is `malloc`.
180 - `MCO_FREE` - Default deallocation function. Default is `free`.
181 - `MCO_DEBUG` - Enable debug mode, logging any runtime error to stdout. Defined automatically unless `NDEBUG` or `MCO_NO_DEBUG` is defined.
182 - `MCO_NO_DEBUG` - Disable debug mode.
183 - `MCO_NO_MULTITHREAD` - Disable multithread usage. Multithread is supported when `thread_local` is supported.
184 - `MCO_NO_DEFAULT_ALLOCATORS` - Disable the default allocator using `MCO_MALLOC` and `MCO_FREE`.
185 - `MCO_ZERO_MEMORY` - Zero memory of stack for new coroutines and when poping storage, intended for garbage collected environments.
186 - `MCO_USE_ASM` - Force use of assembly context switch implementation.
187 - `MCO_USE_UCONTEXT` - Force use of ucontext context switch implementation.
188 - `MCO_USE_FIBERS` - Force use of fibers context switch implementation.
189 - `MCO_USE_ASYNCIFY` - Force use of Binaryen asyncify context switch implementation.
190 - `MCO_USE_VALGRIND` - Define if you want run with valgrind to fix accessing memory errors.
191 
192 # License
193 
194 Your choice of either Public Domain or MIT No Attribution, see end of file.
195 */
196 
197 
198 #ifndef MINICORO_H
199 #define MINICORO_H
200 
201 #ifdef __cplusplus
202 extern "C" {
203 #endif
204 
205 /* Public API qualifier. */
206 #ifndef MCO_API
207 #define MCO_API extern
208 #endif
209 
210 /* Size of coroutine storage buffer. */
211 #ifndef MCO_DEFAULT_STORAGE_SIZE
212 #define MCO_DEFAULT_STORAGE_SIZE 1024
213 #endif
214 
215 #include <stddef.h> /* for size_t */
216 
217 /* ---------------------------------------------------------------------------------------------- */
218 
219 /* Coroutine states. */
220 typedef enum mco_state {
221  MCO_DEAD = 0, /* The coroutine has finished normally or was uninitialized before finishing. */
222  MCO_NORMAL, /* The coroutine is active but not running (that is, it has resumed another coroutine). */
223  MCO_RUNNING, /* The coroutine is active and running. */
224  MCO_SUSPENDED /* The coroutine is suspended (in a call to yield, or it has not started running yet). */
225 } mco_state;
226 
227 /* Coroutine result codes. */
228 typedef enum mco_result {
242 } mco_result;
243 
244 /* Coroutine structure. */
245 typedef struct mco_coro mco_coro;
246 struct mco_coro {
247  void* context;
249  void (*func)(mco_coro* co);
251  void* user_data;
253  void (*free_cb)(void* ptr, void* allocator_data);
254  void* stack_base; /* Stack base address, can be used to scan memory in a garbage collector. */
255  size_t stack_size;
256  unsigned char* storage;
257  size_t bytes_stored;
258  size_t storage_size;
259  void* asan_prev_stack; /* Used by address sanitizer. */
260  void* tsan_prev_fiber; /* Used by thread sanitizer. */
261  void* tsan_fiber; /* Used by thread sanitizer. */
262  size_t magic_number; /* Used to check stack overflow. */
263 };
264 
265 /* Structure used to initialize a coroutine. */
266 typedef struct mco_desc {
267  void (*func)(mco_coro* co); /* Entry point function for the coroutine. */
268  void* user_data; /* Coroutine user data, can be get with `mco_get_user_data`. */
269  /* Custom allocation interface. */
270  void* (*malloc_cb)(size_t size, void* allocator_data); /* Custom allocation function. */
271  void (*free_cb)(void* ptr, void* allocator_data); /* Custom deallocation function. */
272  void* allocator_data; /* User data pointer passed to `malloc`/`free` allocation functions. */
273  size_t storage_size; /* Coroutine storage size, to be used with the storage APIs. */
274  /* These must be initialized only through `mco_init_desc`. */
275  size_t coro_size; /* Coroutine structure size. */
276  size_t stack_size; /* Coroutine stack size. */
277 } mco_desc;
278 
279 /* Coroutine functions. */
280 MCO_API mco_desc mco_desc_init(void (*func)(mco_coro* co), size_t stack_size); /* Initialize description of a coroutine. When stack size is 0 then MCO_DEFAULT_STACK_SIZE is used. */
281 MCO_API mco_result mco_init(mco_coro* co, mco_desc* desc); /* Initialize the coroutine. */
282 MCO_API mco_result mco_uninit(mco_coro* co); /* Uninitialize the coroutine, may fail if it's not dead or suspended. */
283 MCO_API mco_result mco_create(mco_coro** out_co, mco_desc* desc); /* Allocates and initializes a new coroutine. */
284 MCO_API mco_result mco_destroy(mco_coro* co); /* Uninitialize and deallocate the coroutine, may fail if it's not dead or suspended. */
285 MCO_API mco_result mco_resume(mco_coro* co); /* Starts or continues the execution of the coroutine. */
286 MCO_API mco_result mco_yield(mco_coro* co); /* Suspends the execution of a coroutine. */
287 MCO_API mco_state mco_status(mco_coro* co); /* Returns the status of the coroutine. */
288 MCO_API void* mco_get_user_data(mco_coro* co); /* Get coroutine user data supplied on coroutine creation. */
289 
290 /* Storage interface functions, used to pass values between yield and resume. */
291 MCO_API mco_result mco_push(mco_coro* co, const void* src, size_t len); /* Push bytes to the coroutine storage. Use to send values between yield and resume. */
292 MCO_API mco_result mco_pop(mco_coro* co, void* dest, size_t len); /* Pop bytes from the coroutine storage. Use to get values between yield and resume. */
293 MCO_API mco_result mco_peek(mco_coro* co, void* dest, size_t len); /* Like `mco_pop` but it does not consumes the storage. */
294 MCO_API size_t mco_get_bytes_stored(mco_coro* co); /* Get the available bytes that can be retrieved with a `mco_pop`. */
295 MCO_API size_t mco_get_storage_size(mco_coro* co); /* Get the total storage size. */
296 
297 /* Misc functions. */
298 MCO_API mco_coro* mco_running(void); /* Returns the running coroutine for the current thread. */
299 MCO_API const char* mco_result_description(mco_result res); /* Get the description of a result. */
300 
301 #ifdef __cplusplus
302 }
303 #endif
304 
305 #endif /* MINICORO_H */
306 
307 #ifdef MINICORO_IMPL
308 
309 #ifdef __cplusplus
310 extern "C" {
311 #endif
312 
313 /* ---------------------------------------------------------------------------------------------- */
314 
315 /* Minimum stack size when creating a coroutine. */
316 #ifndef MCO_MIN_STACK_SIZE
317 #define MCO_MIN_STACK_SIZE 32768
318 #endif
319 
320 /* Default stack size when creating a coroutine. */
321 #ifndef MCO_DEFAULT_STACK_SIZE
322 #define MCO_DEFAULT_STACK_SIZE 57344 /* Don't use multiples of 64K to avoid D-cache aliasing conflicts. */
323 #endif
324 
325 /* Number used only to assist checking for stack overflows. */
326 #define MCO_MAGIC_NUMBER 0x7E3CB1A9
327 
328 /* Detect implementation based on OS, arch and compiler. */
329 #if !defined(MCO_USE_UCONTEXT) && !defined(MCO_USE_FIBERS) && !defined(MCO_USE_ASM) && !defined(MCO_USE_ASYNCIFY)
330  #if defined(_WIN32)
331  #if (defined(__GNUC__) && defined(__x86_64__)) || (defined(_MSC_VER) && defined(_M_X64))
332  #define MCO_USE_ASM
333  #else
334  #define MCO_USE_FIBERS
335  #endif
336  #elif defined(__CYGWIN__) /* MSYS */
337  #define MCO_USE_UCONTEXT
338  #elif defined(__EMSCRIPTEN__)
339  #define MCO_USE_FIBERS
340  #elif defined(__wasm__)
341  #define MCO_USE_ASYNCIFY
342  #else
343  #if __GNUC__ >= 3 /* Assembly extension supported. */
344  #if defined(__x86_64__) || \
345  defined(__i386) || defined(__i386__) || \
346  defined(__ARM_EABI__) || defined(__aarch64__) || \
347  defined(__riscv)
348  #define MCO_USE_ASM
349  #else
350  #define MCO_USE_UCONTEXT
351  #endif
352  #else
353  #define MCO_USE_UCONTEXT
354  #endif
355  #endif
356 #endif
357 
358 #define _MCO_UNUSED(x) (void)(x)
359 
360 #if !defined(MCO_NO_DEBUG) && !defined(NDEBUG) && !defined(MCO_DEBUG)
361 #define MCO_DEBUG
362 #endif
363 
364 #ifndef MCO_LOG
365  #ifdef MCO_DEBUG
366  #include <stdio.h>
367  #define MCO_LOG(s) puts(s)
368  #else
369  #define MCO_LOG(s)
370  #endif
371 #endif
372 
373 #ifndef MCO_ASSERT
374  #ifdef MCO_DEBUG
375  #include <assert.h>
376  #define MCO_ASSERT(c) assert(c)
377  #else
378  #define MCO_ASSERT(c)
379  #endif
380 #endif
381 
382 #ifndef MCO_THREAD_LOCAL
383  #ifdef MCO_NO_MULTITHREAD
384  #define MCO_THREAD_LOCAL
385  #else
386  #ifdef thread_local
387  #define MCO_THREAD_LOCAL thread_local
388  #elif __STDC_VERSION__ >= 201112 && !defined(__STDC_NO_THREADS__)
389  #define MCO_THREAD_LOCAL _Thread_local
390  #elif defined(_WIN32) && (defined(_MSC_VER) || defined(__ICL) || defined(__DMC__) || defined(__BORLANDC__))
391  #define MCO_THREAD_LOCAL __declspec(thread)
392  #elif defined(__GNUC__) || defined(__SUNPRO_C) || defined(__xlC__)
393  #define MCO_THREAD_LOCAL __thread
394  #else /* No thread local support, `mco_running` will be thread unsafe. */
395  #define MCO_THREAD_LOCAL
396  #define MCO_NO_MULTITHREAD
397  #endif
398  #endif
399 #endif
400 
401 #ifndef MCO_FORCE_INLINE
402  #ifdef _MSC_VER
403  #define MCO_FORCE_INLINE __forceinline
404  #elif defined(__GNUC__)
405  #if defined(__STRICT_ANSI__)
406  #define MCO_FORCE_INLINE __inline__ __attribute__((always_inline))
407  #else
408  #define MCO_FORCE_INLINE inline __attribute__((always_inline))
409  #endif
410  #elif defined(__BORLANDC__) || defined(__DMC__) || defined(__SC__) || defined(__WATCOMC__) || defined(__LCC__) || defined(__DECC)
411  #define MCO_FORCE_INLINE __inline
412  #else /* No inline support. */
413  #define MCO_FORCE_INLINE
414  #endif
415 #endif
416 
417 #ifndef MCO_NO_INLINE
418  #ifdef __GNUC__
419  #define MCO_NO_INLINE __attribute__((noinline))
420  #elif defined(_MSC_VER)
421  #define MCO_NO_INLINE __declspec(noinline)
422  #else
423  #define MCO_NO_INLINE
424  #endif
425 #endif
426 
427 #ifndef MCO_NO_DEFAULT_ALLOCATORS
428 #ifndef MCO_MALLOC
429  #include <stdlib.h>
430  #define MCO_MALLOC malloc
431  #define MCO_FREE free
432 #endif
433 static void* mco_malloc(size_t size, void* allocator_data) {
434  _MCO_UNUSED(allocator_data);
435  return MCO_MALLOC(size);
436 }
437 static void mco_free(void* ptr, void* allocator_data) {
438  _MCO_UNUSED(allocator_data);
439  MCO_FREE(ptr);
440 }
441 #endif /* MCO_NO_DEFAULT_ALLOCATORS */
442 
443 #if defined(__has_feature)
444  #if __has_feature(address_sanitizer)
445  #define _MCO_USE_ASAN
446  #endif
447  #if __has_feature(thread_sanitizer)
448  #define _MCO_USE_TSAN
449  #endif
450 #endif
451 #if defined(__SANITIZE_ADDRESS__)
452  #define _MCO_USE_ASAN
453 #endif
454 #if defined(__SANITIZE_THREAD__)
455  #define _MCO_USE_TSAN
456 #endif
457 #ifdef _MCO_USE_ASAN
458 void __sanitizer_start_switch_fiber(void** fake_stack_save, const void *bottom, size_t size);
459 void __sanitizer_finish_switch_fiber(void* fake_stack_save, const void **bottom_old, size_t *size_old);
460 #endif
461 #ifdef _MCO_USE_TSAN
462 void* __tsan_get_current_fiber(void);
463 void* __tsan_create_fiber(unsigned flags);
464 void __tsan_destroy_fiber(void* fiber);
465 void __tsan_switch_to_fiber(void* fiber, unsigned flags);
466 #endif
467 
468 #include <string.h> /* For memcpy and memset. */
469 
470 /* Utility for aligning addresses. */
471 static MCO_FORCE_INLINE size_t _mco_align_forward(size_t addr, size_t align) {
472  return (addr + (align-1)) & ~(align-1);
473 }
474 
475 /* Variable holding the current running coroutine per thread. */
476 static MCO_THREAD_LOCAL mco_coro* mco_current_co = NULL;
477 
478 static MCO_FORCE_INLINE void _mco_prepare_jumpin(mco_coro* co) {
479  /* Set the old coroutine to normal state and update it. */
480  mco_coro* prev_co = mco_running(); /* Must access through `mco_running`. */
481  MCO_ASSERT(co->prev_co == NULL);
482  co->prev_co = prev_co;
483  if(prev_co) {
484  MCO_ASSERT(prev_co->state == MCO_RUNNING);
485  prev_co->state = MCO_NORMAL;
486  }
487  mco_current_co = co;
488 #ifdef _MCO_USE_ASAN
489  if(prev_co) {
490  void* bottom_old = NULL;
491  size_t size_old = 0;
492  __sanitizer_finish_switch_fiber(prev_co->asan_prev_stack, (const void**)&bottom_old, &size_old);
493  prev_co->asan_prev_stack = NULL;
494  }
495  __sanitizer_start_switch_fiber(&co->asan_prev_stack, co->stack_base, co->stack_size);
496 #endif
497 #ifdef _MCO_USE_TSAN
498  co->tsan_prev_fiber = __tsan_get_current_fiber();
499  __tsan_switch_to_fiber(co->tsan_fiber, 0);
500 #endif
501 }
502 
503 static MCO_FORCE_INLINE void _mco_prepare_jumpout(mco_coro* co) {
504  /* Switch back to the previous running coroutine. */
505  /* MCO_ASSERT(mco_running() == co); */
506  mco_coro* prev_co = co->prev_co;
507  co->prev_co = NULL;
508  if(prev_co) {
509  /* MCO_ASSERT(prev_co->state == MCO_NORMAL); */
510  prev_co->state = MCO_RUNNING;
511  }
512  mco_current_co = prev_co;
513 #ifdef _MCO_USE_ASAN
514  void* bottom_old = NULL;
515  size_t size_old = 0;
516  __sanitizer_finish_switch_fiber(co->asan_prev_stack, (const void**)&bottom_old, &size_old);
517  co->asan_prev_stack = NULL;
518  if(prev_co) {
519  __sanitizer_start_switch_fiber(&prev_co->asan_prev_stack, bottom_old, size_old);
520  }
521 #endif
522 #ifdef _MCO_USE_TSAN
523  void* tsan_prev_fiber = co->tsan_prev_fiber;
524  co->tsan_prev_fiber = NULL;
525  __tsan_switch_to_fiber(tsan_prev_fiber, 0);
526 #endif
527 }
528 
529 static void _mco_jumpin(mco_coro* co);
530 static void _mco_jumpout(mco_coro* co);
531 
532 static MCO_NO_INLINE void _mco_main(mco_coro* co) {
533  co->func(co); /* Run the coroutine function. */
534  co->state = MCO_DEAD; /* Coroutine finished successfully, set state to dead. */
535  _mco_jumpout(co); /* Jump back to the old context .*/
536 }
537 
538 /* ---------------------------------------------------------------------------------------------- */
539 
540 #if defined(MCO_USE_UCONTEXT) || defined(MCO_USE_ASM)
541 
542 /*
543 Some of the following assembly code is taken from LuaCoco by Mike Pall.
544 See https://coco.luajit.org/index.html
545 
546 MIT license
547 
548 Copyright (C) 2004-2016 Mike Pall. All rights reserved.
549 
550 Permission is hereby granted, free of charge, to any person obtaining
551 a copy of this software and associated documentation files (the
552 "Software"), to deal in the Software without restriction, including
553 without limitation the rights to use, copy, modify, merge, publish,
554 distribute, sublicense, and/or sell copies of the Software, and to
555 permit persons to whom the Software is furnished to do so, subject to
556 the following conditions:
557 
558 The above copyright notice and this permission notice shall be
559 included in all copies or substantial portions of the Software.
560 
561 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
562 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
563 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
564 IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
565 CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
566 TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
567 SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
568 */
569 
570 #ifdef MCO_USE_ASM
571 
572 #if defined(__x86_64__) || defined(_M_X64)
573 
574 #ifdef _WIN32
575 
576 typedef struct _mco_ctxbuf {
577  void *rip, *rsp, *rbp, *rbx, *r12, *r13, *r14, *r15, *rdi, *rsi;
578  void* xmm[20]; /* xmm6, xmm7, xmm8, xmm9, xmm10, xmm11, xmm12, xmm13, xmm14, xmm15 */
579  void* fiber_storage;
580  void* dealloc_stack;
581  void* stack_limit;
582  void* stack_base;
583 } _mco_ctxbuf;
584 
585 #if defined(__GNUC__)
586 #define _MCO_ASM_BLOB __attribute__((section(".text")))
587 #elif defined(_MSC_VER)
588 #define _MCO_ASM_BLOB __declspec(allocate(".text"))
589 #pragma section(".text")
590 #endif
591 
592 _MCO_ASM_BLOB static unsigned char _mco_wrap_main_code[] = {
593  0x4c, 0x89, 0xe9, /* mov %r13,%rcx */
594  0x41, 0xff, 0xe4, /* jmpq *%r12 */
595  0xc3, /* retq */
596  0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90 /* nop */
597 };
598 
599 _MCO_ASM_BLOB static unsigned char _mco_switch_code[] = {
600  0x48, 0x8d, 0x05, 0x3e, 0x01, 0x00, 0x00, /* lea 0x13e(%rip),%rax */
601  0x48, 0x89, 0x01, /* mov %rax,(%rcx) */
602  0x48, 0x89, 0x61, 0x08, /* mov %rsp,0x8(%rcx) */
603  0x48, 0x89, 0x69, 0x10, /* mov %rbp,0x10(%rcx) */
604  0x48, 0x89, 0x59, 0x18, /* mov %rbx,0x18(%rcx) */
605  0x4c, 0x89, 0x61, 0x20, /* mov %r12,0x20(%rcx) */
606  0x4c, 0x89, 0x69, 0x28, /* mov %r13,0x28(%rcx) */
607  0x4c, 0x89, 0x71, 0x30, /* mov %r14,0x30(%rcx) */
608  0x4c, 0x89, 0x79, 0x38, /* mov %r15,0x38(%rcx) */
609  0x48, 0x89, 0x79, 0x40, /* mov %rdi,0x40(%rcx) */
610  0x48, 0x89, 0x71, 0x48, /* mov %rsi,0x48(%rcx) */
611  0x0f, 0x11, 0x71, 0x50, /* movups %xmm6,0x50(%rcx) */
612  0x0f, 0x11, 0x79, 0x60, /* movups %xmm7,0x60(%rcx) */
613  0x44, 0x0f, 0x11, 0x41, 0x70, /* movups %xmm8,0x70(%rcx) */
614  0x44, 0x0f, 0x11, 0x89, 0x80, 0x00, 0x00, 0x00, /* movups %xmm9,0x80(%rcx) */
615  0x44, 0x0f, 0x11, 0x91, 0x90, 0x00, 0x00, 0x00, /* movups %xmm10,0x90(%rcx) */
616  0x44, 0x0f, 0x11, 0x99, 0xa0, 0x00, 0x00, 0x00, /* movups %xmm11,0xa0(%rcx) */
617  0x44, 0x0f, 0x11, 0xa1, 0xb0, 0x00, 0x00, 0x00, /* movups %xmm12,0xb0(%rcx) */
618  0x44, 0x0f, 0x11, 0xa9, 0xc0, 0x00, 0x00, 0x00, /* movups %xmm13,0xc0(%rcx) */
619  0x44, 0x0f, 0x11, 0xb1, 0xd0, 0x00, 0x00, 0x00, /* movups %xmm14,0xd0(%rcx) */
620  0x44, 0x0f, 0x11, 0xb9, 0xe0, 0x00, 0x00, 0x00, /* movups %xmm15,0xe0(%rcx) */
621  0x65, 0x4c, 0x8b, 0x14, 0x25, 0x30, 0x00, 0x00, 0x00, /* mov %gs:0x30,%r10 */
622  0x49, 0x8b, 0x42, 0x20, /* mov 0x20(%r10),%rax */
623  0x48, 0x89, 0x81, 0xf0, 0x00, 0x00, 0x00, /* mov %rax,0xf0(%rcx) */
624  0x49, 0x8b, 0x82, 0x78, 0x14, 0x00, 0x00, /* mov 0x1478(%r10),%rax */
625  0x48, 0x89, 0x81, 0xf8, 0x00, 0x00, 0x00, /* mov %rax,0xf8(%rcx) */
626  0x49, 0x8b, 0x42, 0x10, /* mov 0x10(%r10),%rax */
627  0x48, 0x89, 0x81, 0x00, 0x01, 0x00, 0x00, /* mov %rax,0x100(%rcx) */
628  0x49, 0x8b, 0x42, 0x08, /* mov 0x8(%r10),%rax */
629  0x48, 0x89, 0x81, 0x08, 0x01, 0x00, 0x00, /* mov %rax,0x108(%rcx) */
630  0x48, 0x8b, 0x82, 0x08, 0x01, 0x00, 0x00, /* mov 0x108(%rdx),%rax */
631  0x49, 0x89, 0x42, 0x08, /* mov %rax,0x8(%r10) */
632  0x48, 0x8b, 0x82, 0x00, 0x01, 0x00, 0x00, /* mov 0x100(%rdx),%rax */
633  0x49, 0x89, 0x42, 0x10, /* mov %rax,0x10(%r10) */
634  0x48, 0x8b, 0x82, 0xf8, 0x00, 0x00, 0x00, /* mov 0xf8(%rdx),%rax */
635  0x49, 0x89, 0x82, 0x78, 0x14, 0x00, 0x00, /* mov %rax,0x1478(%r10) */
636  0x48, 0x8b, 0x82, 0xf0, 0x00, 0x00, 0x00, /* mov 0xf0(%rdx),%rax */
637  0x49, 0x89, 0x42, 0x20, /* mov %rax,0x20(%r10) */
638  0x44, 0x0f, 0x10, 0xba, 0xe0, 0x00, 0x00, 0x00, /* movups 0xe0(%rdx),%xmm15 */
639  0x44, 0x0f, 0x10, 0xb2, 0xd0, 0x00, 0x00, 0x00, /* movups 0xd0(%rdx),%xmm14 */
640  0x44, 0x0f, 0x10, 0xaa, 0xc0, 0x00, 0x00, 0x00, /* movups 0xc0(%rdx),%xmm13 */
641  0x44, 0x0f, 0x10, 0xa2, 0xb0, 0x00, 0x00, 0x00, /* movups 0xb0(%rdx),%xmm12 */
642  0x44, 0x0f, 0x10, 0x9a, 0xa0, 0x00, 0x00, 0x00, /* movups 0xa0(%rdx),%xmm11 */
643  0x44, 0x0f, 0x10, 0x92, 0x90, 0x00, 0x00, 0x00, /* movups 0x90(%rdx),%xmm10 */
644  0x44, 0x0f, 0x10, 0x8a, 0x80, 0x00, 0x00, 0x00, /* movups 0x80(%rdx),%xmm9 */
645  0x44, 0x0f, 0x10, 0x42, 0x70, /* movups 0x70(%rdx),%xmm8 */
646  0x0f, 0x10, 0x7a, 0x60, /* movups 0x60(%rdx),%xmm7 */
647  0x0f, 0x10, 0x72, 0x50, /* movups 0x50(%rdx),%xmm6 */
648  0x48, 0x8b, 0x72, 0x48, /* mov 0x48(%rdx),%rsi */
649  0x48, 0x8b, 0x7a, 0x40, /* mov 0x40(%rdx),%rdi */
650  0x4c, 0x8b, 0x7a, 0x38, /* mov 0x38(%rdx),%r15 */
651  0x4c, 0x8b, 0x72, 0x30, /* mov 0x30(%rdx),%r14 */
652  0x4c, 0x8b, 0x6a, 0x28, /* mov 0x28(%rdx),%r13 */
653  0x4c, 0x8b, 0x62, 0x20, /* mov 0x20(%rdx),%r12 */
654  0x48, 0x8b, 0x5a, 0x18, /* mov 0x18(%rdx),%rbx */
655  0x48, 0x8b, 0x6a, 0x10, /* mov 0x10(%rdx),%rbp */
656  0x48, 0x8b, 0x62, 0x08, /* mov 0x8(%rdx),%rsp */
657  0xff, 0x22, /* jmpq *(%rdx) */
658  0xc3, /* retq */
659  0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, /* nop */
660  0x90, 0x90, /* nop */
661 };
662 
663 void (*_mco_wrap_main)(void) = (void(*)(void))(void*)_mco_wrap_main_code;
664 void (*_mco_switch)(_mco_ctxbuf* from, _mco_ctxbuf* to) = (void(*)(_mco_ctxbuf* from, _mco_ctxbuf* to))(void*)_mco_switch_code;
665 
666 static mco_result _mco_makectx(mco_coro* co, _mco_ctxbuf* ctx, void* stack_base, size_t stack_size) {
667  stack_size = stack_size - 32; /* Reserve 32 bytes for the shadow space. */
668  void** stack_high_ptr = (void**)((size_t)stack_base + stack_size - sizeof(size_t));
669  stack_high_ptr[0] = (void*)(0xdeaddeaddeaddead); /* Dummy return address. */
670  ctx->rip = (void*)(_mco_wrap_main);
671  ctx->rsp = (void*)(stack_high_ptr);
672  ctx->r12 = (void*)(_mco_main);
673  ctx->r13 = (void*)(co);
674  void* stack_top = (void*)((size_t)stack_base + stack_size);
675  ctx->stack_base = stack_top;
676  ctx->stack_limit = stack_base;
677  ctx->dealloc_stack = stack_base;
678  return MCO_SUCCESS;
679 }
680 
681 #else /* not _WIN32 */
682 
683 typedef struct _mco_ctxbuf {
684  void *rip, *rsp, *rbp, *rbx, *r12, *r13, *r14, *r15;
685 } _mco_ctxbuf;
686 
687 void _mco_wrap_main(void);
688 int _mco_switch(_mco_ctxbuf* from, _mco_ctxbuf* to);
689 
690 __asm__(
691  ".text\n"
692 #ifdef __MACH__ /* Mac OS X assembler */
693  ".globl __mco_wrap_main\n"
694  "__mco_wrap_main:\n"
695 #else /* Linux assembler */
696  ".globl _mco_wrap_main\n"
697  ".type _mco_wrap_main @function\n"
698  ".hidden _mco_wrap_main\n"
699  "_mco_wrap_main:\n"
700 #endif
701  " movq %r13, %rdi\n"
702  " jmpq *%r12\n"
703 #ifndef __MACH__
704  ".size _mco_wrap_main, .-_mco_wrap_main\n"
705 #endif
706 );
707 
708 __asm__(
709  ".text\n"
710 #ifdef __MACH__ /* Mac OS assembler */
711  ".globl __mco_switch\n"
712  "__mco_switch:\n"
713 #else /* Linux assembler */
714  ".globl _mco_switch\n"
715  ".type _mco_switch @function\n"
716  ".hidden _mco_switch\n"
717  "_mco_switch:\n"
718 #endif
719  " leaq 0x3d(%rip), %rax\n"
720  " movq %rax, (%rdi)\n"
721  " movq %rsp, 8(%rdi)\n"
722  " movq %rbp, 16(%rdi)\n"
723  " movq %rbx, 24(%rdi)\n"
724  " movq %r12, 32(%rdi)\n"
725  " movq %r13, 40(%rdi)\n"
726  " movq %r14, 48(%rdi)\n"
727  " movq %r15, 56(%rdi)\n"
728  " movq 56(%rsi), %r15\n"
729  " movq 48(%rsi), %r14\n"
730  " movq 40(%rsi), %r13\n"
731  " movq 32(%rsi), %r12\n"
732  " movq 24(%rsi), %rbx\n"
733  " movq 16(%rsi), %rbp\n"
734  " movq 8(%rsi), %rsp\n"
735  " jmpq *(%rsi)\n"
736  " ret\n"
737 #ifndef __MACH__
738  ".size _mco_switch, .-_mco_switch\n"
739 #endif
740 );
741 
742 static mco_result _mco_makectx(mco_coro* co, _mco_ctxbuf* ctx, void* stack_base, size_t stack_size) {
743  stack_size = stack_size - 128; /* Reserve 128 bytes for the Red Zone space (System V AMD64 ABI). */
744  void** stack_high_ptr = (void**)((size_t)stack_base + stack_size - sizeof(size_t));
745  stack_high_ptr[0] = (void*)(0xdeaddeaddeaddead); /* Dummy return address. */
746  ctx->rip = (void*)(_mco_wrap_main);
747  ctx->rsp = (void*)(stack_high_ptr);
748  ctx->r12 = (void*)(_mco_main);
749  ctx->r13 = (void*)(co);
750  return MCO_SUCCESS;
751 }
752 
753 #endif /* not _WIN32 */
754 
755 #elif defined(__riscv)
756 
757 typedef struct _mco_ctxbuf {
758  void* s[12]; /* s0-s11 */
759  void* ra;
760  void* pc;
761  void* sp;
762 #ifdef __riscv_flen
763 #if __riscv_flen == 64
764  double fs[12]; /* fs0-fs11 */
765 #elif __riscv_flen == 32
766  float fs[12]; /* fs0-fs11 */
767 #endif
768 #endif /* __riscv_flen */
769 } _mco_ctxbuf;
770 
771 void _mco_wrap_main(void);
772 int _mco_switch(_mco_ctxbuf* from, _mco_ctxbuf* to);
773 
774 __asm__(
775  ".text\n"
776  ".globl _mco_wrap_main\n"
777  ".type _mco_wrap_main @function\n"
778  ".hidden _mco_wrap_main\n"
779  "_mco_wrap_main:\n"
780  " mv a0, s0\n"
781  " jr s1\n"
782  ".size _mco_wrap_main, .-_mco_wrap_main\n"
783 );
784 
785 __asm__(
786  ".text\n"
787  ".globl _mco_switch\n"
788  ".type _mco_switch @function\n"
789  ".hidden _mco_switch\n"
790  "_mco_switch:\n"
791  #if __riscv_xlen == 64
792  " sd s0, 0x00(a0)\n"
793  " sd s1, 0x08(a0)\n"
794  " sd s2, 0x10(a0)\n"
795  " sd s3, 0x18(a0)\n"
796  " sd s4, 0x20(a0)\n"
797  " sd s5, 0x28(a0)\n"
798  " sd s6, 0x30(a0)\n"
799  " sd s7, 0x38(a0)\n"
800  " sd s8, 0x40(a0)\n"
801  " sd s9, 0x48(a0)\n"
802  " sd s10, 0x50(a0)\n"
803  " sd s11, 0x58(a0)\n"
804  " sd ra, 0x60(a0)\n"
805  " sd ra, 0x68(a0)\n" /* pc */
806  " sd sp, 0x70(a0)\n"
807  #ifdef __riscv_flen
808  #if __riscv_flen == 64
809  " fsd fs0, 0x78(a0)\n"
810  " fsd fs1, 0x80(a0)\n"
811  " fsd fs2, 0x88(a0)\n"
812  " fsd fs3, 0x90(a0)\n"
813  " fsd fs4, 0x98(a0)\n"
814  " fsd fs5, 0xa0(a0)\n"
815  " fsd fs6, 0xa8(a0)\n"
816  " fsd fs7, 0xb0(a0)\n"
817  " fsd fs8, 0xb8(a0)\n"
818  " fsd fs9, 0xc0(a0)\n"
819  " fsd fs10, 0xc8(a0)\n"
820  " fsd fs11, 0xd0(a0)\n"
821  " fld fs0, 0x78(a1)\n"
822  " fld fs1, 0x80(a1)\n"
823  " fld fs2, 0x88(a1)\n"
824  " fld fs3, 0x90(a1)\n"
825  " fld fs4, 0x98(a1)\n"
826  " fld fs5, 0xa0(a1)\n"
827  " fld fs6, 0xa8(a1)\n"
828  " fld fs7, 0xb0(a1)\n"
829  " fld fs8, 0xb8(a1)\n"
830  " fld fs9, 0xc0(a1)\n"
831  " fld fs10, 0xc8(a1)\n"
832  " fld fs11, 0xd0(a1)\n"
833  #else
834  #error "Unsupported RISC-V FLEN"
835  #endif
836  #endif /* __riscv_flen */
837  " ld s0, 0x00(a1)\n"
838  " ld s1, 0x08(a1)\n"
839  " ld s2, 0x10(a1)\n"
840  " ld s3, 0x18(a1)\n"
841  " ld s4, 0x20(a1)\n"
842  " ld s5, 0x28(a1)\n"
843  " ld s6, 0x30(a1)\n"
844  " ld s7, 0x38(a1)\n"
845  " ld s8, 0x40(a1)\n"
846  " ld s9, 0x48(a1)\n"
847  " ld s10, 0x50(a1)\n"
848  " ld s11, 0x58(a1)\n"
849  " ld ra, 0x60(a1)\n"
850  " ld a2, 0x68(a1)\n" /* pc */
851  " ld sp, 0x70(a1)\n"
852  " jr a2\n"
853  #elif __riscv_xlen == 32
854  " sw s0, 0x00(a0)\n"
855  " sw s1, 0x04(a0)\n"
856  " sw s2, 0x08(a0)\n"
857  " sw s3, 0x0c(a0)\n"
858  " sw s4, 0x10(a0)\n"
859  " sw s5, 0x14(a0)\n"
860  " sw s6, 0x18(a0)\n"
861  " sw s7, 0x1c(a0)\n"
862  " sw s8, 0x20(a0)\n"
863  " sw s9, 0x24(a0)\n"
864  " sw s10, 0x28(a0)\n"
865  " sw s11, 0x2c(a0)\n"
866  " sw ra, 0x30(a0)\n"
867  " sw ra, 0x34(a0)\n" /* pc */
868  " sw sp, 0x38(a0)\n"
869  #ifdef __riscv_flen
870  #if __riscv_flen == 64
871  " fsd fs0, 0x3c(a0)\n"
872  " fsd fs1, 0x44(a0)\n"
873  " fsd fs2, 0x4c(a0)\n"
874  " fsd fs3, 0x54(a0)\n"
875  " fsd fs4, 0x5c(a0)\n"
876  " fsd fs5, 0x64(a0)\n"
877  " fsd fs6, 0x6c(a0)\n"
878  " fsd fs7, 0x74(a0)\n"
879  " fsd fs8, 0x7c(a0)\n"
880  " fsd fs9, 0x84(a0)\n"
881  " fsd fs10, 0x8c(a0)\n"
882  " fsd fs11, 0x94(a0)\n"
883  " fld fs0, 0x3c(a1)\n"
884  " fld fs1, 0x44(a1)\n"
885  " fld fs2, 0x4c(a1)\n"
886  " fld fs3, 0x54(a1)\n"
887  " fld fs4, 0x5c(a1)\n"
888  " fld fs5, 0x64(a1)\n"
889  " fld fs6, 0x6c(a1)\n"
890  " fld fs7, 0x74(a1)\n"
891  " fld fs8, 0x7c(a1)\n"
892  " fld fs9, 0x84(a1)\n"
893  " fld fs10, 0x8c(a1)\n"
894  " fld fs11, 0x94(a1)\n"
895  #elif __riscv_flen == 32
896  " fsw fs0, 0x3c(a0)\n"
897  " fsw fs1, 0x40(a0)\n"
898  " fsw fs2, 0x44(a0)\n"
899  " fsw fs3, 0x48(a0)\n"
900  " fsw fs4, 0x4c(a0)\n"
901  " fsw fs5, 0x50(a0)\n"
902  " fsw fs6, 0x54(a0)\n"
903  " fsw fs7, 0x58(a0)\n"
904  " fsw fs8, 0x5c(a0)\n"
905  " fsw fs9, 0x60(a0)\n"
906  " fsw fs10, 0x64(a0)\n"
907  " fsw fs11, 0x68(a0)\n"
908  " flw fs0, 0x3c(a1)\n"
909  " flw fs1, 0x40(a1)\n"
910  " flw fs2, 0x44(a1)\n"
911  " flw fs3, 0x48(a1)\n"
912  " flw fs4, 0x4c(a1)\n"
913  " flw fs5, 0x50(a1)\n"
914  " flw fs6, 0x54(a1)\n"
915  " flw fs7, 0x58(a1)\n"
916  " flw fs8, 0x5c(a1)\n"
917  " flw fs9, 0x60(a1)\n"
918  " flw fs10, 0x64(a1)\n"
919  " flw fs11, 0x68(a1)\n"
920  #else
921  #error "Unsupported RISC-V FLEN"
922  #endif
923  #endif /* __riscv_flen */
924  " lw s0, 0x00(a1)\n"
925  " lw s1, 0x04(a1)\n"
926  " lw s2, 0x08(a1)\n"
927  " lw s3, 0x0c(a1)\n"
928  " lw s4, 0x10(a1)\n"
929  " lw s5, 0x14(a1)\n"
930  " lw s6, 0x18(a1)\n"
931  " lw s7, 0x1c(a1)\n"
932  " lw s8, 0x20(a1)\n"
933  " lw s9, 0x24(a1)\n"
934  " lw s10, 0x28(a1)\n"
935  " lw s11, 0x2c(a1)\n"
936  " lw ra, 0x30(a1)\n"
937  " lw a2, 0x34(a1)\n" /* pc */
938  " lw sp, 0x38(a1)\n"
939  " jr a2\n"
940  #else
941  #error "Unsupported RISC-V XLEN"
942  #endif /* __riscv_xlen */
943  ".size _mco_switch, .-_mco_switch\n"
944 );
945 
946 static mco_result _mco_makectx(mco_coro* co, _mco_ctxbuf* ctx, void* stack_base, size_t stack_size) {
947  ctx->s[0] = (void*)(co);
948  ctx->s[1] = (void*)(_mco_main);
949  ctx->pc = (void*)(_mco_wrap_main);
950 #if __riscv_xlen == 64
951  ctx->ra = (void*)(0xdeaddeaddeaddead);
952 #elif __riscv_xlen == 32
953  ctx->ra = (void*)(0xdeaddead);
954 #endif
955  ctx->sp = (void*)((size_t)stack_base + stack_size);
956  return MCO_SUCCESS;
957 }
958 
959 #elif defined(__i386) || defined(__i386__)
960 
961 typedef struct _mco_ctxbuf {
962  void *eip, *esp, *ebp, *ebx, *esi, *edi;
963 } _mco_ctxbuf;
964 
965 void _mco_switch(_mco_ctxbuf* from, _mco_ctxbuf* to);
966 
967 __asm__(
968 #ifdef __DJGPP__ /* DOS compiler */
969  "__mco_switch:\n"
970 #else
971  ".text\n"
972  ".globl _mco_switch\n"
973  ".type _mco_switch @function\n"
974  ".hidden _mco_switch\n"
975  "_mco_switch:\n"
976 #endif
977  " call 1f\n"
978  " 1:\n"
979  " popl %ecx\n"
980  " addl $(2f-1b), %ecx\n"
981  " movl 4(%esp), %eax\n"
982  " movl 8(%esp), %edx\n"
983  " movl %ecx, (%eax)\n"
984  " movl %esp, 4(%eax)\n"
985  " movl %ebp, 8(%eax)\n"
986  " movl %ebx, 12(%eax)\n"
987  " movl %esi, 16(%eax)\n"
988  " movl %edi, 20(%eax)\n"
989  " movl 20(%edx), %edi\n"
990  " movl 16(%edx), %esi\n"
991  " movl 12(%edx), %ebx\n"
992  " movl 8(%edx), %ebp\n"
993  " movl 4(%edx), %esp\n"
994  " jmp *(%edx)\n"
995  " 2:\n"
996  " ret\n"
997 #ifndef __DJGPP__
998  ".size _mco_switch, .-_mco_switch\n"
999 #endif
1000 );
1001 
1002 static mco_result _mco_makectx(mco_coro* co, _mco_ctxbuf* ctx, void* stack_base, size_t stack_size) {
1003  void** stack_high_ptr = (void**)((size_t)stack_base + stack_size - 16 - 1*sizeof(size_t));
1004  stack_high_ptr[0] = (void*)(0xdeaddead); /* Dummy return address. */
1005  stack_high_ptr[1] = (void*)(co);
1006  ctx->eip = (void*)(_mco_main);
1007  ctx->esp = (void*)(stack_high_ptr);
1008  return MCO_SUCCESS;
1009 }
1010 
1011 #elif defined(__ARM_EABI__)
1012 
1013 typedef struct _mco_ctxbuf {
1014 #ifndef __SOFTFP__
1015  void* f[16];
1016 #endif
1017  void *d[4]; /* d8-d15 */
1018  void *r[4]; /* r4-r11 */
1019  void *lr;
1020  void *sp;
1021 } _mco_ctxbuf;
1022 
1023 void _mco_wrap_main(void);
1024 int _mco_switch(_mco_ctxbuf* from, _mco_ctxbuf* to);
1025 
1026 __asm__(
1027  ".text\n"
1028 #ifdef __APPLE__
1029  ".globl __mco_switch\n"
1030  "__mco_switch:\n"
1031 #else
1032  ".globl _mco_switch\n"
1033  ".type _mco_switch #function\n"
1034  ".hidden _mco_switch\n"
1035  "_mco_switch:\n"
1036 #endif
1037 #ifndef __SOFTFP__
1038  " vstmia r0!, {d8-d15}\n"
1039 #endif
1040  " stmia r0, {r4-r11, lr}\n"
1041  " str sp, [r0, #9*4]\n"
1042 #ifndef __SOFTFP__
1043  " vldmia r1!, {d8-d15}\n"
1044 #endif
1045  " ldr sp, [r1, #9*4]\n"
1046  " ldmia r1, {r4-r11, pc}\n"
1047 #ifndef __APPLE__
1048  ".size _mco_switch, .-_mco_switch\n"
1049 #endif
1050 );
1051 
1052 __asm__(
1053  ".text\n"
1054 #ifdef __APPLE__
1055  ".globl __mco_wrap_main\n"
1056  "__mco_wrap_main:\n"
1057 #else
1058  ".globl _mco_wrap_main\n"
1059  ".type _mco_wrap_main #function\n"
1060  ".hidden _mco_wrap_main\n"
1061  "_mco_wrap_main:\n"
1062 #endif
1063  " mov r0, r4\n"
1064  " mov ip, r5\n"
1065  " mov lr, r6\n"
1066  " bx ip\n"
1067 #ifndef __APPLE__
1068  ".size _mco_wrap_main, .-_mco_wrap_main\n"
1069 #endif
1070 );
1071 
1072 static mco_result _mco_makectx(mco_coro* co, _mco_ctxbuf* ctx, void* stack_base, size_t stack_size) {
1073  ctx->d[0] = (void*)(co);
1074  ctx->d[1] = (void*)(_mco_main);
1075  ctx->d[2] = (void*)(0xdeaddead); /* Dummy return address. */
1076  ctx->lr = (void*)(_mco_wrap_main);
1077  ctx->sp = (void*)((size_t)stack_base + stack_size);
1078  return MCO_SUCCESS;
1079 }
1080 
1081 #elif defined(__aarch64__)
1082 
1083 typedef struct _mco_ctxbuf {
1084  void *x[12]; /* x19-x30 */
1085  void *sp;
1086  void *lr;
1087  void *d[8]; /* d8-d15 */
1088 } _mco_ctxbuf;
1089 
1090 void _mco_wrap_main(void);
1091 int _mco_switch(_mco_ctxbuf* from, _mco_ctxbuf* to);
1092 
1093 __asm__(
1094  ".text\n"
1095 #ifdef __APPLE__
1096  ".globl __mco_switch\n"
1097  "__mco_switch:\n"
1098 #else
1099  ".globl _mco_switch\n"
1100  ".type _mco_switch #function\n"
1101  ".hidden _mco_switch\n"
1102  "_mco_switch:\n"
1103 #endif
1104 
1105  " mov x10, sp\n"
1106  " mov x11, x30\n"
1107  " stp x19, x20, [x0, #(0*16)]\n"
1108  " stp x21, x22, [x0, #(1*16)]\n"
1109  " stp d8, d9, [x0, #(7*16)]\n"
1110  " stp x23, x24, [x0, #(2*16)]\n"
1111  " stp d10, d11, [x0, #(8*16)]\n"
1112  " stp x25, x26, [x0, #(3*16)]\n"
1113  " stp d12, d13, [x0, #(9*16)]\n"
1114  " stp x27, x28, [x0, #(4*16)]\n"
1115  " stp d14, d15, [x0, #(10*16)]\n"
1116  " stp x29, x30, [x0, #(5*16)]\n"
1117  " stp x10, x11, [x0, #(6*16)]\n"
1118  " ldp x19, x20, [x1, #(0*16)]\n"
1119  " ldp x21, x22, [x1, #(1*16)]\n"
1120  " ldp d8, d9, [x1, #(7*16)]\n"
1121  " ldp x23, x24, [x1, #(2*16)]\n"
1122  " ldp d10, d11, [x1, #(8*16)]\n"
1123  " ldp x25, x26, [x1, #(3*16)]\n"
1124  " ldp d12, d13, [x1, #(9*16)]\n"
1125  " ldp x27, x28, [x1, #(4*16)]\n"
1126  " ldp d14, d15, [x1, #(10*16)]\n"
1127  " ldp x29, x30, [x1, #(5*16)]\n"
1128  " ldp x10, x11, [x1, #(6*16)]\n"
1129  " mov sp, x10\n"
1130  " br x11\n"
1131 #ifndef __APPLE__
1132  ".size _mco_switch, .-_mco_switch\n"
1133 #endif
1134 );
1135 
1136 __asm__(
1137  ".text\n"
1138 #ifdef __APPLE__
1139  ".globl __mco_wrap_main\n"
1140  "__mco_wrap_main:\n"
1141 #else
1142  ".globl _mco_wrap_main\n"
1143  ".type _mco_wrap_main #function\n"
1144  ".hidden _mco_wrap_main\n"
1145  "_mco_wrap_main:\n"
1146 #endif
1147  " mov x0, x19\n"
1148  " mov x30, x21\n"
1149  " br x20\n"
1150 #ifndef __APPLE__
1151  ".size _mco_wrap_main, .-_mco_wrap_main\n"
1152 #endif
1153 );
1154 
1155 static mco_result _mco_makectx(mco_coro* co, _mco_ctxbuf* ctx, void* stack_base, size_t stack_size) {
1156  ctx->x[0] = (void*)(co);
1157  ctx->x[1] = (void*)(_mco_main);
1158  ctx->x[2] = (void*)(0xdeaddeaddeaddead); /* Dummy return address. */
1159  ctx->sp = (void*)((size_t)stack_base + stack_size);
1160  ctx->lr = (void*)(_mco_wrap_main);
1161  return MCO_SUCCESS;
1162 }
1163 
1164 #else
1165 
1166 #error "Unsupported architecture for assembly method."
1167 
1168 #endif /* ARCH */
1169 
1170 #elif defined(MCO_USE_UCONTEXT)
1171 
1172 #include <ucontext.h>
1173 
1174 typedef ucontext_t _mco_ctxbuf;
1175 
1176 #if defined(_LP64) || defined(__LP64__)
1177 static void _mco_wrap_main(unsigned int lo, unsigned int hi) {
1178  mco_coro* co = (mco_coro*)(((size_t)lo) | (((size_t)hi) << 32)); /* Extract coroutine pointer. */
1179  _mco_main(co);
1180 }
1181 #else
1182 static void _mco_wrap_main(unsigned int lo) {
1183  mco_coro* co = (mco_coro*)((size_t)lo); /* Extract coroutine pointer. */
1184  _mco_main(co);
1185 }
1186 #endif
1187 
1188 static MCO_FORCE_INLINE void _mco_switch(_mco_ctxbuf* from, _mco_ctxbuf* to) {
1189  int res = swapcontext(from, to);
1190  _MCO_UNUSED(res);
1191  MCO_ASSERT(res == 0);
1192 }
1193 
1194 static mco_result _mco_makectx(mco_coro* co, _mco_ctxbuf* ctx, void* stack_base, size_t stack_size) {
1195  /* Initialize ucontext. */
1196  if(getcontext(ctx) != 0) {
1197  MCO_LOG("failed to get ucontext");
1198  return MCO_MAKE_CONTEXT_ERROR;
1199  }
1200  ctx->uc_link = NULL; /* We never exit from _mco_wrap_main. */
1201  ctx->uc_stack.ss_sp = stack_base;
1202  ctx->uc_stack.ss_size = stack_size;
1203  unsigned int lo = (unsigned int)((size_t)co);
1204 #if defined(_LP64) || defined(__LP64__)
1205  unsigned int hi = (unsigned int)(((size_t)co)>>32);
1206  makecontext(ctx, (void (*)(void))_mco_wrap_main, 2, lo, hi);
1207 #else
1208  makecontext(ctx, (void (*)(void))_mco_wrap_main, 1, lo);
1209 #endif
1210  return MCO_SUCCESS;
1211 }
1212 
1213 #endif /* defined(MCO_USE_UCONTEXT) */
1214 
1215 #ifdef MCO_USE_VALGRIND
1216 #include <valgrind/valgrind.h>
1217 #endif
1218 
1219 typedef struct _mco_context {
1220 #ifdef MCO_USE_VALGRIND
1221  unsigned int valgrind_stack_id;
1222 #endif
1223  _mco_ctxbuf ctx;
1224  _mco_ctxbuf back_ctx;
1225 } _mco_context;
1226 
1227 static void _mco_jumpin(mco_coro* co) {
1228  _mco_context* context = (_mco_context*)co->context;
1229  _mco_prepare_jumpin(co);
1230  _mco_switch(&context->back_ctx, &context->ctx); /* Do the context switch. */
1231 }
1232 
1233 static void _mco_jumpout(mco_coro* co) {
1234  _mco_context* context = (_mco_context*)co->context;
1235  _mco_prepare_jumpout(co);
1236  _mco_switch(&context->ctx, &context->back_ctx); /* Do the context switch. */
1237 }
1238 
1239 static mco_result _mco_create_context(mco_coro* co, mco_desc* desc) {
1240  /* Determine the context and stack address. */
1241  size_t co_addr = (size_t)co;
1242  size_t context_addr = _mco_align_forward(co_addr + sizeof(mco_coro), 16);
1243  size_t storage_addr = _mco_align_forward(context_addr + sizeof(_mco_context), 16);
1244  size_t stack_addr = _mco_align_forward(storage_addr + desc->storage_size, 16);
1245  /* Initialize context. */
1246  _mco_context* context = (_mco_context*)context_addr;
1247  memset(context, 0, sizeof(_mco_context));
1248  /* Initialize storage. */
1249  unsigned char* storage = (unsigned char*)storage_addr;
1250  memset(storage, 0, desc->storage_size);
1251  /* Initialize stack. */
1252  void *stack_base = (void*)stack_addr;
1253  size_t stack_size = desc->stack_size;
1254 #ifdef MCO_ZERO_MEMORY
1255  memset(stack_base, 0, stack_size);
1256 #endif
1257  /* Make the context. */
1258  mco_result res = _mco_makectx(co, &context->ctx, stack_base, stack_size);
1259  if(res != MCO_SUCCESS) {
1260  return res;
1261  }
1262 #ifdef MCO_USE_VALGRIND
1263  context->valgrind_stack_id = VALGRIND_STACK_REGISTER(stack_addr, stack_addr + stack_size);
1264 #endif
1265  co->context = context;
1266  co->stack_base = stack_base;
1267  co->stack_size = stack_size;
1268  co->storage = storage;
1269  co->storage_size = desc->storage_size;
1270  return MCO_SUCCESS;
1271 }
1272 
1273 static void _mco_destroy_context(mco_coro* co) {
1274 #ifdef MCO_USE_VALGRIND
1275  _mco_context* context = (_mco_context*)co->context;
1276  if(context && context->valgrind_stack_id != 0) {
1277  VALGRIND_STACK_DEREGISTER(context->valgrind_stack_id);
1278  context->valgrind_stack_id = 0;
1279  }
1280 #else
1281  _MCO_UNUSED(co);
1282 #endif
1283 }
1284 
1285 static MCO_FORCE_INLINE void _mco_init_desc_sizes(mco_desc* desc, size_t stack_size) {
1286  desc->coro_size = _mco_align_forward(sizeof(mco_coro), 16) +
1287  _mco_align_forward(sizeof(_mco_context), 16) +
1288  _mco_align_forward(desc->storage_size, 16) +
1289  stack_size + 16;
1290  desc->stack_size = stack_size; /* This is just a hint, it won't be the real one. */
1291 }
1292 
1293 #endif /* defined(MCO_USE_UCONTEXT) || defined(MCO_USE_ASM) */
1294 
1295 /* ---------------------------------------------------------------------------------------------- */
1296 
1297 #ifdef MCO_USE_FIBERS
1298 
1299 #ifdef _WIN32
1300 
1301 #ifndef _WIN32_WINNT
1302 #define _WIN32_WINNT 0x0400
1303 #endif
1304 #ifndef WIN32_LEAN_AND_MEAN
1305 #define WIN32_LEAN_AND_MEAN
1306 #endif
1307 #include <windows.h>
1308 
1309 typedef struct _mco_context {
1310  void* fib;
1311  void* back_fib;
1312 } _mco_context;
1313 
1314 static void _mco_jumpin(mco_coro* co) {
1315  void *cur_fib = GetCurrentFiber();
1316  if(!cur_fib || cur_fib == (void*)0x1e00) { /* See http://blogs.msdn.com/oldnewthing/archive/2004/12/31/344799.aspx */
1317  cur_fib = ConvertThreadToFiber(NULL);
1318  }
1319  MCO_ASSERT(cur_fib != NULL);
1320  _mco_context* context = (_mco_context*)co->context;
1321  context->back_fib = cur_fib;
1322  _mco_prepare_jumpin(co);
1323  SwitchToFiber(context->fib);
1324 }
1325 
1326 static void CALLBACK _mco_wrap_main(void* co) {
1327  _mco_main((mco_coro*)co);
1328 }
1329 
1330 static void _mco_jumpout(mco_coro* co) {
1331  _mco_context* context = (_mco_context*)co->context;
1332  void* back_fib = context->back_fib;
1333  MCO_ASSERT(back_fib != NULL);
1334  context->back_fib = NULL;
1335  _mco_prepare_jumpout(co);
1336  SwitchToFiber(back_fib);
1337 }
1338 
1339 /* Reverse engineered Fiber struct, used to get stack base. */
1340 typedef struct _mco_fiber {
1341  LPVOID param; /* fiber param */
1342  void* except; /* saved exception handlers list */
1343  void* stack_base; /* top of fiber stack */
1344  void* stack_limit; /* fiber stack low-water mark */
1345  void* stack_allocation; /* base of the fiber stack allocation */
1346  CONTEXT context; /* fiber context */
1347  DWORD flags; /* fiber flags */
1348  LPFIBER_START_ROUTINE start; /* start routine */
1349  void **fls_slots; /* fiber storage slots */
1350 } _mco_fiber;
1351 
1352 static mco_result _mco_create_context(mco_coro* co, mco_desc* desc) {
1353  /* Determine the context address. */
1354  size_t co_addr = (size_t)co;
1355  size_t context_addr = _mco_align_forward(co_addr + sizeof(mco_coro), 16);
1356  size_t storage_addr = _mco_align_forward(context_addr + sizeof(_mco_context), 16);
1357  /* Initialize context. */
1358  _mco_context* context = (_mco_context*)context_addr;
1359  memset(context, 0, sizeof(_mco_context));
1360  /* Initialize storage. */
1361  unsigned char* storage = (unsigned char*)storage_addr;
1362  memset(storage, 0, desc->storage_size);
1363  /* Create the fiber. */
1364  _mco_fiber* fib = (_mco_fiber*)CreateFiberEx(desc->stack_size, desc->stack_size, FIBER_FLAG_FLOAT_SWITCH, _mco_wrap_main, co);
1365  if(!fib) {
1366  MCO_LOG("failed to create fiber");
1367  return MCO_MAKE_CONTEXT_ERROR;
1368  }
1369  context->fib = fib;
1370  co->context = context;
1371  co->stack_base = (void*)((size_t)fib->stack_base - desc->stack_size);
1372  co->stack_size = desc->stack_size;
1373  co->storage = storage;
1374  co->storage_size = desc->storage_size;
1375  return MCO_SUCCESS;
1376 }
1377 
1378 static void _mco_destroy_context(mco_coro* co) {
1379  _mco_context* context = (_mco_context*)co->context;
1380  if(context && context->fib) {
1381  DeleteFiber(context->fib);
1382  context->fib = NULL;
1383  }
1384 }
1385 
1386 static MCO_FORCE_INLINE void _mco_init_desc_sizes(mco_desc* desc, size_t stack_size) {
1387  desc->coro_size = _mco_align_forward(sizeof(mco_coro), 16) +
1388  _mco_align_forward(sizeof(_mco_context), 16) +
1389  _mco_align_forward(desc->storage_size, 16) +
1390  16;
1391  desc->stack_size = stack_size;
1392 }
1393 
1394 #elif defined(__EMSCRIPTEN__)
1395 
1396 #include <emscripten/fiber.h>
1397 
1398 #ifndef MCO_ASYNCFY_STACK_SIZE
1399 #define MCO_ASYNCFY_STACK_SIZE 16384
1400 #endif
1401 
1402 typedef struct _mco_context {
1403  emscripten_fiber_t fib;
1404  emscripten_fiber_t* back_fib;
1405 } _mco_context;
1406 
1407 static emscripten_fiber_t* running_fib = NULL;
1408 static unsigned char main_asyncify_stack[MCO_ASYNCFY_STACK_SIZE];
1409 static emscripten_fiber_t main_fib;
1410 
1411 static void _mco_wrap_main(void* co) {
1412  _mco_main((mco_coro*)co);
1413 }
1414 
1415 static void _mco_jumpin(mco_coro* co) {
1416  _mco_context* context = (_mco_context*)co->context;
1417  emscripten_fiber_t* back_fib = running_fib;
1418  if(!back_fib) {
1419  back_fib = &main_fib;
1420  emscripten_fiber_init_from_current_context(back_fib, main_asyncify_stack, MCO_ASYNCFY_STACK_SIZE);
1421  }
1422  running_fib = &context->fib;
1423  context->back_fib = back_fib;
1424  _mco_prepare_jumpin(co);
1425  emscripten_fiber_swap(back_fib, &context->fib); /* Do the context switch. */
1426 }
1427 
1428 static void _mco_jumpout(mco_coro* co) {
1429  _mco_context* context = (_mco_context*)co->context;
1430  running_fib = context->back_fib;
1431  _mco_prepare_jumpout(co);
1432  emscripten_fiber_swap(&context->fib, context->back_fib); /* Do the context switch. */
1433 }
1434 
1435 static mco_result _mco_create_context(mco_coro* co, mco_desc* desc) {
1436  if(emscripten_has_asyncify() != 1) {
1437  MCO_LOG("failed to create fiber because ASYNCIFY is not enabled");
1438  return MCO_MAKE_CONTEXT_ERROR;
1439  }
1440  /* Determine the context address. */
1441  size_t co_addr = (size_t)co;
1442  size_t context_addr = _mco_align_forward(co_addr + sizeof(mco_coro), 16);
1443  size_t storage_addr = _mco_align_forward(context_addr + sizeof(_mco_context), 16);
1444  size_t stack_addr = _mco_align_forward(storage_addr + desc->storage_size, 16);
1445  size_t asyncify_stack_addr = _mco_align_forward(stack_addr + desc->stack_size, 16);
1446  /* Initialize context. */
1447  _mco_context* context = (_mco_context*)context_addr;
1448  memset(context, 0, sizeof(_mco_context));
1449  /* Initialize storage. */
1450  unsigned char* storage = (unsigned char*)storage_addr;
1451  memset(storage, 0, desc->storage_size);
1452  /* Initialize stack. */
1453  void *stack_base = (void*)stack_addr;
1454  size_t stack_size = asyncify_stack_addr - stack_addr;
1455  void *asyncify_stack_base = (void*)asyncify_stack_addr;
1456  size_t asyncify_stack_size = co_addr + desc->coro_size - asyncify_stack_addr;
1457 #ifdef MCO_ZERO_MEMORY
1458  memset(stack_base, 0, stack_size);
1459  memset(asyncify_stack_base, 0, asyncify_stack_size);
1460 #endif
1461  /* Create the fiber. */
1462  emscripten_fiber_init(&context->fib, _mco_wrap_main, co, stack_base, stack_size, asyncify_stack_base, asyncify_stack_size);
1463  co->context = context;
1464  co->stack_base = stack_base;
1465  co->stack_size = stack_size;
1466  co->storage = storage;
1467  co->storage_size = desc->storage_size;
1468  return MCO_SUCCESS;
1469 }
1470 
1471 static void _mco_destroy_context(mco_coro* co) {
1472  /* Nothing to do. */
1473  _MCO_UNUSED(co);
1474 }
1475 
1476 static MCO_FORCE_INLINE void _mco_init_desc_sizes(mco_desc* desc, size_t stack_size) {
1477  desc->coro_size = _mco_align_forward(sizeof(mco_coro), 16) +
1478  _mco_align_forward(sizeof(_mco_context), 16) +
1479  _mco_align_forward(desc->storage_size, 16) +
1480  _mco_align_forward(stack_size, 16) +
1481  _mco_align_forward(MCO_ASYNCFY_STACK_SIZE, 16) +
1482  16;
1483  desc->stack_size = stack_size; /* This is just a hint, it won't be the real one. */
1484 }
1485 
1486 #else
1487 
1488 #error "Unsupported architecture for fibers method."
1489 
1490 #endif
1491 
1492 #endif /* MCO_USE_FIBERS */
1493 
1494 /* ---------------------------------------------------------------------------------------------- */
1495 
1496 #ifdef MCO_USE_ASYNCIFY
1497 
1498 typedef struct _asyncify_stack_region {
1499  void* start;
1500  void* limit;
1501 } _asyncify_stack_region;
1502 
1503 typedef struct _mco_context {
1504  int rewind_id;
1505  _asyncify_stack_region stack_region;
1506 } _mco_context;
1507 
1508 __attribute__((import_module("asyncify"), import_name("start_unwind"))) void _asyncify_start_unwind(void*);
1509 __attribute__((import_module("asyncify"), import_name("stop_unwind"))) void _asyncify_stop_unwind();
1510 __attribute__((import_module("asyncify"), import_name("start_rewind"))) void _asyncify_start_rewind(void*);
1511 __attribute__((import_module("asyncify"), import_name("stop_rewind"))) void _asyncify_stop_rewind();
1512 
1513 MCO_NO_INLINE void _mco_jumpin(mco_coro* co) {
1514  _mco_context* context = (_mco_context*)co->context;
1515  _mco_prepare_jumpin(co);
1516  if(context->rewind_id > 0) { /* Begin rewinding until last yield point. */
1517  _asyncify_start_rewind(&context->stack_region);
1518  }
1519  _mco_main(co); /* Run the coroutine function. */
1520  _asyncify_stop_unwind(); /* Stop saving coroutine stack. */
1521 }
1522 
1523 static MCO_NO_INLINE void _mco_finish_jumpout(mco_coro* co, volatile int rewind_id) {
1524  _mco_context* context = (_mco_context*)co->context;
1525  int next_rewind_id = context->rewind_id + 1;
1526  if(rewind_id == next_rewind_id) { /* Begins unwinding the stack (save locals and call stack to rewind later) */
1527  _mco_prepare_jumpout(co);
1528  context->rewind_id = next_rewind_id;
1529  _asyncify_start_unwind(&context->stack_region);
1530  } else if(rewind_id == context->rewind_id) { /* Continue from yield point. */
1531  _asyncify_stop_rewind();
1532  }
1533  /* Otherwise, we should be rewinding, let it continue... */
1534 }
1535 
1536 MCO_NO_INLINE void _mco_jumpout(mco_coro* co) {
1537  _mco_context* context = (_mco_context*)co->context;
1538  /*
1539  Save rewind point into a local, that should be restored when rewinding.
1540  That is "rewind_id != co->rewind_id + 1" may be true when rewinding.
1541  Use volatile here just to be safe from compiler optimizing this out.
1542  */
1543  volatile int rewind_id = context->rewind_id + 1;
1544  _mco_finish_jumpout(co, rewind_id);
1545 }
1546 
1547 static mco_result _mco_create_context(mco_coro* co, mco_desc* desc) {
1548  /* Determine the context address. */
1549  size_t co_addr = (size_t)co;
1550  size_t context_addr = _mco_align_forward(co_addr + sizeof(mco_coro), 16);
1551  size_t storage_addr = _mco_align_forward(context_addr + sizeof(_mco_context), 16);
1552  size_t stack_addr = _mco_align_forward(storage_addr + desc->storage_size, 16);
1553  /* Initialize context. */
1554  _mco_context* context = (_mco_context*)context_addr;
1555  memset(context, 0, sizeof(_mco_context));
1556  /* Initialize storage. */
1557  unsigned char* storage = (unsigned char*)storage_addr;
1558  memset(storage, 0, desc->storage_size);
1559  /* Initialize stack. */
1560  void *stack_base = (void*)stack_addr;
1561  size_t stack_size = desc->stack_size;
1562 #ifdef MCO_ZERO_MEMORY
1563  memset(stack_base, 0, stack_size);
1564 #endif
1565  context->stack_region.start = stack_base;
1566  context->stack_region.limit = (void*)((size_t)stack_base + stack_size);
1567  co->context = context;
1568  co->stack_base = stack_base;
1569  co->stack_size = stack_size;
1570  co->storage = storage;
1571  co->storage_size = desc->storage_size;
1572  return MCO_SUCCESS;
1573 }
1574 
1575 static void _mco_destroy_context(mco_coro* co) {
1576  /* Nothing to do. */
1577  _MCO_UNUSED(co);
1578 }
1579 
1580 static MCO_FORCE_INLINE void _mco_init_desc_sizes(mco_desc* desc, size_t stack_size) {
1581  desc->coro_size = _mco_align_forward(sizeof(mco_coro), 16) +
1582  _mco_align_forward(sizeof(_mco_context), 16) +
1583  _mco_align_forward(desc->storage_size, 16) +
1584  _mco_align_forward(stack_size, 16) +
1585  16;
1586  desc->stack_size = stack_size; /* This is just a hint, it won't be the real one. */
1587 }
1588 
1589 #endif /* MCO_USE_ASYNCIFY */
1590 
1591 /* ---------------------------------------------------------------------------------------------- */
1592 
1593 mco_desc mco_desc_init(void (*func)(mco_coro* co), size_t stack_size) {
1594  if(stack_size != 0) {
1595  /* Stack size should be at least `MCO_MIN_STACK_SIZE`. */
1596  if(stack_size < MCO_MIN_STACK_SIZE) {
1597  stack_size = MCO_MIN_STACK_SIZE;
1598  }
1599  } else {
1600  stack_size = MCO_DEFAULT_STACK_SIZE;
1601  }
1602  stack_size = _mco_align_forward(stack_size, 16); /* Stack size should be aligned to 16 bytes. */
1603  mco_desc desc;
1604  memset(&desc, 0, sizeof(mco_desc));
1605 #ifndef MCO_NO_DEFAULT_ALLOCATORS
1606  /* Set default allocators. */
1607  desc.malloc_cb = mco_malloc;
1608  desc.free_cb = mco_free;
1609 #endif
1610  desc.func = func;
1612  _mco_init_desc_sizes(&desc, stack_size);
1613  return desc;
1614 }
1615 
1616 static mco_result _mco_validate_desc(mco_desc* desc) {
1617  if(!desc) {
1618  MCO_LOG("coroutine description is NULL");
1619  return MCO_INVALID_ARGUMENTS;
1620  }
1621  if(!desc->func) {
1622  MCO_LOG("coroutine function in invalid");
1623  return MCO_INVALID_ARGUMENTS;
1624  }
1625  if(desc->stack_size < MCO_MIN_STACK_SIZE) {
1626  MCO_LOG("coroutine stack size is too small");
1627  return MCO_INVALID_ARGUMENTS;
1628  }
1629  if(desc->coro_size < sizeof(mco_coro)) {
1630  MCO_LOG("coroutine size is invalid");
1631  return MCO_INVALID_ARGUMENTS;
1632  }
1633  return MCO_SUCCESS;
1634 }
1635 
1636 mco_result mco_init(mco_coro* co, mco_desc* desc) {
1637  if(!co) {
1638  MCO_LOG("attempt to initialize an invalid coroutine");
1639  return MCO_INVALID_COROUTINE;
1640  }
1641  memset(co, 0, sizeof(mco_coro));
1642  /* Validate coroutine description. */
1643  mco_result res = _mco_validate_desc(desc);
1644  if(res != MCO_SUCCESS)
1645  return res;
1646  /* Create the coroutine. */
1647  res = _mco_create_context(co, desc);
1648  if(res != MCO_SUCCESS)
1649  return res;
1650  co->state = MCO_SUSPENDED; /* We initialize in suspended state. */
1651  co->free_cb = desc->free_cb;
1652  co->allocator_data = desc->allocator_data;
1653  co->func = desc->func;
1654  co->user_data = desc->user_data;
1655 #ifdef _MCO_USE_TSAN
1656  co->tsan_fiber = __tsan_create_fiber(0);
1657 #endif
1658  co->magic_number = MCO_MAGIC_NUMBER;
1659  return MCO_SUCCESS;
1660 }
1661 
1663  if(!co) {
1664  MCO_LOG("attempt to uninitialize an invalid coroutine");
1665  return MCO_INVALID_COROUTINE;
1666  }
1667  /* Cannot uninitialize while running. */
1668  if(!(co->state == MCO_SUSPENDED || co->state == MCO_DEAD)) {
1669  MCO_LOG("attempt to uninitialize a coroutine that is not dead or suspended");
1670  return MCO_INVALID_OPERATION;
1671  }
1672  /* The coroutine is now dead and cannot be used anymore. */
1673  co->state = MCO_DEAD;
1674 #ifdef _MCO_USE_TSAN
1675  if(co->tsan_fiber != NULL) {
1676  __tsan_destroy_fiber(co->tsan_fiber);
1677  co->tsan_fiber = NULL;
1678  }
1679 #endif
1680  _mco_destroy_context(co);
1681  return MCO_SUCCESS;
1682 }
1683 
1684 mco_result mco_create(mco_coro** out_co, mco_desc* desc) {
1685  /* Validate input. */
1686  if(!out_co) {
1687  MCO_LOG("coroutine output pointer is NULL");
1688  return MCO_INVALID_POINTER;
1689  }
1690  if(!desc || !desc->malloc_cb || !desc->free_cb) {
1691  *out_co = NULL;
1692  MCO_LOG("coroutine allocator description is not set");
1693  return MCO_INVALID_ARGUMENTS;
1694  }
1695  /* Allocate the coroutine. */
1696  mco_coro* co = (mco_coro*)desc->malloc_cb(desc->coro_size, desc->allocator_data);
1697  if(!co) {
1698  MCO_LOG("coroutine allocation failed");
1699  *out_co = NULL;
1700  return MCO_OUT_OF_MEMORY;
1701  }
1702  /* Initialize the coroutine. */
1703  mco_result res = mco_init(co, desc);
1704  if(res != MCO_SUCCESS) {
1705  desc->free_cb(co, desc->allocator_data);
1706  *out_co = NULL;
1707  return res;
1708  }
1709  *out_co = co;
1710  return MCO_SUCCESS;
1711 }
1712 
1714  if(!co) {
1715  MCO_LOG("attempt to destroy an invalid coroutine");
1716  return MCO_INVALID_COROUTINE;
1717  }
1718  /* Uninitialize the coroutine first. */
1719  mco_result res = mco_uninit(co);
1720  if(res != MCO_SUCCESS)
1721  return res;
1722  /* Free the coroutine. */
1723  if(!co->free_cb) {
1724  MCO_LOG("attempt destroy a coroutine that has no free callback");
1725  return MCO_INVALID_POINTER;
1726  }
1727  co->free_cb(co, co->allocator_data);
1728  return MCO_SUCCESS;
1729 }
1730 
1732  if(!co) {
1733  MCO_LOG("attempt to resume an invalid coroutine");
1734  return MCO_INVALID_COROUTINE;
1735  }
1736  if(co->state != MCO_SUSPENDED) { /* Can only resume coroutines that are suspended. */
1737  MCO_LOG("attempt to resume a coroutine that is not suspended");
1738  return MCO_NOT_SUSPENDED;
1739  }
1740  co->state = MCO_RUNNING; /* The coroutine is now running. */
1741  _mco_jumpin(co);
1742  return MCO_SUCCESS;
1743 }
1744 
1746  if(!co) {
1747  MCO_LOG("attempt to yield an invalid coroutine");
1748  return MCO_INVALID_COROUTINE;
1749  }
1750 #ifdef MCO_USE_ASYNCIFY
1751  /* Asyncify already checks for stack overflow. */
1752 #else
1753  /* This check happens when the stack overflow already happened, but better later than never. */
1754  volatile size_t dummy;
1755  size_t stack_addr = (size_t)&dummy;
1756  size_t stack_min = (size_t)co->stack_base;
1757  size_t stack_max = stack_min + co->stack_size;
1758  if(co->magic_number != MCO_MAGIC_NUMBER || stack_addr < stack_min || stack_addr > stack_max) { /* Stack overflow. */
1759  MCO_LOG("coroutine stack overflow, try increasing the stack size");
1760  return MCO_STACK_OVERFLOW;
1761  }
1762 #endif
1763  if(co->state != MCO_RUNNING) { /* Can only yield coroutines that are running. */
1764  MCO_LOG("attempt to yield a coroutine that is not running");
1765  return MCO_NOT_RUNNING;
1766  }
1767  co->state = MCO_SUSPENDED; /* The coroutine is now suspended. */
1768  _mco_jumpout(co);
1769  return MCO_SUCCESS;
1770 }
1771 
1773  if(co != NULL) {
1774  return co->state;
1775  }
1776  return MCO_DEAD;
1777 }
1778 
1779 void* mco_get_user_data(mco_coro* co) {
1780  if(co != NULL) {
1781  return co->user_data;
1782  }
1783  return NULL;
1784 }
1785 
1786 mco_result mco_push(mco_coro* co, const void* src, size_t len) {
1787  if(!co) {
1788  MCO_LOG("attempt to use an invalid coroutine");
1789  return MCO_INVALID_COROUTINE;
1790  } else if(len > 0) {
1791  size_t bytes_stored = co->bytes_stored + len;
1792  if(bytes_stored > co->storage_size) {
1793  MCO_LOG("attempt to push too many bytes into coroutine storage");
1794  return MCO_NOT_ENOUGH_SPACE;
1795  }
1796  if(!src) {
1797  MCO_LOG("attempt push a null pointer into coroutine storage");
1798  return MCO_INVALID_POINTER;
1799  }
1800  memcpy(&co->storage[co->bytes_stored], src, len);
1801  co->bytes_stored = bytes_stored;
1802  }
1803  return MCO_SUCCESS;
1804 }
1805 
1806 mco_result mco_pop(mco_coro* co, void* dest, size_t len) {
1807  if(!co) {
1808  MCO_LOG("attempt to use an invalid coroutine");
1809  return MCO_INVALID_COROUTINE;
1810  } else if(len > 0) {
1811  if(len > co->bytes_stored) {
1812  MCO_LOG("attempt to pop too many bytes from coroutine storage");
1813  return MCO_NOT_ENOUGH_SPACE;
1814  }
1815  size_t bytes_stored = co->bytes_stored - len;
1816  if(dest) {
1817  memcpy(dest, &co->storage[bytes_stored], len);
1818  }
1819  co->bytes_stored = bytes_stored;
1820 #ifdef MCO_ZERO_MEMORY
1821  /* Clear garbage in the discarded storage. */
1822  memset(&co->storage[bytes_stored], 0, len);
1823 #endif
1824  }
1825  return MCO_SUCCESS;
1826 }
1827 
1828 mco_result mco_peek(mco_coro* co, void* dest, size_t len) {
1829  if(!co) {
1830  MCO_LOG("attempt to use an invalid coroutine");
1831  return MCO_INVALID_COROUTINE;
1832  } else if(len > 0) {
1833  if(len > co->bytes_stored) {
1834  MCO_LOG("attempt to peek too many bytes from coroutine storage");
1835  return MCO_NOT_ENOUGH_SPACE;
1836  }
1837  if(!dest) {
1838  MCO_LOG("attempt peek into a null pointer");
1839  return MCO_INVALID_POINTER;
1840  }
1841  memcpy(dest, &co->storage[co->bytes_stored - len], len);
1842  }
1843  return MCO_SUCCESS;
1844 }
1845 
1846 size_t mco_get_bytes_stored(mco_coro* co) {
1847  if(co == NULL) {
1848  return 0;
1849  }
1850  return co->bytes_stored;
1851 }
1852 
1853 size_t mco_get_storage_size(mco_coro* co) {
1854  if(co == NULL) {
1855  return 0;
1856  }
1857  return co->storage_size;
1858 }
1859 
1860 #ifdef MCO_NO_MULTITHREAD
1861 mco_coro* mco_running(void) {
1862  return mco_current_co;
1863 }
1864 #else
1865 static MCO_NO_INLINE mco_coro* _mco_running(void) {
1866  return mco_current_co;
1867 }
1868 mco_coro* mco_running(void) {
1869  /*
1870  Compilers aggressively optimize the use of TLS by caching loads.
1871  Since fiber code can migrate between threads it’s possible for the load to be stale.
1872  To prevent this from happening we avoid inline functions.
1873  */
1874  mco_coro* (*volatile func)(void) = _mco_running;
1875  return func();
1876 }
1877 #endif
1878 
1879 const char* mco_result_description(mco_result res) {
1880  switch(res) {
1881  case MCO_SUCCESS:
1882  return "No error";
1883  case MCO_GENERIC_ERROR:
1884  return "Generic error";
1885  case MCO_INVALID_POINTER:
1886  return "Invalid pointer";
1887  case MCO_INVALID_COROUTINE:
1888  return "Invalid coroutine";
1889  case MCO_NOT_SUSPENDED:
1890  return "Coroutine not suspended";
1891  case MCO_NOT_RUNNING:
1892  return "Coroutine not running";
1894  return "Make context error";
1896  return "Switch context error";
1897  case MCO_NOT_ENOUGH_SPACE:
1898  return "Not enough space";
1899  case MCO_OUT_OF_MEMORY:
1900  return "Out of memory";
1901  case MCO_INVALID_ARGUMENTS:
1902  return "Invalid arguments";
1903  case MCO_INVALID_OPERATION:
1904  return "Invalid operation";
1905  case MCO_STACK_OVERFLOW:
1906  return "Stack overflow";
1907  }
1908  return "Unknown error";
1909 }
1910 
1911 #ifdef __cplusplus
1912 }
1913 #endif
1914 
1915 #endif /* MINICORO_IMPL */
1916 
1917 /*
1918 This software is available as a choice of the following licenses. Choose
1919 whichever you prefer.
1920 
1921 ===============================================================================
1922 ALTERNATIVE 1 - Public Domain (www.unlicense.org)
1923 ===============================================================================
1924 This is free and unencumbered software released into the public domain.
1925 
1926 Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
1927 software, either in source code form or as a compiled binary, for any purpose,
1928 commercial or non-commercial, and by any means.
1929 
1930 In jurisdictions that recognize copyright laws, the author or authors of this
1931 software dedicate any and all copyright interest in the software to the public
1932 domain. We make this dedication for the benefit of the public at large and to
1933 the detriment of our heirs and successors. We intend this dedication to be an
1934 overt act of relinquishment in perpetuity of all present and future rights to
1935 this software under copyright law.
1936 
1937 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1938 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1939 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
1940 AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
1941 ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
1942 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
1943 
1944 For more information, please refer to <http://unlicense.org/>
1945 
1946 ===============================================================================
1947 ALTERNATIVE 2 - MIT No Attribution
1948 ===============================================================================
1949 Copyright (c) 2021-2022 Eduardo Bart (https://github.com/edubart/minicoro)
1950 
1951 Permission is hereby granted, free of charge, to any person obtaining a copy of
1952 this software and associated documentation files (the "Software"), to deal in
1953 the Software without restriction, including without limitation the rights to
1954 use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
1955 of the Software, and to permit persons to whom the Software is furnished to do
1956 so.
1957 
1958 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1959 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1960 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
1961 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
1962 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
1963 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
1964 SOFTWARE.
1965 */
cx::size
constexpr auto size(const C &c) -> decltype(c.size())
Definition: wildcards.hpp:636
mco_coro::tsan_prev_fiber
void * tsan_prev_fiber
Definition: minicoro.h:260
mco_coro::context
void * context
Definition: minicoro.h:247
mco_yield
MCO_API mco_result mco_yield(mco_coro *co)
mco_desc::storage_size
size_t storage_size
Definition: minicoro.h:273
mco_coro::storage_size
size_t storage_size
Definition: minicoro.h:258
mco_coro::stack_size
size_t stack_size
Definition: minicoro.h:255
mco_desc
Definition: minicoro.h:266
MCO_INVALID_ARGUMENTS
@ MCO_INVALID_ARGUMENTS
Definition: minicoro.h:239
mco_state
mco_state
Definition: minicoro.h:220
MCO_RUNNING
@ MCO_RUNNING
Definition: minicoro.h:223
mco_desc::malloc_cb
void *(* malloc_cb)(size_t size, void *allocator_data)
Definition: minicoro.h:270
mco_result
mco_result
Definition: minicoro.h:228
mco_coro::user_data
void * user_data
Definition: minicoro.h:251
mco_status
MCO_API mco_state mco_status(mco_coro *co)
MCO_STACK_OVERFLOW
@ MCO_STACK_OVERFLOW
Definition: minicoro.h:241
mco_create
MCO_API mco_result mco_create(mco_coro **out_co, mco_desc *desc)
MCO_API
#define MCO_API
Definition: minicoro.h:207
mco_resume
MCO_API mco_result mco_resume(mco_coro *co)
mco_get_storage_size
MCO_API size_t mco_get_storage_size(mco_coro *co)
mco_coro::state
mco_state state
Definition: minicoro.h:248
MCO_SUCCESS
@ MCO_SUCCESS
Definition: minicoro.h:229
mco_coro
Definition: minicoro.h:246
MCO_INVALID_COROUTINE
@ MCO_INVALID_COROUTINE
Definition: minicoro.h:232
MCO_MAKE_CONTEXT_ERROR
@ MCO_MAKE_CONTEXT_ERROR
Definition: minicoro.h:235
MCO_NOT_ENOUGH_SPACE
@ MCO_NOT_ENOUGH_SPACE
Definition: minicoro.h:237
MCO_DEAD
@ MCO_DEAD
Definition: minicoro.h:221
MCO_INVALID_OPERATION
@ MCO_INVALID_OPERATION
Definition: minicoro.h:240
detail::void
j template void())
Definition: json.hpp:4893
mco_desc_init
MCO_API mco_desc mco_desc_init(void(*func)(mco_coro *co), size_t stack_size)
mco_coro::storage
unsigned char * storage
Definition: minicoro.h:256
mco_desc::stack_size
size_t stack_size
Definition: minicoro.h:276
MCO_GENERIC_ERROR
@ MCO_GENERIC_ERROR
Definition: minicoro.h:230
mco_running
MCO_API mco_coro * mco_running(void)
MCO_INVALID_POINTER
@ MCO_INVALID_POINTER
Definition: minicoro.h:231
MCO_SWITCH_CONTEXT_ERROR
@ MCO_SWITCH_CONTEXT_ERROR
Definition: minicoro.h:236
MCO_DEFAULT_STORAGE_SIZE
#define MCO_DEFAULT_STORAGE_SIZE
Definition: minicoro.h:212
mco_coro::free_cb
void(* free_cb)(void *ptr, void *allocator_data)
Definition: minicoro.h:253
mco_coro::magic_number
size_t magic_number
Definition: minicoro.h:262
mco_coro::bytes_stored
size_t bytes_stored
Definition: minicoro.h:257
mco_desc::allocator_data
void * allocator_data
Definition: minicoro.h:272
mco_push
MCO_API mco_result mco_push(mco_coro *co, const void *src, size_t len)
f
static FILE * f
Definition: minitrace.cpp:74
MCO_NOT_RUNNING
@ MCO_NOT_RUNNING
Definition: minicoro.h:234
mco_coro::stack_base
void * stack_base
Definition: minicoro.h:254
mco_desc::coro_size
size_t coro_size
Definition: minicoro.h:275
mco_desc::user_data
void * user_data
Definition: minicoro.h:268
mco_coro::prev_co
mco_coro * prev_co
Definition: minicoro.h:250
mco_coro::allocator_data
void * allocator_data
Definition: minicoro.h:252
mco_init
MCO_API mco_result mco_init(mco_coro *co, mco_desc *desc)
mco_coro::func
void(* func)(mco_coro *co)
Definition: minicoro.h:249
mco_destroy
MCO_API mco_result mco_destroy(mco_coro *co)
MCO_NORMAL
@ MCO_NORMAL
Definition: minicoro.h:222
lexy::_detail::error
constexpr bool error
Definition: config.hpp:39
mco_desc
struct mco_desc mco_desc
mco_result_description
const MCO_API char * mco_result_description(mco_result res)
lexyd::flags
constexpr auto flags(_sym< Table, Token, Tag > flag_rule)
Definition: flags.hpp:83
mco_get_user_data
MCO_API void * mco_get_user_data(mco_coro *co)
mco_coro::tsan_fiber
void * tsan_fiber
Definition: minicoro.h:261
MCO_SUSPENDED
@ MCO_SUSPENDED
Definition: minicoro.h:224
mco_peek
MCO_API mco_result mco_peek(mco_coro *co, void *dest, size_t len)
MCO_OUT_OF_MEMORY
@ MCO_OUT_OF_MEMORY
Definition: minicoro.h:238
mco_pop
MCO_API mco_result mco_pop(mco_coro *co, void *dest, size_t len)
mco_uninit
MCO_API mco_result mco_uninit(mco_coro *co)
MCO_NOT_SUSPENDED
@ MCO_NOT_SUSPENDED
Definition: minicoro.h:233
mco_desc::func
void(* func)(mco_coro *co)
Definition: minicoro.h:267
mco_get_bytes_stored
MCO_API size_t mco_get_bytes_stored(mco_coro *co)
mco_coro::asan_prev_stack
void * asan_prev_stack
Definition: minicoro.h:259
mco_desc::free_cb
void(* free_cb)(void *ptr, void *allocator_data)
Definition: minicoro.h:271


behaviortree_cpp_v4
Author(s): Davide Faconti
autogenerated on Fri Jun 28 2024 02:20:07