1 
2 //          Copyright Oliver Kowalke 2017.
3 // Distributed under the Boost Software License, Version 1.0.
4 //    (See accompanying file LICENSE_1_0.txt or copy at
5 //          http://www.boost.org/LICENSE_1_0.txt)
6 
7 #ifndef BOOST_CONTEXT_CONTINUATION_H
8 #define BOOST_CONTEXT_CONTINUATION_H
9 
10 #include <boost/context/detail/config.hpp>
11 
12 #include <algorithm>
13 #include <cstddef>
14 #include <cstdint>
15 #include <cstdlib>
16 #include <exception>
17 #include <functional>
18 #include <memory>
19 #include <ostream>
20 #include <tuple>
21 #include <utility>
22 
23 #include <boost/assert.hpp>
24 #include <boost/config.hpp>
25 #include <boost/intrusive_ptr.hpp>
26 
27 #if defined(BOOST_NO_CXX14_STD_EXCHANGE)
28 #include <boost/context/detail/exchange.hpp>
29 #endif
30 #if defined(BOOST_NO_CXX17_STD_INVOKE)
31 #include <boost/context/detail/invoke.hpp>
32 #endif
33 #include <boost/context/detail/disable_overload.hpp>
34 #include <boost/context/detail/exception.hpp>
35 #include <boost/context/detail/fcontext.hpp>
36 #include <boost/context/detail/tuple.hpp>
37 #include <boost/context/fixedsize_stack.hpp>
38 #include <boost/context/flags.hpp>
39 #include <boost/context/preallocated.hpp>
40 #include <boost/context/segmented_stack.hpp>
41 #include <boost/context/stack_context.hpp>
42 
43 #ifdef BOOST_HAS_ABI_HEADERS
44 # include BOOST_ABI_PREFIX
45 #endif
46 
47 #if defined(BOOST_MSVC)
48 # pragma warning(push)
49 # pragma warning(disable: 4702)
50 #endif
51 
52 namespace boost {
53 namespace context {
54 namespace detail {
55 
56 inline
context_unwind(transfer_t t)57 transfer_t context_unwind( transfer_t t) {
58     throw forced_unwind( t.fctx);
59     return { nullptr, nullptr };
60 }
61 
62 template< typename Rec >
context_exit(transfer_t t)63 transfer_t context_exit( transfer_t t) noexcept {
64     Rec * rec = static_cast< Rec * >( t.data);
65     // destroy context stack
66     rec->deallocate();
67     return { nullptr, nullptr };
68 }
69 
70 template< typename Rec >
context_entry(transfer_t t)71 void context_entry( transfer_t t) noexcept {
72     // transfer control structure to the context-stack
73     Rec * rec = static_cast< Rec * >( t.data);
74     BOOST_ASSERT( nullptr != t.fctx);
75     BOOST_ASSERT( nullptr != rec);
76     try {
77         // jump back to `create_context()`
78         t = jump_fcontext( t.fctx, nullptr);
79         // start executing
80         t.fctx = rec->run( t.fctx);
81     } catch ( forced_unwind const& ex) {
82         t = { ex.fctx, nullptr };
83 #ifndef BOOST_ASSERT_IS_VOID
84         const_cast< forced_unwind & >( ex).caught = true;
85 #endif
86     }
87     BOOST_ASSERT( nullptr != t.fctx);
88     // destroy context-stack of `this`context on next context
89     ontop_fcontext( t.fctx, rec, context_exit< Rec >);
90     BOOST_ASSERT_MSG( false, "context already terminated");
91 }
92 
93 template< typename Ctx, typename Fn >
context_ontop(transfer_t t)94 transfer_t context_ontop( transfer_t t) {
95     auto p = static_cast< std::tuple< Fn > * >( t.data);
96     BOOST_ASSERT( nullptr != p);
97     typename std::decay< Fn >::type fn = std::get< 0 >( * p);
98     t.data = nullptr;
99     Ctx c{ t.fctx };
100     // execute function, pass continuation via reference
101     c = fn( std::move( c) );
102 #if defined(BOOST_NO_CXX14_STD_EXCHANGE)
103     return { exchange( c.fctx_, nullptr), nullptr };
104 #else
105     return { std::exchange( c.fctx_, nullptr), nullptr };
106 #endif
107 }
108 
109 template< typename Ctx, typename StackAlloc, typename Fn >
110 class record {
111 private:
112     stack_context                                       sctx_;
113     typename std::decay< StackAlloc >::type             salloc_;
114     typename std::decay< Fn >::type                     fn_;
115 
destroy(record * p)116     static void destroy( record * p) noexcept {
117         typename std::decay< StackAlloc >::type salloc = std::move( p->salloc_);
118         stack_context sctx = p->sctx_;
119         // deallocate record
120         p->~record();
121         // destroy stack with stack allocator
122         salloc.deallocate( sctx);
123     }
124 
125 public:
record(stack_context sctx,StackAlloc && salloc,Fn && fn)126     record( stack_context sctx, StackAlloc && salloc,
127             Fn && fn) noexcept :
128         sctx_( sctx),
129         salloc_( std::forward< StackAlloc >( salloc)),
130         fn_( std::forward< Fn >( fn) ) {
131     }
132 
133     record( record const&) = delete;
134     record & operator=( record const&) = delete;
135 
deallocate()136     void deallocate() noexcept {
137         destroy( this);
138     }
139 
run(fcontext_t fctx)140     fcontext_t run( fcontext_t fctx) {
141         Ctx c{ fctx };
142         // invoke context-function
143 #if defined(BOOST_NO_CXX17_STD_INVOKE)
144         c = boost::context::detail::invoke( fn_, std::move( c) );
145 #else
146         c = std::invoke( fn_, std::move( c) );
147 #endif
148 #if defined(BOOST_NO_CXX14_STD_EXCHANGE)
149         return exchange( c.fctx_, nullptr);
150 #else
151         return std::exchange( c.fctx_, nullptr);
152 #endif
153     }
154 };
155 
156 template< typename Record, typename StackAlloc, typename Fn >
create_context1(StackAlloc && salloc,Fn && fn)157 fcontext_t create_context1( StackAlloc && salloc, Fn && fn) {
158     auto sctx = salloc.allocate();
159     // reserve space for control structure
160 	void * storage = reinterpret_cast< void * >(
161 			( reinterpret_cast< uintptr_t >( sctx.sp) - static_cast< uintptr_t >( sizeof( Record) ) )
162             & ~static_cast< uintptr_t >( 0xff) );
163     // placment new for control structure on context stack
164     Record * record = new ( storage) Record{
165             sctx, std::forward< StackAlloc >( salloc), std::forward< Fn >( fn) };
166     // 64byte gab between control structure and stack top
167     // should be 16byte aligned
168     void * stack_top = reinterpret_cast< void * >(
169             reinterpret_cast< uintptr_t >( storage) - static_cast< uintptr_t >( 64) );
170     void * stack_bottom = reinterpret_cast< void * >(
171             reinterpret_cast< uintptr_t >( sctx.sp) - static_cast< uintptr_t >( sctx.size) );
172     // create fast-context
173     const std::size_t size = reinterpret_cast< uintptr_t >( stack_top) - reinterpret_cast< uintptr_t >( stack_bottom);
174     const fcontext_t fctx = make_fcontext( stack_top, size, & context_entry< Record >);
175     BOOST_ASSERT( nullptr != fctx);
176     // transfer control structure to context-stack
177     return jump_fcontext( fctx, record).fctx;
178 }
179 
180 template< typename Record, typename StackAlloc, typename Fn >
create_context2(preallocated palloc,StackAlloc && salloc,Fn && fn)181 fcontext_t create_context2( preallocated palloc, StackAlloc && salloc, Fn && fn) {
182     // reserve space for control structure
183     void * storage = reinterpret_cast< void * >(
184             ( reinterpret_cast< uintptr_t >( palloc.sp) - static_cast< uintptr_t >( sizeof( Record) ) )
185             & ~ static_cast< uintptr_t >( 0xff) );
186     // placment new for control structure on context-stack
187     Record * record = new ( storage) Record{
188             palloc.sctx, std::forward< StackAlloc >( salloc), std::forward< Fn >( fn) };
189     // 64byte gab between control structure and stack top
190     void * stack_top = reinterpret_cast< void * >(
191             reinterpret_cast< uintptr_t >( storage) - static_cast< uintptr_t >( 64) );
192     void * stack_bottom = reinterpret_cast< void * >(
193             reinterpret_cast< uintptr_t >( palloc.sctx.sp) - static_cast< uintptr_t >( palloc.sctx.size) );
194     // create fast-context
195     const std::size_t size = reinterpret_cast< uintptr_t >( stack_top) - reinterpret_cast< uintptr_t >( stack_bottom);
196     const fcontext_t fctx = make_fcontext( stack_top, size, & context_entry< Record >);
197     BOOST_ASSERT( nullptr != fctx);
198     // transfer control structure to context-stack
199     return jump_fcontext( fctx, record).fctx;
200 }
201 
202 }
203 
204 class continuation {
205 private:
206     template< typename Ctx, typename StackAlloc, typename Fn >
207     friend class detail::record;
208 
209     template< typename Ctx, typename Fn >
210     friend detail::transfer_t
211     detail::context_ontop( detail::transfer_t);
212 
213     template< typename StackAlloc, typename Fn >
214     friend continuation
215     callcc( std::allocator_arg_t, StackAlloc &&, Fn &&);
216 
217     template< typename StackAlloc, typename Fn >
218     friend continuation
219     callcc( std::allocator_arg_t, preallocated, StackAlloc &&, Fn &&);
220 
221     detail::fcontext_t  fctx_{ nullptr };
222 
continuation(detail::fcontext_t fctx)223     continuation( detail::fcontext_t fctx) noexcept :
224         fctx_{ fctx } {
225     }
226 
227 public:
228     continuation() noexcept = default;
229 
~continuation()230     ~continuation() {
231         if ( BOOST_UNLIKELY( nullptr != fctx_) ) {
232             detail::ontop_fcontext(
233 #if defined(BOOST_NO_CXX14_STD_EXCHANGE)
234                     detail::exchange( fctx_, nullptr),
235 #else
236                     std::exchange( fctx_, nullptr),
237 #endif
238                    nullptr,
239                    detail::context_unwind);
240         }
241     }
242 
continuation(continuation && other)243     continuation( continuation && other) noexcept {
244         swap( other);
245     }
246 
operator =(continuation && other)247     continuation & operator=( continuation && other) noexcept {
248         if ( BOOST_LIKELY( this != & other) ) {
249             continuation tmp = std::move( other);
250             swap( tmp);
251         }
252         return * this;
253     }
254 
255     continuation( continuation const& other) noexcept = delete;
256     continuation & operator=( continuation const& other) noexcept = delete;
257 
resume()258     continuation resume() & {
259         return std::move( * this).resume();
260     }
261 
resume()262     continuation resume() && {
263         BOOST_ASSERT( nullptr != fctx_);
264         return { detail::jump_fcontext(
265 #if defined(BOOST_NO_CXX14_STD_EXCHANGE)
266                     detail::exchange( fctx_, nullptr),
267 #else
268                     std::exchange( fctx_, nullptr),
269 #endif
270                     nullptr).fctx };
271     }
272 
273     template< typename Fn >
resume_with(Fn && fn)274     continuation resume_with( Fn && fn) & {
275         return std::move( * this).resume_with( std::forward< Fn >( fn) );
276     }
277 
278     template< typename Fn >
resume_with(Fn && fn)279     continuation resume_with( Fn && fn) && {
280         BOOST_ASSERT( nullptr != fctx_);
281         auto p = std::make_tuple( std::forward< Fn >( fn) );
282         return { detail::ontop_fcontext(
283 #if defined(BOOST_NO_CXX14_STD_EXCHANGE)
284                     detail::exchange( fctx_, nullptr),
285 #else
286                     std::exchange( fctx_, nullptr),
287 #endif
288                     & p,
289                     detail::context_ontop< continuation, Fn >).fctx };
290     }
291 
operator bool() const292     explicit operator bool() const noexcept {
293         return nullptr != fctx_;
294     }
295 
operator !() const296     bool operator!() const noexcept {
297         return nullptr == fctx_;
298     }
299 
operator <(continuation const & other) const300     bool operator<( continuation const& other) const noexcept {
301         return fctx_ < other.fctx_;
302     }
303 
304     template< typename charT, class traitsT >
305     friend std::basic_ostream< charT, traitsT > &
operator <<(std::basic_ostream<charT,traitsT> & os,continuation const & other)306     operator<<( std::basic_ostream< charT, traitsT > & os, continuation const& other) {
307         if ( nullptr != other.fctx_) {
308             return os << other.fctx_;
309         } else {
310             return os << "{not-a-context}";
311         }
312     }
313 
swap(continuation & other)314     void swap( continuation & other) noexcept {
315         std::swap( fctx_, other.fctx_);
316     }
317 };
318 
319 template<
320     typename Fn,
321     typename = detail::disable_overload< continuation, Fn >
322 >
323 continuation
callcc(Fn && fn)324 callcc( Fn && fn) {
325     return callcc(
326             std::allocator_arg, fixedsize_stack(),
327             std::forward< Fn >( fn) );
328 }
329 
330 template< typename StackAlloc, typename Fn >
331 continuation
callcc(std::allocator_arg_t,StackAlloc && salloc,Fn && fn)332 callcc( std::allocator_arg_t, StackAlloc && salloc, Fn && fn) {
333     using Record = detail::record< continuation, StackAlloc, Fn >;
334     return continuation{
335                 detail::create_context1< Record >(
336                         std::forward< StackAlloc >( salloc), std::forward< Fn >( fn) ) }.resume();
337 }
338 
339 template< typename StackAlloc, typename Fn >
340 continuation
callcc(std::allocator_arg_t,preallocated palloc,StackAlloc && salloc,Fn && fn)341 callcc( std::allocator_arg_t, preallocated palloc, StackAlloc && salloc, Fn && fn) {
342     using Record = detail::record< continuation, StackAlloc, Fn >;
343     return continuation{
344                 detail::create_context2< Record >(
345                         palloc, std::forward< StackAlloc >( salloc), std::forward< Fn >( fn) ) }.resume();
346 }
347 
348 #if defined(BOOST_USE_SEGMENTED_STACKS)
349 template< typename Fn >
350 continuation
351 callcc( std::allocator_arg_t, segmented_stack, Fn &&);
352 
353 template< typename StackAlloc, typename Fn >
354 continuation
355 callcc( std::allocator_arg_t, preallocated, segmented_stack, Fn &&);
356 #endif
357 
358 inline
swap(continuation & l,continuation & r)359 void swap( continuation & l, continuation & r) noexcept {
360     l.swap( r);
361 }
362 
363 }}
364 
365 #if defined(BOOST_MSVC)
366 # pragma warning(pop)
367 #endif
368 
369 #ifdef BOOST_HAS_ABI_HEADERS
370 # include BOOST_ABI_SUFFIX
371 #endif
372 
373 #endif // BOOST_CONTEXT_CONTINUATION_H
374