Ruby 4.1.0dev (2026-09-09 revision 213b1add1ff300be0e375b8c792c9b0eb9f53981)
Context.h
1#ifndef COROUTINE_AMD64_CONTEXT_H
2#define COROUTINE_AMD64_CONTEXT_H 1
3
4/*
5 * This file is part of the "Coroutine" project and released under the MIT License.
6 *
7 * Created by Samuel Williams on 10/5/2018.
8 * Copyright, 2018, by Samuel Williams.
9*/
10
11#pragma once
12
13#include <assert.h>
14#include <stddef.h>
15#include <stdint.h>
16#include <string.h>
17
18#define COROUTINE __attribute__((noreturn)) void
19
20enum {COROUTINE_REGISTERS = 6};
21
22#if defined(__SANITIZE_ADDRESS__)
23 #define COROUTINE_SANITIZE_ADDRESS
24#elif defined(__has_feature)
25 #if __has_feature(address_sanitizer)
26 #define COROUTINE_SANITIZE_ADDRESS
27 #endif
28#endif
29
30#if defined(COROUTINE_SANITIZE_ADDRESS)
31#include <sanitizer/common_interface_defs.h>
32#include <sanitizer/asan_interface.h>
33#endif
34
35#if defined(__SANITIZE_THREAD__)
36 #define COROUTINE_SANITIZE_THREAD
37#elif defined(__has_feature)
38 #if __has_feature(thread_sanitizer)
39 #define COROUTINE_SANITIZE_THREAD
40 #endif
41#endif
42
43#if defined(COROUTINE_SANITIZE_THREAD)
44/* ThreadSanitizer cannot follow a userspace stack switch on its own: its
45 * per-OS-thread shadow stack must be handed to the destination fiber on every
46 * coroutine switch via the fiber API, otherwise it leaks the shadow stack
47 * across switches and eventually faults inside libtsan. */
48#include <sanitizer/tsan_interface.h>
49#endif
50
52{
53 void **stack_pointer;
54 void *argument;
55
56#if defined(COROUTINE_SANITIZE_ADDRESS)
57 void *fake_stack;
58 void *stack_base;
59 size_t stack_size;
60#endif
61
62#if defined(COROUTINE_SANITIZE_THREAD)
63 void *tsan_fiber;
64 /* Whether we created tsan_fiber (via __tsan_create_fiber, must be
65 * destroyed) or borrowed it from __tsan_get_current_fiber (the OS thread's
66 * implicit fiber, owned by TSan; must not be destroyed). */
67 int tsan_fiber_owned;
68#endif
69};
70
71typedef COROUTINE(* coroutine_start)(struct coroutine_context *from, struct coroutine_context *self);
72
73static inline void coroutine_initialize_main(struct coroutine_context * context) {
74 context->stack_pointer = NULL;
75
76#if defined(COROUTINE_SANITIZE_THREAD)
77 /* The OS thread's implicit (already running) fiber, owned by TSan. */
78 context->tsan_fiber = __tsan_get_current_fiber();
79 context->tsan_fiber_owned = 0;
80#endif
81}
82
83static inline void coroutine_initialize(
84 struct coroutine_context *context,
85 coroutine_start start,
86 void *stack,
87 size_t size
88) {
89 assert(start && stack && size >= 1024);
90
91#if defined(COROUTINE_SANITIZE_ADDRESS)
92 context->fake_stack = NULL;
93 context->stack_base = stack;
94 context->stack_size = size;
95#endif
96
97#if defined(COROUTINE_SANITIZE_THREAD)
98 context->tsan_fiber = __tsan_create_fiber(0);
99 context->tsan_fiber_owned = 1;
100#endif
101
102 // Stack grows down. Force 16-byte alignment.
103 char * top = (char*)stack + size;
104 context->stack_pointer = (void**)((uintptr_t)top & ~0xF);
105
106 *--context->stack_pointer = NULL;
107 *--context->stack_pointer = (void*)(uintptr_t)start;
108
109 context->stack_pointer -= COROUTINE_REGISTERS;
110 memset(context->stack_pointer, 0, sizeof(void*) * COROUTINE_REGISTERS);
111}
112
113struct coroutine_context * coroutine_transfer(struct coroutine_context * current, struct coroutine_context * target);
114
115static inline void coroutine_destroy(struct coroutine_context * context)
116{
117 context->stack_pointer = NULL;
118
119#if defined(COROUTINE_SANITIZE_THREAD)
120 /* Only destroy fibers we created. The borrowed __tsan_get_current_fiber()
121 * handle (the OS thread's implicit fiber) is owned by TSan; destroying it
122 * aborts libtsan (FiberDestroy -> ProcWire CheckFailed). */
123 if (context->tsan_fiber && context->tsan_fiber_owned) {
124 __tsan_destroy_fiber(context->tsan_fiber);
125 context->tsan_fiber = NULL;
126 context->tsan_fiber_owned = 0;
127 }
128#endif
129}
130
131#endif /* COROUTINE_AMD64_CONTEXT_H */