Zth (libzth)
context.cpp
Go to the documentation of this file.
1 /*
2  * Zth (libzth), a cooperative userspace multitasking library.
3  * Copyright (C) 2019-2022 Jochem Rutgers
4  *
5  * This Source Code Form is subject to the terms of the Mozilla Public
6  * License, v. 2.0. If a copy of the MPL was not distributed with this
7  * file, You can obtain one at https://mozilla.org/MPL/2.0/.
8  */
9 
10 #include <libzth/macros.h>
11 
12 #ifdef ZTH_OS_MAC
13 # define _XOPEN_SOURCE
14 #endif
15 
16 #include <libzth/context.h>
17 #include <libzth/context/context.h>
18 #include <libzth/worker.h>
19 
20 
21 
23 // context functions
24 
25 namespace zth {
26 
32 int context_init() noexcept
33 {
34  zth_dbg(context, "[%s] Initialize", currentWorker().id_str());
35  return Context::init();
36 }
37 
43 void context_deinit() noexcept
44 {
45  zth_dbg(context, "[%s] Deinit", currentWorker().id_str());
47 }
48 
58 int context_create(Context*& context, ContextAttr const& attr) noexcept
59 {
60  try {
61  context = new Context(attr);
62  } catch(std::bad_alloc const&) {
63  zth_dbg(context, "[%s] Cannot create context; %s", currentWorker().id_str(),
64  err(ENOMEM).c_str());
65  return ENOMEM;
66  } catch(...) {
67  // Should not be thrown by new...
68  return EAGAIN;
69  }
70 
71  int res = 0;
72 
73  if((res = context->create())) {
74  delete context;
75  context = nullptr;
76  zth_dbg(context, "[%s] Cannot create context; %s", currentWorker().id_str(),
77  err(res).c_str());
78  return res;
79  }
80 
81  if(likely(attr.stackSize > 0)) {
82 #ifdef ZTH_CONTEXT_WINFIBER
83  zth_dbg(context, "[%s] New context %p", currentWorker().id_str(), context);
84 #else
85  Context::Stack const& stack = context->stackUsable();
86  zth_dbg(context, "[%s] New context %p with stack: %p-%p", currentWorker().id_str(),
87  context, stack.p, stack.p + stack.size - 1u);
88 #endif
89  }
90 
91  return 0;
92 }
93 
99 void context_destroy(Context* context) noexcept
100 {
101  if(!context)
102  return;
103 
104  context->destroy();
105  delete context;
106  // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDelete)
107  zth_dbg(context, "[%s] Deleted context %p", currentWorker().id_str(), context);
108 }
109 
116 void context_switch(Context* from, Context* to) noexcept
117 {
118  zth_assert(from);
119  zth_assert(to);
120 
121 #ifdef ZTH_ENABLE_ASAN
122  zth_assert(to->alive());
123  void* fake_stack = nullptr;
124  __sanitizer_start_switch_fiber(
125  from->alive() ? &fake_stack : nullptr, to->stackUsable().p, to->stackUsable().size);
126 #endif
127 
128  // cppcheck-suppress nullPointerRedundantCheck
129  from->context_switch(*to);
130 
131 #ifdef ZTH_ENABLE_ASAN
132  __sanitizer_finish_switch_fiber(fake_stack, nullptr, nullptr);
133 #endif
134 
135  // Got back from somewhere else.
136  from->stackGuard();
137 }
138 
143 size_t context_stack_usage(Context* context) noexcept
144 {
145  if(!Config::EnableStackWaterMark || !context)
146  return 0;
147 
149  void* guard = nullptr;
150 
151  if(f && f->context() == context) {
152  // Guards may be in place on the current stack. Disable it
153  // while iterating it.
154  guard = context->stackGuard(nullptr);
155  }
156 
157  size_t res = stack_watermark_maxused(context->stackUsable().p);
158 
159  if(guard)
160  context->stackGuard(guard);
161 
162  return res;
163 }
164 
165 } // namespace zth
166 
178 void context_entry(zth::Context* context) noexcept
179 {
180 #ifdef ZTH_ENABLE_ASAN
181  __sanitizer_finish_switch_fiber(nullptr, nullptr, nullptr);
182 #endif
183 
184  zth_assert(context);
185 
186  // Go execute the fiber.
187  // cppcheck-suppress nullPointerRedundantCheck
188  context->stackGuard();
189 
190  try {
191  // cppcheck-suppress nullPointerRedundantCheck
192  context->attr().entry(context->attr().arg);
193  } catch(...) {
194  }
195 
196  // Fiber has quit. Switch to another one.
197  context->die();
198  zth::yield();
199 
200  // We should never get here, otherwise the fiber wasn't dead...
201  zth_abort("Returned to finished context");
202 }
203 
204 
205 
207 // Stack functions
208 
209 namespace zth {
210 
211 #define ZTH_STACK_WATERMARKER ((uint64_t)0x5a7468574de2889eULL) // Some nice UTF-8 text.
212 
220 static void stack_watermark_align(void*& stack, size_t*& sizeptr, size_t* size = nullptr) noexcept
221 {
222  if(!stack)
223  return;
224 
225  uintptr_t stack_ = reinterpret_cast<uintptr_t>(stack);
226 
227  // Align to size_t
228  stack_ = ((uintptr_t)stack_ + sizeof(size_t) - 1) & ~(uintptr_t)(sizeof(size_t) - 1);
229  // The stack size is stored here.
230  sizeptr = reinterpret_cast<size_t*>(stack_);
231 
232  // Reduce stack to store the size.
233  stack_ += sizeof(size_t);
234  if(size)
235  // Reduce remaining stack size.
236  *size -= stack_ - reinterpret_cast<uintptr_t>(stack);
237 
238  stack = reinterpret_cast<void*>(stack_);
239 }
240 
245 void stack_watermark_init(void* stack, size_t size) noexcept
246 {
247  if(!Config::EnableStackWaterMark || !stack || !size)
248  return;
249 
250  // Most likely, but growing up stack is not implemented here (yet?)
252 
253  size_t* sizeptr = nullptr;
254  stack_watermark_align(stack, sizeptr, &size);
255  *sizeptr = size;
256 
257  for(size_t i = 0; i * sizeof(uint64_t) <= size - sizeof(uint64_t); i++)
258  static_cast<uint64_t*>(stack)[i] = ZTH_STACK_WATERMARKER;
259 }
260 
267 size_t stack_watermark_size(void* stack) noexcept
268 {
269  if(!Config::EnableStackWaterMark || !stack)
270  return 0;
271 
272  size_t* sizeptr = nullptr;
273  stack_watermark_align(stack, sizeptr);
274  return *sizeptr;
275 }
276 
283 size_t stack_watermark_maxused(void* stack) noexcept
284 {
285  if(!Config::EnableStackWaterMark || !stack)
286  return 0;
287 
288  size_t* sizeptr = nullptr;
289  stack_watermark_align(stack, sizeptr);
290  size_t size = *sizeptr;
291 
292  size_t unused = 0;
293 
294 #ifdef ZTH_USE_VALGRIND
295  VALGRIND_DISABLE_ADDR_ERROR_REPORTING_IN_RANGE(stack, size); // NOLINT
296 #endif
297 
298  for(uint64_t* s = static_cast<uint64_t*>(stack);
299  unused < size / sizeof(uint64_t) && *s == ZTH_STACK_WATERMARKER; unused++, s++)
300  ;
301 
302 #ifdef ZTH_USE_VALGRIND
303  VALGRIND_ENABLE_ADDR_ERROR_REPORTING_IN_RANGE(stack, size); // NOLINT
304 #endif
305 
306  return size - unused * sizeof(uint64_t);
307 }
308 
315 size_t stack_watermark_remaining(void* stack) noexcept
316 {
317  return stack_watermark_size(stack) - stack_watermark_maxused(stack);
318 }
319 
320 } // namespace zth
321 
322 #ifdef ZTH_STACK_SWITCH
323 void* zth_stack_switch(void* stack, size_t size, void* (*f)(void*) noexcept, void* arg) noexcept
324 {
326 
327  if(!worker) {
328 noswitch:
329  return f(arg);
330  }
331 
332  zth::Fiber* fiber = worker->currentFiber();
333  if(!fiber)
334  goto noswitch;
335 
336  zth::Context* context = fiber->context();
337  if(!context)
338  goto noswitch;
339 
340  return context->stack_switch(stack, size, f, arg);
341 }
342 #endif // ZTH_STACK_SWITCH
static int init() noexcept
Definition: sigaltstack.h:119
static void deinit() noexcept
Definition: sigaltstack.h:139
The fiber.
Definition: fiber.h:52
Context * context() const noexcept
Definition: fiber.h:138
static safe_ptr< singleton_type >::type instance() noexcept
Return the only instance of T within this thread.
Definition: util.h:989
The class that manages the fibers within this thread.
Definition: worker.h:38
Fiber * currentFiber() const noexcept
Definition: worker.h:96
static bool stackGrowsDown(void const *reference)
Checks if the stack grows down or up.
Definition: context.h:427
void * stack_switch(void *stack, size_t size, void *(*f)(void *) noexcept, void *arg) noexcept
Definition: context.h:530
Context switch mechanism implementation.
#define ZTH_STACK_WATERMARKER
Definition: context.cpp:211
void context_entry(zth::Context *context) noexcept
Entry point of the fiber.
Definition: context.cpp:178
#define zth_stack_switch(stack, size, f, arg)
Call the function f using the new stack pointer.
Definition: context.h:41
void zth_abort(char const *fmt,...)
Aborts the process after printing the given printf() formatted message.
Definition: util.cpp:280
Worker & currentWorker() noexcept
Return the (thread-local) singleton Worker instance.
Definition: worker.h:388
void yield(Fiber *preferFiber=nullptr, bool alwaysYield=false, Timestamp const &now=Timestamp::now())
Allow a context switch.
Definition: worker.h:435
fiber_type< F >::factory fiber(F f, char const *name=nullptr)
Create a new fiber.
Definition: async.h:713
constexpr auto guard(T &&g, char const *name=nullptr)
Create a guard from a function.
Definition: fsm14.h:530
size_t stack_watermark_remaining(void *stack) noexcept
Return the remaining stack size that was never touched.
Definition: context.cpp:315
void stack_watermark_init(void *stack, size_t size) noexcept
Initialize the memory region for stack high water marking.
Definition: context.cpp:245
size_t stack_watermark_maxused(void *stack) noexcept
Return the high water mark of the stack.
Definition: context.cpp:283
size_t stack_watermark_size(void *stack) noexcept
Return the size of the given stack region.
Definition: context.cpp:267
#define zth_dbg(group, fmt, a...)
Debug printf()-like function.
Definition: util.h:210
Definition: allocator.h:23
void context_switch(Context *from, Context *to) noexcept
Perform context switch.
Definition: context.cpp:116
void context_destroy(Context *context) noexcept
Destroy and cleanup a context.
Definition: context.cpp:99
int context_init() noexcept
One-time context mechanism initialization.
Definition: context.cpp:32
string err(int e)
Return a string like strerror() does, but as a zth::string.
Definition: util.h:617
void context_deinit() noexcept
Final cleanup.
Definition: context.cpp:43
int context_create(Context *&context, ContextAttr const &attr) noexcept
Create a context.
Definition: context.cpp:58
size_t context_stack_usage(Context *context) noexcept
Return the high water mark of the stack of the given context.
Definition: context.cpp:143
static bool const EnableStackWaterMark
When true, enable stack watermark to detect maximum stack usage.
Definition: config.h:137
Stack information.
Definition: context.h:110
#define zth_assert(expr)
assert(), but better integrated in Zth.
Definition: util.h:236
#define likely(expr)
Marks the given expression to likely be evaluated to true.
Definition: util.h:42