Zth (libzth)
Loading...
Searching...
No Matches
ucontext.h
Go to the documentation of this file.
1#ifndef ZTH_CONTEXT_UCONTEXT_H
2#define ZTH_CONTEXT_UCONTEXT_H
3/*
4 * SPDX-FileCopyrightText: 2019-2026 Jochem Rutgers
5 *
6 * SPDX-License-Identifier: MPL-2.0
7 */
8
9#ifndef ZTH_CONTEXT_CONTEXT_H
10# error This file must be included by libzth/context/context.h.
11#endif
12
13#ifdef __cplusplus
14
15# ifdef ZTH_OS_MAC
16// For ucontext_t
17# ifndef _XOPEN_SOURCE
18# error Please define _XOPEN_SOURCE before including headers.
19# endif
20# include <sched.h>
21# endif
22
23# include <csetjmp>
24# include <csignal>
25# include <ucontext.h>
26
27namespace zth {
28
29# pragma GCC diagnostic push
30# pragma GCC diagnostic ignored "-Wdeprecated-declarations" // I know...
31class Context : public impl::ContextArch<Context> {
33public:
35
36 constexpr explicit Context(ContextAttr const& attr) noexcept
37 : base(attr)
38 , m_env()
39 {}
40
41private:
42 // It seems that in Apple's aarch64, makecontext() does not pass 64-bit pointer arguments.
43 // Wrap the arguments in a struct, and pass it via separate low/high 32-bit args.
44 struct context_trampoline_args {
45 sigjmp_buf origin;
46 Context* context;
47 };
48
49 static void context_trampoline(uint32_t args_high, uint32_t args_low) noexcept
50 {
51# ifdef ZTH_ENABLE_ASAN
52 void const* oldstack = nullptr;
53 size_t oldsize = 0;
54 __sanitizer_finish_switch_fiber(nullptr, &oldstack, &oldsize);
55
56 Stack& workerStack = currentWorker().workerStack();
57 if(unlikely(!workerStack))
58 workerStack = Stack((void*)oldstack, oldsize);
59# endif
60
61 context_trampoline_args* args = nullptr;
62 if(sizeof(void*) == 4) {
63 // NOLINTNEXTLINE
64 args = reinterpret_cast<context_trampoline_args*>(
65 (static_cast<uintptr_t>(args_low)));
66 } else {
67 // NOLINTNEXTLINE
68 args = reinterpret_cast<context_trampoline_args*>(
69 ((static_cast<uintptr_t>(args_high) << 32)
70 | (static_cast<uintptr_t>(args_low))));
71 }
72
73 // Copy argument to local stack.
74 Context* context = args->context;
75
76 // We got here via setcontext().
77
78# ifdef ZTH_ENABLE_ASAN
79 // We are jumping back.
80 void* fake_stack = nullptr;
81 __sanitizer_start_switch_fiber(&fake_stack, oldstack, oldsize);
82# endif
83
84 // Save the current context, and return to create().
85 if(sigsetjmp(context->m_env, Config::ContextSignals) == 0)
86 siglongjmp(args->origin, 1);
87
88# ifdef ZTH_ENABLE_ASAN
89 __sanitizer_finish_switch_fiber(fake_stack, nullptr, nullptr);
90# endif
91
92 // args is no longer valid here.
93 context_entry(context);
94 }
95
96public:
97 // cppcheck-suppress duplInheritedMember
98 int create() noexcept
99 {
100 int res = base::create();
101 if(unlikely(res))
102 return res;
103
104 if(unlikely(!stack()))
105 // Stackless fiber only saves current context; nothing to do.
106 return 0;
107
108 // Get current context, to inherit signals/masks.
109 ucontext_t uc;
110 if(getcontext(&uc))
111 return EINVAL;
112
113 // Modify the stack of the new context.
114 uc.uc_link = nullptr;
115 Stack const& stack_ = stackUsable();
116 uc.uc_stack.ss_sp = stack_.p;
117 uc.uc_stack.ss_size = stack_.size;
118
119 // Modify the function to call from this new context.
120 context_trampoline_args args = {};
121 args.context = this;
122
123 makecontext(
124 &uc, reinterpret_cast<void (*)(void)>(&context_trampoline), 2,
125 static_cast<uint32_t>((reinterpret_cast<uintptr_t>(&args)) >> 32),
126 static_cast<uint32_t>(reinterpret_cast<uintptr_t>(&args)));
127
128# ifdef ZTH_ENABLE_ASAN
129 void* fake_stack = nullptr;
130 __sanitizer_start_switch_fiber(&fake_stack, stack_.p, stack_.size);
131# endif
132
133 // switchcontext() is slow, we want to use sigsetjmp/siglongjmp instead.
134 // So, we initialize the sigjmp_buf from the just created context.
135 // After this initial setup, context_switch() is good to go.
136 if(sigsetjmp(args.origin, Config::ContextSignals) == 0) {
137 // Here we go into the context for the first time.
138 setcontext(&uc);
139 }
140
141 // Got back from context_trampoline(). The context is ready now.
142
143# ifdef ZTH_ENABLE_ASAN
144 __sanitizer_finish_switch_fiber(fake_stack, nullptr, nullptr);
145# endif
146 return 0;
147 }
148
149 // cppcheck-suppress duplInheritedMember
150 void context_switch(Context& to) noexcept
151 {
152# ifdef ZTH_ENABLE_ASAN
153 zth_assert(to.alive());
154 void* fake_stack = nullptr;
155
156 Stack const* stack = &to.stackUsable();
157 if(unlikely(!*stack))
159
160 __sanitizer_start_switch_fiber(
161 alive() ? &fake_stack : nullptr, stack->p, stack->size);
162# endif
163
164 // switchcontext() restores signal masks, which is slow...
165 if(sigsetjmp(m_env, Config::ContextSignals) == 0)
166 siglongjmp(to.m_env, 1);
167
168# ifdef ZTH_ENABLE_ASAN
169 __sanitizer_finish_switch_fiber(fake_stack, nullptr, nullptr);
170# endif
171 }
172
173private:
174 sigjmp_buf m_env;
175};
176# pragma GCC diagnostic pop
177
178} // namespace zth
179#endif // __cplusplus
180#endif // ZTH_CONTEXT_UCONTEXT_H
int create() noexcept
Definition ucontext.h:98
impl::ContextArch< Context > base
Definition ucontext.h:34
void context_switch(Context &to) noexcept
Definition ucontext.h:150
sigjmp_buf m_env
constexpr Context(ContextAttr const &attr) noexcept
Definition ucontext.h:36
Stack & workerStack() noexcept
Definition worker.h:382
Stack const & stack() const noexcept
Return the stack address.
Definition context.h:226
int create() noexcept
Create context.
Definition context.h:136
ContextAttr & attr() noexcept
Return the context attributes, requested by the user.
Definition context.h:120
Stack const & stackUsable() const noexcept
Return the start of the actual usable stack.
Definition context.h:234
bool alive() const noexcept
Check if fiber is still running.
Definition context.h:497
Worker & currentWorker() noexcept
Return the (thread-local) singleton Worker instance.
Definition worker.h:407
#define ZTH_CLASS_NEW_DELETE(T)
Define new/delete operators for a class, which are allocator-aware.
Definition allocator.h:159
void context_entry(Context *context)
static bool const ContextSignals
Take POSIX signal into account when doing a context switch.
Definition config.h:146
Stack information.
Definition context.h:89
char * p
Definition context.h:105
size_t size
Definition context.h:106
#define zth_assert(expr)
assert(), but better integrated in Zth.
Definition util.h:217
#define unlikely(expr)
Marks the given expression to likely be evaluated to true.
Definition util.h:60