Zth (libzth)
Loading...
Searching...
No Matches
context.cpp
Go to the documentation of this file.
1/*
2 * SPDX-FileCopyrightText: 2019-2026 Jochem Rutgers
3 *
4 * SPDX-License-Identifier: MPL-2.0
5 */
6
7#include <libzth/macros.h>
8
9#ifdef ZTH_OS_MAC
10# define _XOPEN_SOURCE
11#endif
12
13#include <libzth/context.h>
15#include <libzth/worker.h>
16
17
18
20// context functions
21
22namespace zth {
23
29int context_init() noexcept
30{
31 zth_dbg(context, "[%s] Initialize", currentWorker().id_str());
32 return Context::init();
33}
34
40void context_deinit() noexcept
41{
42 zth_dbg(context, "[%s] Deinit", currentWorker().id_str());
43 Context::deinit();
44}
45
55int context_create(Context*& context, ContextAttr const& attr) noexcept
56{
57 try {
58 context = new Context(attr);
59 } catch(std::bad_alloc const&) {
60 zth_dbg(context, "[%s] Cannot create context; %s", currentWorker().id_str(),
61 err(ENOMEM).c_str());
62 return ENOMEM;
63 } catch(...) {
64 // Should not be thrown by new...
65 return EAGAIN;
66 }
67
68 int res = context->create();
69 if(res) {
70 delete context;
71 context = nullptr;
72 zth_dbg(context, "[%s] Cannot create context; %s", currentWorker().id_str(),
73 err(res).c_str());
74 return res;
75 }
76
77 if(likely(attr.stackSize > 0)) {
78#ifdef ZTH_CONTEXT_WINFIBER
79 zth_dbg(context, "[%s] New context %p", currentWorker().id_str(), context);
80#else
81 Context::Stack const& stack = context->stackUsable();
82 zth_dbg(context, "[%s] New context %p with stack: %p-%p", currentWorker().id_str(),
83 context, stack.p, stack.p + stack.size - 1U);
84#endif
85 }
86
87 return 0;
88}
89
95void context_destroy(Context* context) noexcept
96{
97 if(!context)
98 return;
99
100 context->destroy();
101 delete context;
102 // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDelete)
103 zth_dbg(context, "[%s] Deleted context %p", currentWorker().id_str(), context);
104}
105
112void context_switch(Context* from, Context* to) noexcept
113{
114 zth_assert(from);
115 zth_assert(to);
116
117#ifdef ZTH_ENABLE_ASAN
118 zth_assert(to->alive());
119 void* fake_stack = nullptr;
120 __sanitizer_start_switch_fiber(
121 from->alive() ? &fake_stack : nullptr, to->stackUsable().p, to->stackUsable().size);
122#endif
123
124 // cppcheck-suppress nullPointerRedundantCheck
125 from->context_switch(*to);
126
127#ifdef ZTH_ENABLE_ASAN
128 __sanitizer_finish_switch_fiber(fake_stack, nullptr, nullptr);
129#endif
130
131 // Got back from somewhere else.
132 from->stackGuard();
133}
134
139size_t context_stack_usage(Context* context) noexcept
140{
141 if(!Config::EnableStackWaterMark || !context)
142 return 0;
143
145 void* guard = nullptr;
146
147 if(f && f->context() == context) {
148 // Guards may be in place on the current stack. Disable it
149 // while iterating it.
150 guard = context->stackGuard(nullptr);
151 }
152
153 size_t res = stack_watermark_maxused(context->stackUsable().p);
154
155 // cppcheck-suppress knownConditionTrueFalse
156 if(guard)
157 context->stackGuard(guard);
158
159 return res;
160}
161
162} // namespace zth
163
175void context_entry(zth::Context* context) noexcept
176{
177#ifdef ZTH_ENABLE_ASAN
178 __sanitizer_finish_switch_fiber(nullptr, nullptr, nullptr);
179#endif
180
181 zth_assert(context);
182
183 // Go execute the fiber.
184 // cppcheck-suppress nullPointerRedundantCheck
185 context->stackGuard();
186
187 try {
188 // cppcheck-suppress nullPointerRedundantCheck
189 context->attr().entry(context->attr().arg);
190 } catch(...) {
191 zth_dbg(context, "[%s] Uncaught exception in fiber", zth::currentWorker().id_str());
192 }
193
194 // Fiber has quit. Switch to another one.
195 context->die();
196 zth::yield();
197
198 // We should never get here, otherwise the fiber wasn't dead...
199 zth_abort("Returned to finished context");
200}
201
202
203
205// Stack functions
206
207namespace zth {
208
209#define ZTH_STACK_WATERMARKER ((uint64_t)0x5a7468574de2889eULL) // Some nice UTF-8 text.
210
218static void stack_watermark_align(void*& stack, size_t*& sizeptr, size_t* size = nullptr) noexcept
219{
220 if(!stack)
221 return;
222
223 uintptr_t stack_ = reinterpret_cast<uintptr_t>(stack);
224
225 // Align to size_t
226 stack_ = (stack_ + sizeof(size_t) - 1U) & ~(uintptr_t)(sizeof(size_t) - 1U);
227 // The stack size is stored here.
228 sizeptr = reinterpret_cast<size_t*>(stack_); // NOLINT
229
230 // Reduce stack to store the size.
231 stack_ += sizeof(size_t);
232 if(size)
233 // Reduce remaining stack size.
234 *size -= stack_ - (uintptr_t)stack; // NOLINT
235
236 stack = reinterpret_cast<void*>(stack_); // NOLINT
237}
238
243void stack_watermark_init(void* stack, size_t size) noexcept
244{
245 if(!Config::EnableStackWaterMark || !stack || !size)
246 return;
247
248 // Most likely, but growing up stack is not implemented here (yet?)
249 zth_assert(Context::stackGrowsDown(&size));
250
251 size_t* sizeptr = nullptr;
252 stack_watermark_align(stack, sizeptr, &size);
253 *sizeptr = size;
254
255 for(size_t i = 0; i * sizeof(uint64_t) <= size - sizeof(uint64_t); i++)
256 static_cast<uint64_t*>(stack)[i] = ZTH_STACK_WATERMARKER;
257}
258
265size_t stack_watermark_size(void* stack) noexcept
266{
267 if(!Config::EnableStackWaterMark || !stack)
268 return 0;
269
270 size_t* sizeptr = nullptr;
271 stack_watermark_align(stack, sizeptr);
272 return *sizeptr;
273}
274
281size_t stack_watermark_maxused(void* stack) noexcept
282{
283 if(!Config::EnableStackWaterMark || !stack)
284 return 0;
285
286 size_t* sizeptr = nullptr;
287 stack_watermark_align(stack, sizeptr);
288 size_t size = *sizeptr;
289
290 size_t unused = 0;
291
292#ifdef ZTH_USE_VALGRIND
293 VALGRIND_DISABLE_ADDR_ERROR_REPORTING_IN_RANGE(stack, size); // NOLINT
294#endif
295
296 for(uint64_t* s = static_cast<uint64_t*>(stack);
297 unused < size / sizeof(uint64_t) && *s == ZTH_STACK_WATERMARKER; unused++, s++)
298 ;
299
300#ifdef ZTH_USE_VALGRIND
301 VALGRIND_ENABLE_ADDR_ERROR_REPORTING_IN_RANGE(stack, size); // NOLINT
302#endif
303
304 return size - unused * sizeof(uint64_t);
305}
306
313size_t stack_watermark_remaining(void* stack) noexcept
314{
315 return stack_watermark_size(stack) - stack_watermark_maxused(stack);
316}
317
318} // namespace zth
319
320#ifdef ZTH_STACK_SWITCH
321void* zth_stack_switch(void* stack, size_t size, void* (*f)(void*) noexcept, void* arg) noexcept
322{
324
325 if(!worker) {
326noswitch:
327 return f(arg);
328 }
329
330 zth::Fiber* fiber = worker->currentFiber();
331 if(!fiber)
332 goto noswitch;
333
334 zth::Context* context = fiber->context();
335 if(!context)
336 goto noswitch;
337
338 return context->stack_switch(stack, size, f, arg);
339}
340#endif // ZTH_STACK_SWITCH
The fiber.
Definition fiber.h:49
Context * context() const noexcept
Definition fiber.h:134
static safe_ptr< singleton_type >::type instance() noexcept
Return the only instance of T within this thread.
Definition util.h:1047
The class that manages the fibers within this thread.
Definition worker.h:35
Fiber * currentFiber() const noexcept
Definition worker.h:94
void * stack_switch(void *stack, size_t size, void *(*f)(void *) noexcept, void *arg) noexcept
Definition context.h:529
Context switch mechanism implementation.
#define ZTH_STACK_WATERMARKER
Definition context.cpp:209
void context_entry(zth::Context *context) noexcept
Entry point of the fiber.
Definition context.cpp:175
#define zth_stack_switch(stack, size, f, arg)
Call the function f using the new stack pointer.
Definition context.h:40
void zth_abort(char const *fmt,...)
Aborts the process after printing the given printf() formatted message.
Definition util.cpp:334
Worker & currentWorker() noexcept
Return the (thread-local) singleton Worker instance.
Definition worker.h:389
void yield(Fiber *preferFiber=nullptr, bool alwaysYield=false, Timestamp const &now=Timestamp::now())
Allow a context switch.
Definition worker.h:436
fiber_type< F >::factory fiber(F f, char const *name=nullptr)
Create a new fiber.
Definition async.h:754
size_t stack_watermark_remaining(void *stack) noexcept
Return the remaining stack size that was never touched.
Definition context.cpp:313
void stack_watermark_init(void *stack, size_t size) noexcept
Initialize the memory region for stack high water marking.
Definition context.cpp:243
size_t stack_watermark_maxused(void *stack) noexcept
Return the high water mark of the stack.
Definition context.cpp:281
size_t stack_watermark_size(void *stack) noexcept
Return the size of the given stack region.
Definition context.cpp:265
#define zth_dbg(group, fmt, a...)
Debug printf()-like function.
Definition util.h:189
void context_switch(Context *from, Context *to) noexcept
Perform context switch.
Definition context.cpp:112
void context_destroy(Context *context) noexcept
Destroy and cleanup a context.
Definition context.cpp:95
int context_init() noexcept
One-time context mechanism initialization.
Definition context.cpp:29
string err(int e)
Return a string like strerror() does, but as a zth::string.
Definition util.h:675
void context_deinit() noexcept
Final cleanup.
Definition context.cpp:40
int context_create(Context *&context, ContextAttr const &attr) noexcept
Create a context.
Definition context.cpp:55
size_t context_stack_usage(Context *context) noexcept
Return the high water mark of the stack of the given context.
Definition context.cpp:139
Stack information.
Definition context.h:107
#define zth_assert(expr)
assert(), but better integrated in Zth.
Definition util.h:212
#define likely(expr)
Marks the given expression to likely be evaluated to true.
Definition util.h:40