1#ifndef ZTH_CONTEXT_CONTEXT_H
2#define ZTH_CONTEXT_CONTEXT_H
29# ifndef ZTH_OS_WINDOWS
40# ifdef ZTH_HAVE_VALGRIND
41# include <valgrind/memcheck.h>
44# ifdef ZTH_ENABLE_ASAN
45# include <sanitizer/common_interface_defs.h>
67template <
typename Impl>
73# ifdef ZTH_ENABLE_ASAN
92 return static_cast<Impl&
>(*this);
98 Impl
const&
impl() const noexcept
100 return static_cast<Impl const&
>(*this);
141 m_stackUsable =
Stack();
143 if(
attr().stackSize == 0)
152 impl().valgrindRegister();
156 impl().deallocStack(m_stack);
168 impl().stackGuardDeinit();
169 impl().valgrindDeregister();
170 impl().deinitStack(m_stack);
171 m_stackUsable = m_stack =
Stack();
181 size_t const page =
impl().pageSize();
182 zth_assert(__builtin_popcount((
unsigned int)page) == 1);
195 impl().stackAlign(usable);
216 (size_t)sysconf(_SC_PAGESIZE);
236 return m_stackUsable;
244# ifndef ZTH_OS_WINDOWS
251 size +=
impl().pageSize() * 2;
271 mmap(
nullptr, size, PROT_READ | PROT_WRITE,
272 MAP_PRIVATE | MAP_ANONYMOUS |
MAP_STACK, -1, 0);
277 if(
unlikely((p =
static_cast<void*
>(allocate_noexcept<char>(size))) ==
nullptr)) {
312 size_t const ps =
impl().pageSize();
315 uintptr_t stack_old = (uintptr_t)
stack.
p;
322 uintptr_t stack_new = ((uintptr_t)(stack_old + ps - 1U) & ~(ps - 1U));
335 stack.
p =
reinterpret_cast<char*
>(stack_new);
343# ifdef ZTH_USE_VALGRIND
347 m_valgrind_stack_id = VALGRIND_STACK_REGISTER(
348 m_stackUsable.
p, m_stackUsable.
p + m_stackUsable.
size - 1U);
350 if(RUNNING_ON_VALGRIND)
351 zth_dbg(context,
"[%s] Stack of context %p has Valgrind id %u",
361# ifdef ZTH_USE_VALGRIND
362 VALGRIND_STACK_DEREGISTER(m_valgrind_stack_id);
363 m_valgrind_stack_id = 0;
377 size_t const ps =
impl().pageSize();
379 if(
unlikely(mprotect(m_stackUsable.
p - ps, ps, PROT_NONE)))
381 if(
unlikely(mprotect(m_stackUsable.
p + m_stackUsable.
size, ps, PROT_NONE)))
404 return reinterpret_cast<uintptr_t
>(&i) <
reinterpret_cast<uintptr_t
>(reference);
450 static void set_sp(jmp_buf& env,
void**
sp)
noexcept;
455 static void set_pc(jmp_buf& env,
void*
sp)
noexcept;
489# ifdef ZTH_ENABLE_ASAN
499# ifdef ZTH_ENABLE_ASAN
521# ifdef ZTH_USE_VALGRIND
523 unsigned int m_valgrind_stack_id;
525# ifdef ZTH_ENABLE_ASAN
531template <
typename Impl>
532static Impl* current_context() noexcept;
537# if defined(ZTH_ARCH_ARM)
545# if defined(ZTH_CONTEXT_SIGALTSTACK)
547# elif defined(ZTH_CONTEXT_SJLJ)
549# elif defined(ZTH_CONTEXT_UCONTEXT)
551# elif defined(ZTH_CONTEXT_WINFIBER)
554# error Unknown context switching approach.
Base class of the Context.
static void stack_push(void **&sp, void *p) noexcept
Push data into the stack.
void stackGuardDeinit() noexcept
Release the guards around the memory.
static size_t pageSize() noexcept
Get system's page size.
Impl & impl() noexcept
Return Impl this.
static bool stackGrowsDown(void const *reference)
Checks if the stack grows down or up.
void context_pop_regs() noexcept
Post-sjlj context restoring.
Stack const & stack() const noexcept
Return the stack address.
static void deinit() noexcept
Final system cleanup.
int initStack(Stack &stack, Stack &usable) noexcept
Allocate and initialize stack.
void * stack_switch(void *stack, size_t size, void *(*f)(void *) noexcept, void *arg) noexcept
static void ** sp(Stack const &stack) noexcept
Get the initial stack pointer for the given stack.
void * stackGuard(void *p) noexcept
Configure the guard for the address.
int stackGuardInit() noexcept
Initialize guards around the stack memory.
void die() noexcept
Flag fiber as died after it returned from context_entry().
void stackGuard() noexcept
Configure the guard for the current fiber.
static void set_sp(jmp_buf &env, void **sp) noexcept
Set the stack pointer in a jmp_buf.
void context_prepare_jmp(Impl &to, jmp_buf &env) noexcept
Pre-sjlj jump.
static void set_pc(jmp_buf &env, void *sp) noexcept
Set the program counter in a jmp_buf.
int create() noexcept
Create context.
void deallocStack(Stack &stack) noexcept
Frees the previously allocated stack.
ContextAttr & attr() noexcept
Return the context attributes, requested by the user.
Impl const & impl() const noexcept
Return Impl this.
static void context_trampoline_from_jmp_buf() noexcept
Entry point to jump to from a (sig)jmp_buf.
void valgrindRegister() noexcept
Register the current stack to valgrind.
Stack const & stackUsable() const noexcept
Return the start of the actual usable stack.
void context_switch(Context &to) noexcept
Perform a context switch.
void stackGuard(Stack const &stack) noexcept
Configure the guard for the given stack.
void destroy() noexcept
Destroy and cleanup context.
void deinitStack(Stack &stack) noexcept
Deinit and free stack.
void stackAlign(Stack &stack) noexcept
Compute and modify the stack alignment and size, within the allocated space.
void valgrindDeregister() noexcept
Deregister the current stack from valgrind.
void * allocStack(size_t size) noexcept
Allocate requested size of stack memory.
bool alive() const noexcept
Check if fiber is still running.
size_t calcStackSize(size_t size) noexcept
Compute the stack size, given the requested user size and current configuration.
constexpr ContextBase(ContextAttr const &attr) noexcept
void context_push_regs() noexcept
Pre-sjlj context saving.
static int init() noexcept
One-time system initialization.
ContextAttr const & attr() const noexcept
Return the context attributes, requested by the user.
Worker & currentWorker() noexcept
Return the (thread-local) singleton Worker instance.
void stack_watermark_init(void *stack, size_t size) noexcept
Initialize the memory region for stack high water marking.
#define zth_dbg(group, fmt, a...)
Debug printf()-like function.
void context_entry(Context *context)
static bool const EnableStackGuard
When true, enable stack guards.
#define zth_assert(expr)
assert(), but better integrated in Zth.
#define ZTH_CLASS_NOCOPY(Class)
#define unlikely(expr)
Marks the given expression to likely be evaluated to true.