]> git.ipfire.org Git - thirdparty/pdns.git/blame - pdns/mtasker_fcontext.cc
Merge pull request #8223 from PowerDNS/omoerbeek-patch-1
[thirdparty/pdns.git] / pdns / mtasker_fcontext.cc
CommitLineData
12471842
PL
1/*
2 * This file is part of PowerDNS or dnsdist.
3 * Copyright -- PowerDNS.COM B.V. and its contributors
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of version 2 of the GNU General Public License as
7 * published by the Free Software Foundation.
8 *
9 * In addition, for the avoidance of any doubt, permission is granted to
10 * link this program with OpenSSL and to (re)distribute the binaries
11 * produced as the result of such linking.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 */
d83568e5
AN
22#include "mtasker_context.hh"
23#include <exception>
24#include <cassert>
f9e89baf 25#include <type_traits>
f9e89baf 26#include <boost/version.hpp>
64413f6a 27#if BOOST_VERSION < 106100
f53de853 28#include <boost/context/fcontext.hpp>
f9e89baf 29using boost::context::make_fcontext;
64413f6a
RG
30#else
31#include <boost/context/detail/fcontext.hpp>
32using boost::context::detail::make_fcontext;
33#endif /* BOOST_VERSION < 106100 */
f53de853 34
ec7fcc43
RG
35#ifdef PDNS_USE_VALGRIND
36#include <valgrind/valgrind.h>
37#endif /* PDNS_USE_VALGRIND */
f9e89baf 38
9c811931
RG
39#ifdef HAVE_FIBER_SANITIZER
40__thread void* t_mainStack{nullptr};
41__thread size_t t_mainStackSize{0};
42#endif /* HAVE_FIBER_SANITIZER */
43
f9e89baf 44#if BOOST_VERSION < 105600
440284e5
AN
45/* Note: This typedef means functions taking fcontext_t*, like jump_fcontext(),
46 * now require a reinterpret_cast rather than a static_cast, since we're
47 * casting from pdns_context_t->uc_mcontext, which is void**, to
48 * some_opaque_struct**. In later versions fcontext_t is already void*. So if
49 * you remove this, then fix the ugly.
50 */
f9e89baf
AN
51using fcontext_t = boost::context::fcontext_t*;
52
440284e5 53/* Emulate the >= 1.56 API for Boost 1.52 through 1.55 */
f9e89baf
AN
54static inline intptr_t
55jump_fcontext (fcontext_t* const ofc, fcontext_t const nfc,
56 intptr_t const arg) {
440284e5
AN
57 /* If the fcontext_t is preallocated then use it, otherwise allocate one
58 * on the stack ('self') and stash a pointer away in *ofc so the returning
59 * MThread can access it. This is safe because we're suspended, so the
60 * context object always outlives the jump.
61 */
f9e89baf
AN
62 if (*ofc) {
63 return boost::context::jump_fcontext (*ofc, nfc, arg);
64 } else {
65 boost::context::fcontext_t self;
66 *ofc = &self;
67 auto ret = boost::context::jump_fcontext (*ofc, nfc, arg);
68 *ofc = nullptr;
69 return ret;
70 }
71}
72#else
f53de853
RG
73
74#if BOOST_VERSION < 106100
d83568e5
AN
75using boost::context::fcontext_t;
76using boost::context::jump_fcontext;
f53de853
RG
77#else
78using boost::context::detail::fcontext_t;
79using boost::context::detail::jump_fcontext;
80using boost::context::detail::transfer_t;
81#endif /* BOOST_VERSION < 106100 */
f9e89baf
AN
82
83static_assert (std::is_pointer<fcontext_t>::value,
84 "Boost Context has changed the fcontext_t type again :-(");
85#endif
d83568e5 86
440284e5
AN
87/* Boost context only provides a means of passing a single argument across a
88 * jump. args_t simply provides a way to pass more by reference.
89 */
d83568e5 90struct args_t {
f53de853 91#if BOOST_VERSION < 106100
d83568e5 92 fcontext_t prev_ctx = nullptr;
f53de853 93#endif
d83568e5 94 pdns_ucontext_t* self = nullptr;
abc75058 95 boost::function<void(void)>* work = nullptr;
d83568e5
AN
96};
97
98extern "C" {
99static
100void
f53de853 101#if BOOST_VERSION < 106100
d83568e5 102threadWrapper (intptr_t const xargs) {
f53de853
RG
103#else
104threadWrapper (transfer_t const t) {
105#endif
440284e5
AN
106 /* Access the args passed from pdns_makecontext, and copy them directly from
107 * the calling stack on to ours (we're now using the MThreads stack).
108 * This saves heap allocating an args object, at the cost of an extra
109 * context switch to fashion this constructor-like init phase. The work
110 * function object is still only moved after we're (re)started, so may
111 * still be set or changed after a call to pdns_makecontext. This matches
112 * the behaviour of the System V implementation, which can inherently only
113 * be passed ints and pointers.
114 */
9c811931 115 notifyStackSwitchDone();
f53de853 116#if BOOST_VERSION < 106100
d83568e5 117 auto args = reinterpret_cast<args_t*>(xargs);
f53de853
RG
118#else
119 auto args = reinterpret_cast<args_t*>(t.data);
120#endif
d83568e5
AN
121 auto ctx = args->self;
122 auto work = args->work;
f53de853 123 /* we switch back to pdns_makecontext() */
9c811931 124 notifyStackSwitchToKernel();
f53de853 125#if BOOST_VERSION < 106100
f9e89baf
AN
126 jump_fcontext (reinterpret_cast<fcontext_t*>(&ctx->uc_mcontext),
127 static_cast<fcontext_t>(args->prev_ctx), 0);
f53de853
RG
128#else
129 transfer_t res = jump_fcontext (t.fctx, 0);
130 /* we got switched back from pdns_swapcontext() */
131 if (res.data) {
132 /* if res.data is not a nullptr, it holds a pointer to the context
133 we just switched from, and we need to fill it to be able to
134 switch back to it later. */
135 fcontext_t* ptr = static_cast<fcontext_t*>(res.data);
136 *ptr = res.fctx;
137 }
138#endif
9c811931 139 notifyStackSwitchDone();
d83568e5
AN
140 args = nullptr;
141
142 try {
143 auto start = std::move (*work);
144 start();
145 } catch (...) {
146 ctx->exception = std::current_exception();
147 }
148
9c811931 149 notifyStackSwitchToKernel();
440284e5 150 /* Emulate the System V uc_link feature. */
d83568e5 151 auto const next_ctx = ctx->uc_link->uc_mcontext;
f53de853 152#if BOOST_VERSION < 106100
f9e89baf
AN
153 jump_fcontext (reinterpret_cast<fcontext_t*>(&ctx->uc_mcontext),
154 static_cast<fcontext_t>(next_ctx),
580f51c2 155 reinterpret_cast<intptr_t>(ctx));
f53de853
RG
156#else
157 jump_fcontext (static_cast<fcontext_t>(next_ctx), 0);
158#endif
159
d83568e5
AN
160#ifdef NDEBUG
161 __builtin_unreachable();
162#endif
163}
164}
165
166pdns_ucontext_t::pdns_ucontext_t
167(): uc_mcontext(nullptr), uc_link(nullptr) {
ec7fcc43
RG
168#ifdef PDNS_USE_VALGRIND
169 valgrind_id = 0;
170#endif /* PDNS_USE_VALGRIND */
d83568e5
AN
171}
172
173pdns_ucontext_t::~pdns_ucontext_t
174() {
440284e5
AN
175 /* There's nothing to delete here since fcontext doesn't require anything
176 * to be heap allocated.
177 */
ec7fcc43
RG
178#ifdef PDNS_USE_VALGRIND
179 if (valgrind_id != 0) {
180 VALGRIND_STACK_DEREGISTER(valgrind_id);
181 }
182#endif /* PDNS_USE_VALGRIND */
d83568e5
AN
183}
184
185void
186pdns_swapcontext
187(pdns_ucontext_t& __restrict octx, pdns_ucontext_t const& __restrict ctx) {
f53de853
RG
188 /* we either switch back to threadwrapper() if it's the first time,
189 or we switch back to pdns_swapcontext(),
190 in both case we will be returning from a call to jump_fcontext(). */
191#if BOOST_VERSION < 106100
580f51c2 192 intptr_t ptr = jump_fcontext(reinterpret_cast<fcontext_t*>(&octx.uc_mcontext),
193 static_cast<fcontext_t>(ctx.uc_mcontext), 0);
194
195 auto origctx = reinterpret_cast<pdns_ucontext_t*>(ptr);
196 if(origctx && origctx->exception)
197 std::rethrow_exception (origctx->exception);
f53de853
RG
198#else
199 transfer_t res = jump_fcontext (static_cast<fcontext_t>(ctx.uc_mcontext), &octx.uc_mcontext);
200 if (res.data) {
201 /* if res.data is not a nullptr, it holds a pointer to the context
202 we just switched from, and we need to fill it to be able to
203 switch back to it later. */
204 fcontext_t* ptr = static_cast<fcontext_t*>(res.data);
205 *ptr = res.fctx;
206 }
207 if (ctx.exception) {
208 std::rethrow_exception (ctx.exception);
209 }
210#endif
d83568e5
AN
211}
212
213void
214pdns_makecontext
abc75058 215(pdns_ucontext_t& ctx, boost::function<void(void)>& start) {
d83568e5
AN
216 assert (ctx.uc_link);
217 assert (ctx.uc_stack.size() >= 8192);
218 assert (!ctx.uc_mcontext);
5529b1b1
RG
219 ctx.uc_mcontext = make_fcontext (&ctx.uc_stack[ctx.uc_stack.size()-1],
220 ctx.uc_stack.size()-1, &threadWrapper);
d83568e5
AN
221 args_t args;
222 args.self = &ctx;
223 args.work = &start;
f53de853 224 /* jumping to threadwrapper */
5529b1b1 225 notifyStackSwitch(&ctx.uc_stack[ctx.uc_stack.size()-1], ctx.uc_stack.size()-1);
f53de853 226#if BOOST_VERSION < 106100
f9e89baf
AN
227 jump_fcontext (reinterpret_cast<fcontext_t*>(&args.prev_ctx),
228 static_cast<fcontext_t>(ctx.uc_mcontext),
d83568e5 229 reinterpret_cast<intptr_t>(&args));
f53de853
RG
230#else
231 transfer_t res = jump_fcontext (static_cast<fcontext_t>(ctx.uc_mcontext),
232 &args);
233 /* back from threadwrapper, updating the context */
234 ctx.uc_mcontext = res.fctx;
235#endif
9c811931
RG
236 notifyStackSwitchDone();
237
d83568e5 238}