Zth (libzth)
ucontext.h
Go to the documentation of this file.
1 #ifndef ZTH_CONTEXT_UCONTEXT_H
2 #define ZTH_CONTEXT_UCONTEXT_H
3 /*
4  * Zth (libzth), a cooperative userspace multitasking library.
5  * Copyright (C) 2019-2022 Jochem Rutgers
6  *
7  * This Source Code Form is subject to the terms of the Mozilla Public
8  * License, v. 2.0. If a copy of the MPL was not distributed with this
9  * file, You can obtain one at https://mozilla.org/MPL/2.0/.
10  */
11 
12 #ifndef ZTH_CONTEXT_CONTEXT_H
13 # error This file must be included by libzth/context/context.h.
14 #endif
15 
16 #ifdef __cplusplus
17 
18 # ifdef ZTH_OS_MAC
19 // For ucontext_t
20 # ifndef _XOPEN_SOURCE
21 # error Please define _XOPEN_SOURCE before including headers.
22 # endif
23 # include <sched.h>
24 # endif
25 
26 # include <csetjmp>
27 # include <csignal>
28 # include <ucontext.h>
29 
30 namespace zth {
31 
32 # pragma GCC diagnostic push
33 # pragma GCC diagnostic ignored "-Wdeprecated-declarations" // I know...
34 class Context : public impl::ContextArch<Context> {
36 public:
38 
39  constexpr explicit Context(ContextAttr const& attr) noexcept
40  : base(attr)
41  , m_env()
42  {}
43 
44 private:
45  static void context_trampoline(Context* context, sigjmp_buf origin)
46  {
47  // We got here via setcontext().
48 
49 # ifdef ZTH_ENABLE_ASAN
50  void const* oldstack = nullptr;
51  size_t oldsize = 0;
52  __sanitizer_finish_switch_fiber(nullptr, &oldstack, &oldsize);
53 
54  // We are jumping back.
55  __sanitizer_start_switch_fiber(nullptr, oldstack, oldsize);
56 # endif
57 
58  // Save the current context, and return to create().
59  if(sigsetjmp(context->m_env, Config::ContextSignals) == 0)
60  siglongjmp(origin, 1);
61 
62  // Note that context_entry has the __sanitizer_finish_switch_fiber().
63  context_entry(context);
64  }
65 
66 public:
67  int create() noexcept
68  {
69  int res = base::create();
70  if(unlikely(res))
71  return res;
72 
73  if(unlikely(!stack()))
74  // Stackless fiber only saves current context; nothing to do.
75  return 0;
76 
77  // Get current context, to inherit signals/masks.
78  ucontext_t uc;
79  if(getcontext(&uc))
80  return EINVAL;
81 
82  // Modify the stack of the new context.
83  uc.uc_link = nullptr;
84  Stack const& stack_ = stackUsable();
85  uc.uc_stack.ss_sp = stack_.p;
86  uc.uc_stack.ss_size = stack_.size;
87 
88  // Modify the function to call from this new context.
89  sigjmp_buf origin;
90  makecontext(
91  &uc, reinterpret_cast<void (*)(void)>(&context_trampoline), 2, this,
92  origin);
93 
94 # ifdef ZTH_ENABLE_ASAN
95  void* fake_stack = nullptr;
96  __sanitizer_start_switch_fiber(&fake_stack, stack_.p, stack_.size);
97 # endif
98 
99  // switchcontext() is slow, we want to use sigsetjmp/siglongjmp instead.
100  // So, we initialize the sigjmp_buf from the just created context.
101  // After this initial setup, context_switch() is good to go.
102  if(sigsetjmp(origin, Config::ContextSignals) == 0) {
103  // Here we go into the context for the first time.
104  setcontext(&uc);
105  }
106 
107  // Got back from context_trampoline(). The context is ready now.
108 
109 # ifdef ZTH_ENABLE_ASAN
110  __sanitizer_finish_switch_fiber(fake_stack, nullptr, nullptr);
111 # endif
112  return 0;
113  }
114 
115  void context_switch(Context& to) noexcept
116  {
117  // switchcontext() restores signal masks, which is slow...
118  if(sigsetjmp(m_env, Config::ContextSignals) == 0)
119  siglongjmp(to.m_env, 1);
120  }
121 
122 private:
123  sigjmp_buf m_env;
124 };
125 # pragma GCC diagnostic pop
126 
127 } // namespace zth
128 #endif // __cplusplus
129 #endif // ZTH_CONTEXT_UCONTEXT_H
int create() noexcept
Definition: ucontext.h:67
impl::ContextArch< Context > base
Definition: ucontext.h:37
void context_switch(Context &to) noexcept
Definition: ucontext.h:115
sigjmp_buf m_env
Definition: sigaltstack.h:302
constexpr Context(ContextAttr const &attr) noexcept
Definition: sigaltstack.h:65
Stack const & stack() const noexcept
Return the stack address.
Definition: context.h:252
int create() noexcept
Create context.
Definition: context.h:162
ContextAttr & attr() noexcept
Return the context attributes, requested by the user.
Definition: context.h:146
Stack const & stackUsable() const noexcept
Return the start of the actual usable stack.
Definition: context.h:260
#define ZTH_CLASS_NEW_DELETE(T)
Define new/delete operators for a class, which are allocator-aware.
Definition: allocator.h:114
Definition: allocator.h:23
void context_entry(Context *context)
static bool const ContextSignals
Take POSIX signal into account when doing a context switch.
Definition: config.h:139
Stack information.
Definition: context.h:110
#define unlikely(expr)
Marks the given expression to likely be evaluated to true.
Definition: util.h:56