File indexing completed on 2025-01-18 09:30:24
0001
0002
0003
0004
0005
0006
0007 #ifndef BOOST_CONTEXT_CONTINUATION_H
0008 #define BOOST_CONTEXT_CONTINUATION_H
0009
0010 #include <boost/context/detail/config.hpp>
0011
0012 #include <algorithm>
0013 #include <cstddef>
0014 #include <cstdint>
0015 #include <cstdlib>
0016 #include <exception>
0017 #include <functional>
0018 #include <memory>
0019 #include <ostream>
0020 #include <tuple>
0021 #include <utility>
0022
0023 #include <boost/assert.hpp>
0024 #include <boost/config.hpp>
0025 #include <boost/intrusive_ptr.hpp>
0026
0027 #if defined(BOOST_NO_CXX14_STD_EXCHANGE)
0028 #include <boost/context/detail/exchange.hpp>
0029 #endif
0030 #if defined(BOOST_NO_CXX17_STD_INVOKE)
0031 #include <boost/context/detail/invoke.hpp>
0032 #endif
0033 #include <boost/context/detail/disable_overload.hpp>
0034 #include <boost/context/detail/exception.hpp>
0035 #include <boost/context/detail/fcontext.hpp>
0036 #include <boost/context/detail/tuple.hpp>
0037 #include <boost/context/fixedsize_stack.hpp>
0038 #include <boost/context/flags.hpp>
0039 #include <boost/context/preallocated.hpp>
0040 #include <boost/context/segmented_stack.hpp>
0041 #include <boost/context/stack_context.hpp>
0042
0043 #ifdef BOOST_HAS_ABI_HEADERS
0044 # include BOOST_ABI_PREFIX
0045 #endif
0046
0047 #if defined(__CET__) && defined(__unix__)
0048 # include <cet.h>
0049 # include <sys/mman.h>
0050 # define SHSTK_ENABLED (__CET__ & 0x2)
0051 # define BOOST_CONTEXT_SHADOW_STACK (SHSTK_ENABLED && SHADOW_STACK_SYSCALL)
0052 # define __NR_map_shadow_stack 451
0053 #ifndef SHADOW_STACK_SET_TOKEN
0054 # define SHADOW_STACK_SET_TOKEN 0x1
0055 #endif
0056 #endif
0057
0058 #if defined(BOOST_MSVC)
0059 # pragma warning(push)
0060 # pragma warning(disable: 4702)
0061 #endif
0062
0063 namespace boost {
0064 namespace context {
0065 namespace detail {
0066
0067 inline
0068 transfer_t context_unwind( transfer_t t) {
0069 throw forced_unwind( t.fctx);
0070 return { nullptr, nullptr };
0071 }
0072
0073 template< typename Rec >
0074 transfer_t context_exit( transfer_t t) noexcept {
0075 Rec * rec = static_cast< Rec * >( t.data);
0076 #if BOOST_CONTEXT_SHADOW_STACK
0077
0078 std::size_t ss_size = *((unsigned long*)(reinterpret_cast< uintptr_t >( rec)- 16));
0079 long unsigned int ss_base = *((unsigned long*)(reinterpret_cast< uintptr_t >( rec)- 8));
0080 munmap((void *)ss_base, ss_size);
0081 #endif
0082
0083 rec->deallocate();
0084 return { nullptr, nullptr };
0085 }
0086
0087 template< typename Rec >
0088 void context_entry( transfer_t t) noexcept {
0089
0090 Rec * rec = static_cast< Rec * >( t.data);
0091 BOOST_ASSERT( nullptr != t.fctx);
0092 BOOST_ASSERT( nullptr != rec);
0093 try {
0094
0095 t = jump_fcontext( t.fctx, nullptr);
0096
0097 t.fctx = rec->run( t.fctx);
0098 } catch ( forced_unwind const& ex) {
0099 t = { ex.fctx, nullptr };
0100 }
0101 BOOST_ASSERT( nullptr != t.fctx);
0102
0103 ontop_fcontext( t.fctx, rec, context_exit< Rec >);
0104 BOOST_ASSERT_MSG( false, "context already terminated");
0105 }
0106
0107 template< typename Ctx, typename Fn >
0108 transfer_t context_ontop( transfer_t t) {
0109 auto p = static_cast< std::tuple< Fn > * >( t.data);
0110 BOOST_ASSERT( nullptr != p);
0111 typename std::decay< Fn >::type fn = std::get< 0 >( * p);
0112 t.data = nullptr;
0113 Ctx c{ t.fctx };
0114
0115 c = fn( std::move( c) );
0116 #if defined(BOOST_NO_CXX14_STD_EXCHANGE)
0117 return { exchange( c.fctx_, nullptr), nullptr };
0118 #else
0119 return { std::exchange( c.fctx_, nullptr), nullptr };
0120 #endif
0121 }
0122
0123 template< typename Ctx, typename StackAlloc, typename Fn >
0124 class record {
0125 private:
0126 stack_context sctx_;
0127 typename std::decay< StackAlloc >::type salloc_;
0128 typename std::decay< Fn >::type fn_;
0129
0130 static void destroy( record * p) noexcept {
0131 typename std::decay< StackAlloc >::type salloc = std::move( p->salloc_);
0132 stack_context sctx = p->sctx_;
0133
0134 p->~record();
0135
0136 salloc.deallocate( sctx);
0137 }
0138
0139 public:
0140 record( stack_context sctx, StackAlloc && salloc,
0141 Fn && fn) noexcept :
0142 sctx_( sctx),
0143 salloc_( std::forward< StackAlloc >( salloc)),
0144 fn_( std::forward< Fn >( fn) ) {
0145 }
0146
0147 record( record const&) = delete;
0148 record & operator=( record const&) = delete;
0149
0150 void deallocate() noexcept {
0151 destroy( this);
0152 }
0153
0154 fcontext_t run( fcontext_t fctx) {
0155 Ctx c{ fctx };
0156
0157 #if defined(BOOST_NO_CXX17_STD_INVOKE)
0158 c = boost::context::detail::invoke( fn_, std::move( c) );
0159 #else
0160 c = std::invoke( fn_, std::move( c) );
0161 #endif
0162 #if defined(BOOST_NO_CXX14_STD_EXCHANGE)
0163 return exchange( c.fctx_, nullptr);
0164 #else
0165 return std::exchange( c.fctx_, nullptr);
0166 #endif
0167 }
0168 };
0169
0170 template< typename Record, typename StackAlloc, typename Fn >
0171 fcontext_t create_context1( StackAlloc && salloc, Fn && fn) {
0172 auto sctx = salloc.allocate();
0173
0174 void * storage = reinterpret_cast< void * >(
0175 ( reinterpret_cast< uintptr_t >( sctx.sp) - static_cast< uintptr_t >( sizeof( Record) ) )
0176 & ~static_cast< uintptr_t >( 0xff) );
0177
0178 Record * record = new ( storage) Record{
0179 sctx, std::forward< StackAlloc >( salloc), std::forward< Fn >( fn) };
0180
0181
0182 void * stack_top = reinterpret_cast< void * >(
0183 reinterpret_cast< uintptr_t >( storage) - static_cast< uintptr_t >( 64) );
0184 void * stack_bottom = reinterpret_cast< void * >(
0185 reinterpret_cast< uintptr_t >( sctx.sp) - static_cast< uintptr_t >( sctx.size) );
0186
0187 const std::size_t size = reinterpret_cast< uintptr_t >( stack_top) - reinterpret_cast< uintptr_t >( stack_bottom);
0188
0189 #if BOOST_CONTEXT_SHADOW_STACK
0190 std::size_t ss_size = size >> 5;
0191
0192 ss_size = (ss_size + 7) & ~7;
0193
0194 ss_size = (ss_size > 4096) ? size : 4096;
0195
0196 void *ss_base = (void *)syscall(__NR_map_shadow_stack, 0, ss_size, SHADOW_STACK_SET_TOKEN);
0197 BOOST_ASSERT(ss_base != -1);
0198 unsigned long ss_sp = (unsigned long)ss_base + ss_size;
0199
0200
0201
0202 *((unsigned long*)(reinterpret_cast< uintptr_t >( stack_top)- 8)) = ss_sp;
0203
0204 *((unsigned long*)(reinterpret_cast< uintptr_t >( storage)- 8)) = (unsigned long) ss_base;
0205 *((unsigned long*)(reinterpret_cast< uintptr_t >( storage)- 16)) = ss_size;
0206 #endif
0207 const fcontext_t fctx = make_fcontext( stack_top, size, & context_entry< Record >);
0208 BOOST_ASSERT( nullptr != fctx);
0209
0210 return jump_fcontext( fctx, record).fctx;
0211 }
0212
0213 template< typename Record, typename StackAlloc, typename Fn >
0214 fcontext_t create_context2( preallocated palloc, StackAlloc && salloc, Fn && fn) {
0215
0216 void * storage = reinterpret_cast< void * >(
0217 ( reinterpret_cast< uintptr_t >( palloc.sp) - static_cast< uintptr_t >( sizeof( Record) ) )
0218 & ~ static_cast< uintptr_t >( 0xff) );
0219
0220 Record * record = new ( storage) Record{
0221 palloc.sctx, std::forward< StackAlloc >( salloc), std::forward< Fn >( fn) };
0222
0223 void * stack_top = reinterpret_cast< void * >(
0224 reinterpret_cast< uintptr_t >( storage) - static_cast< uintptr_t >( 64) );
0225 void * stack_bottom = reinterpret_cast< void * >(
0226 reinterpret_cast< uintptr_t >( palloc.sctx.sp) - static_cast< uintptr_t >( palloc.sctx.size) );
0227
0228 const std::size_t size = reinterpret_cast< uintptr_t >( stack_top) - reinterpret_cast< uintptr_t >( stack_bottom);
0229
0230 #if BOOST_CONTEXT_SHADOW_STACK
0231 std::size_t ss_size = size >> 5;
0232
0233 ss_size = (ss_size + 7) & ~7;
0234
0235 ss_size = (ss_size > 4096) ? size : 4096;
0236
0237 void *ss_base = (void *)syscall(__NR_map_shadow_stack, 0, ss_size, SHADOW_STACK_SET_TOKEN);
0238 BOOST_ASSERT(ss_base != -1);
0239 unsigned long ss_sp = (unsigned long)ss_base + ss_size;
0240
0241
0242
0243 *((unsigned long*)(reinterpret_cast< uintptr_t >( stack_top)- 8)) = ss_sp;
0244
0245 *((unsigned long*)(reinterpret_cast< uintptr_t >( storage)- 8)) = (unsigned long) ss_base;
0246 *((unsigned long*)(reinterpret_cast< uintptr_t >( storage)- 16)) = ss_size;
0247 #endif
0248 const fcontext_t fctx = make_fcontext( stack_top, size, & context_entry< Record >);
0249 BOOST_ASSERT( nullptr != fctx);
0250
0251 return jump_fcontext( fctx, record).fctx;
0252 }
0253
0254 }
0255
0256 class continuation {
0257 private:
0258 template< typename Ctx, typename StackAlloc, typename Fn >
0259 friend class detail::record;
0260
0261 template< typename Ctx, typename Fn >
0262 friend detail::transfer_t
0263 detail::context_ontop( detail::transfer_t);
0264
0265 template< typename StackAlloc, typename Fn >
0266 friend continuation
0267 callcc( std::allocator_arg_t, StackAlloc &&, Fn &&);
0268
0269 template< typename StackAlloc, typename Fn >
0270 friend continuation
0271 callcc( std::allocator_arg_t, preallocated, StackAlloc &&, Fn &&);
0272
0273 detail::fcontext_t fctx_{ nullptr };
0274
0275 continuation( detail::fcontext_t fctx) noexcept :
0276 fctx_{ fctx } {
0277 }
0278
0279 public:
0280 continuation() noexcept = default;
0281
0282 ~continuation() {
0283 if ( BOOST_UNLIKELY( nullptr != fctx_) ) {
0284 detail::ontop_fcontext(
0285 #if defined(BOOST_NO_CXX14_STD_EXCHANGE)
0286 detail::exchange( fctx_, nullptr),
0287 #else
0288 std::exchange( fctx_, nullptr),
0289 #endif
0290 nullptr,
0291 detail::context_unwind);
0292 }
0293 }
0294
0295 continuation( continuation && other) noexcept {
0296 swap( other);
0297 }
0298
0299 continuation & operator=( continuation && other) noexcept {
0300 if ( BOOST_LIKELY( this != & other) ) {
0301 continuation tmp = std::move( other);
0302 swap( tmp);
0303 }
0304 return * this;
0305 }
0306
0307 continuation( continuation const& other) noexcept = delete;
0308 continuation & operator=( continuation const& other) noexcept = delete;
0309
0310 continuation resume() & {
0311 return std::move( * this).resume();
0312 }
0313
0314 continuation resume() && {
0315 BOOST_ASSERT( nullptr != fctx_);
0316 return { detail::jump_fcontext(
0317 #if defined(BOOST_NO_CXX14_STD_EXCHANGE)
0318 detail::exchange( fctx_, nullptr),
0319 #else
0320 std::exchange( fctx_, nullptr),
0321 #endif
0322 nullptr).fctx };
0323 }
0324
0325 template< typename Fn >
0326 continuation resume_with( Fn && fn) & {
0327 return std::move( * this).resume_with( std::forward< Fn >( fn) );
0328 }
0329
0330 template< typename Fn >
0331 continuation resume_with( Fn && fn) && {
0332 BOOST_ASSERT( nullptr != fctx_);
0333 auto p = std::make_tuple( std::forward< Fn >( fn) );
0334 return { detail::ontop_fcontext(
0335 #if defined(BOOST_NO_CXX14_STD_EXCHANGE)
0336 detail::exchange( fctx_, nullptr),
0337 #else
0338 std::exchange( fctx_, nullptr),
0339 #endif
0340 & p,
0341 detail::context_ontop< continuation, Fn >).fctx };
0342 }
0343
0344 explicit operator bool() const noexcept {
0345 return nullptr != fctx_;
0346 }
0347
0348 bool operator!() const noexcept {
0349 return nullptr == fctx_;
0350 }
0351
0352 bool operator<( continuation const& other) const noexcept {
0353 return fctx_ < other.fctx_;
0354 }
0355
0356 template< typename charT, class traitsT >
0357 friend std::basic_ostream< charT, traitsT > &
0358 operator<<( std::basic_ostream< charT, traitsT > & os, continuation const& other) {
0359 if ( nullptr != other.fctx_) {
0360 return os << other.fctx_;
0361 } else {
0362 return os << "{not-a-context}";
0363 }
0364 }
0365
0366 void swap( continuation & other) noexcept {
0367 std::swap( fctx_, other.fctx_);
0368 }
0369 };
0370
0371 template<
0372 typename Fn,
0373 typename = detail::disable_overload< continuation, Fn >
0374 >
0375 continuation
0376 callcc( Fn && fn) {
0377 return callcc(
0378 std::allocator_arg, fixedsize_stack(),
0379 std::forward< Fn >( fn) );
0380 }
0381
0382 template< typename StackAlloc, typename Fn >
0383 continuation
0384 callcc( std::allocator_arg_t, StackAlloc && salloc, Fn && fn) {
0385 using Record = detail::record< continuation, StackAlloc, Fn >;
0386 return continuation{
0387 detail::create_context1< Record >(
0388 std::forward< StackAlloc >( salloc), std::forward< Fn >( fn) ) }.resume();
0389 }
0390
0391 template< typename StackAlloc, typename Fn >
0392 continuation
0393 callcc( std::allocator_arg_t, preallocated palloc, StackAlloc && salloc, Fn && fn) {
0394 using Record = detail::record< continuation, StackAlloc, Fn >;
0395 return continuation{
0396 detail::create_context2< Record >(
0397 palloc, std::forward< StackAlloc >( salloc), std::forward< Fn >( fn) ) }.resume();
0398 }
0399
0400 #if defined(BOOST_USE_SEGMENTED_STACKS)
0401 template< typename Fn >
0402 continuation
0403 callcc( std::allocator_arg_t, segmented_stack, Fn &&);
0404
0405 template< typename StackAlloc, typename Fn >
0406 continuation
0407 callcc( std::allocator_arg_t, preallocated, segmented_stack, Fn &&);
0408 #endif
0409
0410 inline
0411 void swap( continuation & l, continuation & r) noexcept {
0412 l.swap( r);
0413 }
0414
0415 }}
0416
0417 #if defined(BOOST_MSVC)
0418 # pragma warning(pop)
0419 #endif
0420
0421 #ifdef BOOST_HAS_ABI_HEADERS
0422 # include BOOST_ABI_SUFFIX
0423 #endif
0424
0425 #endif