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 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 // cppcheck-suppress nullPointerRedundantCheck
118 from->context_switch(*to);
119
120 // Got back from somewhere else.
121 from->stackGuard();
122}
123
128size_t context_stack_usage(Context* context) noexcept
129{
130 if(!Config::EnableStackWaterMark || !context)
131 return 0;
132
134 void* guard = nullptr;
135
136 if(f && f->context() == context) {
137 // Guards may be in place on the current stack. Disable it
138 // while iterating it.
139 guard = context->stackGuard(nullptr);
140 }
141
142 size_t res = stack_watermark_maxused(context->stackUsable().p);
143
144 // cppcheck-suppress knownConditionTrueFalse
145 if(guard)
146 context->stackGuard(guard);
147
148 return res;
149}
150
151} // namespace zth
152
164void context_entry(zth::Context* context) noexcept
165{
166 zth_assert(context);
167
168 // Go execute the fiber.
169 // cppcheck-suppress nullPointerRedundantCheck
170 context->stackGuard();
171
172 try {
173 // cppcheck-suppress nullPointerRedundantCheck
174 context->attr().entry(context->attr().arg);
175 } catch(...) {
176 zth_dbg(context, "[%s] Uncaught exception in fiber", zth::currentWorker().id_str());
177 }
178
179 // Fiber has quit. Switch to another one.
180 context->die();
181 zth::yield();
182
183 // We should never get here, otherwise the fiber wasn't dead...
184 zth_abort("Returned to finished context");
185}
186
187
188
190// Stack functions
191
192namespace zth {
193
194#define ZTH_STACK_WATERMARKER ((uint64_t)0x5a7468574de2889eULL) // Some nice UTF-8 text.
195
203static void stack_watermark_align(void*& stack, size_t*& sizeptr, size_t* size = nullptr) noexcept
204{
205 if(!stack)
206 return;
207
208 uintptr_t stack_ = reinterpret_cast<uintptr_t>(stack);
209
210 // Align to size_t
211 stack_ = (stack_ + sizeof(size_t) - 1U) & ~(uintptr_t)(sizeof(size_t) - 1U);
212 // The stack size is stored here.
213 sizeptr = reinterpret_cast<size_t*>(stack_); // NOLINT
214
215 // Reduce stack to store the size.
216 stack_ += sizeof(size_t);
217 if(size)
218 // Reduce remaining stack size.
219 *size -= stack_ - (uintptr_t)stack; // NOLINT
220
221 stack = reinterpret_cast<void*>(stack_); // NOLINT
222}
223
228void stack_watermark_init(void* stack, size_t size) noexcept
229{
230 if(!Config::EnableStackWaterMark || !stack || !size)
231 return;
232
233 // Most likely, but growing up stack is not implemented here (yet?)
234 zth_assert(Context::stackGrowsDown(&size));
235
236 size_t* sizeptr = nullptr;
237 stack_watermark_align(stack, sizeptr, &size);
238 *sizeptr = size;
239
240 for(size_t i = 0; i * sizeof(uint64_t) <= size - sizeof(uint64_t); i++)
241 static_cast<uint64_t*>(stack)[i] = ZTH_STACK_WATERMARKER;
242}
243
250size_t stack_watermark_size(void* stack) noexcept
251{
252 if(!Config::EnableStackWaterMark || !stack)
253 return 0;
254
255 size_t* sizeptr = nullptr;
256 stack_watermark_align(stack, sizeptr);
257 return *sizeptr;
258}
259
266size_t stack_watermark_maxused(void* stack) noexcept
267{
268 if(!Config::EnableStackWaterMark || !stack)
269 return 0;
270
271 size_t* sizeptr = nullptr;
272 stack_watermark_align(stack, sizeptr);
273 size_t size = *sizeptr;
274
275 size_t unused = 0;
276
277#ifdef ZTH_USE_VALGRIND
278 VALGRIND_DISABLE_ADDR_ERROR_REPORTING_IN_RANGE(stack, size); // NOLINT
279#endif
280
281 for(uint64_t* s = static_cast<uint64_t*>(stack);
282 unused < size / sizeof(uint64_t) && *s == ZTH_STACK_WATERMARKER; unused++, s++)
283 ;
284
285#ifdef ZTH_USE_VALGRIND
286 VALGRIND_ENABLE_ADDR_ERROR_REPORTING_IN_RANGE(stack, size); // NOLINT
287#endif
288
289 return size - unused * sizeof(uint64_t);
290}
291
298size_t stack_watermark_remaining(void* stack) noexcept
299{
300 return stack_watermark_size(stack) - stack_watermark_maxused(stack);
301}
302
303} // namespace zth
304
305#ifdef ZTH_STACK_SWITCH
306void* zth_stack_switch(void* stack, size_t size, void* (*f)(void*) noexcept, void* arg) noexcept
307{
309
310 if(!worker) {
311noswitch:
312 return f(arg);
313 }
314
315 zth::Fiber* fiber = worker->currentFiber();
316 if(!fiber)
317 goto noswitch;
318
319 zth::Context* context = fiber->context();
320 if(!context)
321 goto noswitch;
322
323 return context->stack_switch(stack, size, f, arg);
324}
325#endif // ZTH_STACK_SWITCH
The fiber.
Definition fiber.h:49
Context * context() const noexcept
Definition fiber.h:135
static safe_ptr< singleton_type >::type instance() noexcept
Return the only instance of T within this thread.
Definition util.h:1181
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:506
Context switch mechanism implementation.
#define ZTH_STACK_WATERMARKER
Definition context.cpp:194
void context_entry(zth::Context *context) noexcept
Entry point of the fiber.
Definition context.cpp:164
#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:337
Worker & currentWorker() noexcept
Return the (thread-local) singleton Worker instance.
Definition worker.h:407
fiber_type< F >::fiber fiber(F &&f, Args &&... args)
Create and start a new fiber.
Definition async.h:1192
void yield(Fiber *preferFiber=nullptr, bool alwaysYield=false, Timestamp const &now=Timestamp::now())
Allow a context switch.
Definition worker.h:454
size_t stack_watermark_remaining(void *stack) noexcept
Return the remaining stack size that was never touched.
Definition context.cpp:298
void stack_watermark_init(void *stack, size_t size) noexcept
Initialize the memory region for stack high water marking.
Definition context.cpp:228
size_t stack_watermark_maxused(void *stack) noexcept
Return the high water mark of the stack.
Definition context.cpp:266
size_t stack_watermark_size(void *stack) noexcept
Return the size of the given stack region.
Definition context.cpp:250
#define zth_dbg(group, fmt, a...)
Debug printf()-like function.
Definition util.h:194
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:686
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:128
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 likely(expr)
Marks the given expression to likely be evaluated to true.
Definition util.h:45