Skip to content

Commit

Permalink
rec: stop supporting ucontext flavor for context switching
Browse files Browse the repository at this point in the history
  • Loading branch information
omoerbeek committed Sep 10, 2024
1 parent 12af178 commit 6a590a1
Show file tree
Hide file tree
Showing 7 changed files with 233 additions and 429 deletions.
3 changes: 0 additions & 3 deletions meson/boost-context/meson.build
Original file line number Diff line number Diff line change
@@ -1,5 +1,2 @@
dep_boost_context = dependency('boost', modules: ['context'], required: true)
# Fixed value, we no longer support SystemV
conf.set('HAVE_BOOST_CONTEXT', true)
summary('Context', dep_boost_context.found(), bool_yn: true, section: 'Boost')

1 change: 0 additions & 1 deletion pdns/recursordist/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,6 @@ EXTRA_DIST = \
lua_hpp.mk \
malloctrace.cc malloctrace.hh \
mkpubsuffixcc \
mtasker_fcontext.cc mtasker_ucontext.cc \
NOTICE \
opensslsigners.hh opensslsigners.cc \
portsmplexer.cc \
Expand Down
14 changes: 1 addition & 13 deletions pdns/recursordist/configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -52,18 +52,7 @@ AC_FUNC_STRERROR_R
PDNS_CHECK_CLOCK_GETTIME

BOOST_REQUIRE([1.54])

# Boost Context was introduced in 1.51 (Aug 2012), but there was an immediate
# API break in 1.52 (Nov 2012), so we only support that, and later.
pdns_context_library=""
AS_IF([test $boost_major_version -ge 152], [BOOST_CONTEXT([], [no])])
AS_IF([test x"$boost_cv_lib_context" = "xyes"], [
pdns_context_library="Boost Context"
], [
AC_CHECK_FUNCS([getcontext makecontext swapcontext], [pdns_context_library="System V ucontexts"])
])
AC_MSG_CHECKING([what context library to use for MTasker])
AS_IF([test -n "$pdns_context_library"], [AC_MSG_RESULT([$pdns_context_library])], [AC_MSG_ERROR([neither boost::context nor System V ucontexts available])])
BOOST_CONTEXT([], [yes])]

PDNS_ENABLE_UNIT_TESTS
PDNS_ENABLE_REPRODUCIBLE
Expand Down Expand Up @@ -280,5 +269,4 @@ AS_IF([test "x$HAVE_LIBCURL" != "xn"],
[AC_MSG_NOTICE([libcurl: yes])],
[AC_MSG_NOTICE([libcurl: no])]
)
AC_MSG_NOTICE([Context library: $pdns_context_library])
AC_MSG_NOTICE([])
236 changes: 232 additions & 4 deletions pdns/recursordist/mtasker_context.cc
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,240 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifdef HAVE_CONFIG_H

#include "config.h"
#include "mtasker_context.hh"
#include <exception>
#include <cassert>
#include <type_traits>
#include <boost/version.hpp>
#if BOOST_VERSION < 106100
#include <boost/context/fcontext.hpp>
using boost::context::make_fcontext;
#else
#include <boost/context/detail/fcontext.hpp>
using boost::context::detail::make_fcontext;
#endif /* BOOST_VERSION < 106100 */

// __CET__ is set by the compiler if relevant, so far only relevant/tested for amd64 on OpenBSD
#if defined(__amd64__)
#if __CET__ & 0x1
#define CET_ENDBR __asm("endbr64")
#else
#define CET_ENDBR
#endif
#else
#define CET_ENDBR
#endif

#ifdef PDNS_USE_VALGRIND
#include <valgrind/valgrind.h>
#endif /* PDNS_USE_VALGRIND */

#ifdef HAVE_FIBER_SANITIZER
__thread void* t_mainStack{nullptr};
__thread size_t t_mainStackSize{0};
#endif /* HAVE_FIBER_SANITIZER */

#if BOOST_VERSION < 105600
/* Note: This typedef means functions taking fcontext_t*, like jump_fcontext(),
* now require a reinterpret_cast rather than a static_cast, since we're
* casting from pdns_context_t->uc_mcontext, which is void**, to
* some_opaque_struct**. In later versions fcontext_t is already void*. So if
* you remove this, then fix the ugly.
*/
using fcontext_t = boost::context::fcontext_t*;

/* Emulate the >= 1.56 API for Boost 1.52 through 1.55 */
static inline intptr_t
jump_fcontext(fcontext_t* const ofc, fcontext_t const nfc,
intptr_t const arg)
{
/* If the fcontext_t is preallocated then use it, otherwise allocate one
* on the stack ('self') and stash a pointer away in *ofc so the returning
* MThread can access it. This is safe because we're suspended, so the
* context object always outlives the jump.
*/
if (*ofc) {
return boost::context::jump_fcontext(*ofc, nfc, arg);
}
else {
boost::context::fcontext_t self;
*ofc = &self;
auto ret = boost::context::jump_fcontext(*ofc, nfc, arg);
*ofc = nullptr;
return ret;
}
}
#else

#if BOOST_VERSION < 106100
using boost::context::fcontext_t;
using boost::context::jump_fcontext;
#else
using boost::context::detail::fcontext_t;
using boost::context::detail::jump_fcontext;
using boost::context::detail::transfer_t;
#endif /* BOOST_VERSION < 106100 */

static_assert(std::is_pointer<fcontext_t>::value,
"Boost Context has changed the fcontext_t type again :-(");
#endif

/* Boost context only provides a means of passing a single argument across a
* jump. args_t simply provides a way to pass more by reference.
*/
struct args_t
{
#if BOOST_VERSION < 106100
fcontext_t prev_ctx = nullptr;
#endif
pdns_ucontext_t* self = nullptr;
std::function<void(void)>* work = nullptr;
};

extern "C"
{
static void
#if BOOST_VERSION < 106100
threadWrapper(intptr_t const xargs)
{
#else
threadWrapper(transfer_t const t)

Check warning on line 121 in pdns/recursordist/mtasker_context.cc

View workflow job for this annotation

GitHub Actions / Analyze (cpp, rec)

parameter name 't' is too short, expected at least 3 characters (readability-identifier-length - Level=Warning)
{
#endif
/* Access the args passed from pdns_makecontext, and copy them directly from
* the calling stack on to ours (we're now using the MThreads stack).
* This saves heap allocating an args object, at the cost of an extra
* context switch to fashion this constructor-like init phase. The work
* function object is still only moved after we're (re)started, so may
* still be set or changed after a call to pdns_makecontext. This matches
* the behaviour of the System V implementation, which can inherently only
* be passed ints and pointers.
*/
notifyStackSwitchDone();
#if BOOST_VERSION < 106100
auto args = reinterpret_cast<args_t*>(xargs);
#else
auto args = reinterpret_cast<args_t*>(t.data);

Check warning on line 137 in pdns/recursordist/mtasker_context.cc

View workflow job for this annotation

GitHub Actions / Analyze (cpp, rec)

'auto args' can be declared as 'auto *args' (readability-qualified-auto - Level=Warning)

Check warning on line 137 in pdns/recursordist/mtasker_context.cc

View workflow job for this annotation

GitHub Actions / Analyze (cpp, rec)

do not use reinterpret_cast (cppcoreguidelines-pro-type-reinterpret-cast - Level=Warning)
#endif
auto ctx = args->self;

Check warning on line 139 in pdns/recursordist/mtasker_context.cc

View workflow job for this annotation

GitHub Actions / Analyze (cpp, rec)

'auto ctx' can be declared as 'auto *ctx' (readability-qualified-auto - Level=Warning)
auto work = args->work;

Check warning on line 140 in pdns/recursordist/mtasker_context.cc

View workflow job for this annotation

GitHub Actions / Analyze (cpp, rec)

'auto work' can be declared as 'auto *work' (readability-qualified-auto - Level=Warning)
/* we switch back to pdns_makecontext() */
notifyStackSwitchToKernel();
#if BOOST_VERSION < 106100
jump_fcontext(reinterpret_cast<fcontext_t*>(&ctx->uc_mcontext),
static_cast<fcontext_t>(args->prev_ctx), 0);
#else
transfer_t res = jump_fcontext(t.fctx, 0);

Check warning on line 147 in pdns/recursordist/mtasker_context.cc

View workflow job for this annotation

GitHub Actions / Analyze (cpp, rec)

use nullptr (modernize-use-nullptr - Level=Warning)
CET_ENDBR;
/* we got switched back from pdns_swapcontext() */
if (res.data) {

Check warning on line 150 in pdns/recursordist/mtasker_context.cc

View workflow job for this annotation

GitHub Actions / Analyze (cpp, rec)

implicit conversion 'void *' -> bool (readability-implicit-bool-conversion - Level=Warning)
/* if res.data is not a nullptr, it holds a pointer to the context
we just switched from, and we need to fill it to be able to
switch back to it later. */
fcontext_t* ptr = static_cast<fcontext_t*>(res.data);

Check warning on line 154 in pdns/recursordist/mtasker_context.cc

View workflow job for this annotation

GitHub Actions / Analyze (cpp, rec)

use auto when initializing with a cast to avoid duplicating the type name (modernize-use-auto - Level=Warning)
*ptr = res.fctx;
}
#endif
notifyStackSwitchDone();
args = nullptr;

try {
auto start = std::move(*work);
start();
}
catch (...) {
ctx->exception = std::current_exception();
}

notifyStackSwitchToKernel();
/* Emulate the System V uc_link feature. */
auto const next_ctx = ctx->uc_link->uc_mcontext;

Check warning on line 171 in pdns/recursordist/mtasker_context.cc

View workflow job for this annotation

GitHub Actions / Analyze (cpp, rec)

'const auto next_ctx' can be declared as 'auto *const next_ctx' (readability-qualified-auto - Level=Warning)
#if BOOST_VERSION < 106100
jump_fcontext(reinterpret_cast<fcontext_t*>(&ctx->uc_mcontext),
static_cast<fcontext_t>(next_ctx),
reinterpret_cast<intptr_t>(ctx));
#else
jump_fcontext(static_cast<fcontext_t>(next_ctx), 0);

Check warning on line 177 in pdns/recursordist/mtasker_context.cc

View workflow job for this annotation

GitHub Actions / Analyze (cpp, rec)

use nullptr (modernize-use-nullptr - Level=Warning)
#endif

#ifdef NDEBUG
__builtin_unreachable();
#endif
}
}

pdns_ucontext_t::pdns_ucontext_t() :
uc_mcontext(nullptr), uc_link(nullptr)
{
#ifdef PDNS_USE_VALGRIND
valgrind_id = 0;
#endif /* PDNS_USE_VALGRIND */
}

pdns_ucontext_t::~pdns_ucontext_t()
{
/* There's nothing to delete here since fcontext doesn't require anything
* to be heap allocated.
*/
#ifdef PDNS_USE_VALGRIND
if (valgrind_id != 0) {
VALGRIND_STACK_DEREGISTER(valgrind_id);
}
#endif /* PDNS_USE_VALGRIND */
}

void pdns_swapcontext(pdns_ucontext_t& __restrict octx, pdns_ucontext_t const& __restrict ctx)
{
/* we either switch back to threadwrapper() if it's the first time,
or we switch back to pdns_swapcontext(),
in both case we will be returning from a call to jump_fcontext(). */
#if BOOST_VERSION < 106100
intptr_t ptr = jump_fcontext(reinterpret_cast<fcontext_t*>(&octx.uc_mcontext),
static_cast<fcontext_t>(ctx.uc_mcontext), 0);

auto origctx = reinterpret_cast<pdns_ucontext_t*>(ptr);
if (origctx && origctx->exception)
std::rethrow_exception(origctx->exception);
#else
transfer_t res = jump_fcontext(static_cast<fcontext_t>(ctx.uc_mcontext), &octx.uc_mcontext);
CET_ENDBR;
if (res.data) {
/* if res.data is not a nullptr, it holds a pointer to the context
we just switched from, and we need to fill it to be able to
switch back to it later. */
fcontext_t* ptr = static_cast<fcontext_t*>(res.data);
*ptr = res.fctx;
}
if (ctx.exception) {
std::rethrow_exception(ctx.exception);
}
#endif
}

#if defined(HAVE_BOOST_CONTEXT)
#include "mtasker_fcontext.cc" // NOLINT(bugprone-suspicious-include)
void pdns_makecontext(pdns_ucontext_t& ctx, std::function<void(void)>& start)
{
assert(ctx.uc_link);
assert(ctx.uc_stack.size() >= 8192);
assert(!ctx.uc_mcontext);
ctx.uc_mcontext = make_fcontext(&ctx.uc_stack[ctx.uc_stack.size() - 1],
ctx.uc_stack.size() - 1, &threadWrapper);
args_t args;
args.self = &ctx;
args.work = &start;
/* jumping to threadwrapper */
notifyStackSwitch(&ctx.uc_stack[ctx.uc_stack.size() - 1], ctx.uc_stack.size() - 1);
#if BOOST_VERSION < 106100
jump_fcontext(reinterpret_cast<fcontext_t*>(&args.prev_ctx),
static_cast<fcontext_t>(ctx.uc_mcontext),
reinterpret_cast<intptr_t>(&args));
#else
#include "mtasker_ucontext.cc" // NOLINT(bugprone-suspicious-include)
transfer_t res = jump_fcontext(static_cast<fcontext_t>(ctx.uc_mcontext),
&args);
CET_ENDBR;
/* back from threadwrapper, updating the context */
ctx.uc_mcontext = res.fctx;
#endif
notifyStackSwitchDone();
}
Loading

0 comments on commit 6a590a1

Please sign in to comment.