2 Copyright Oliver Kowalke 2014.
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
9 [section:ecv1 Class execution_context (version 1)]
11 [warning __econtext__ (v1) is deprecated (does not prevent UB).]
13 [note __econtext__ (v1) is the reference implementation of C++ proposal
14 [@http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/p0099r0.pdf P099R0: A
15 low-level API for stackful context switching].]
17 [note __econtext__ (v1) resides in sub-namespace `v1`.]
19 [note Segmented stacks (['segmented-stacks=on]), e.g. on demand growing stacks,
20 can be used with __econtext__ (v1).]
22 Class __econtext__ encapsulates context switching and manages the associated
23 context' stack (allocation/deallocation).
25 __econtext__ allocates the context stack (using its [link stack
26 __stack_allocator__] argument) and creates a control structure on top of it.
27 This structure is responsible for managing context' stack. Instances of
28 __econtext__, associated with a specific context, share the ownership of the
29 control structure. If the last reference goes out of scope, the control
30 structure is destroyed and the stack gets deallocated via the
33 __econtext__ is copy-constructible, move-constructible, copy-assignable and
36 __econtext__ maintains a static (thread-local) pointer, accessed by
37 __ec_current__, pointing to the active context. On each context switch the
39 The usage of this global pointer makes the context switch a little bit slower
40 (due access of thread local storage) but has some advantages. It allows to
41 access the control structure of the current active context from arbitrary code
42 paths required in order to support segmented stacks, which require to call
43 certain maintenance functions (like __splitstack_getcontext() etc.) before each
44 context switch (each context switch exchanges the stack).
46 __econtext__ expects a function/functor with signature `void(void* vp)` (`vp`
47 is the data passed at the first invocation of
48 [operator_link ecv1 operator_call operator()]).
51 [heading usage of __econtext__]
54 boost::context::v1::execution_context sink(boost::context::v1::execution_context::current());
55 boost::context::v1::execution_context source(
56 [n,&sink](void*)mutable{
66 for(int i=0;i<10;++i){
67 std::cout<<*(int*)source()<<" ";
71 0 1 1 2 3 5 8 13 21 34
73 This simple example demonstrates the basic usage of __econtext__. The context
74 `sink`, returned by __ec_current__, represents the ['main]-context (function
75 ['main()] running) and is one of the captured parameters in the lambda
76 expression. The lambda that calculates the Fibonacci numbers is executed inside
77 the context represented by `source`. Calculated Fibonacci numbers are
78 transferred between the two context' via expression ['sink(&a)] (and returned by
81 The locale variables `a`, `b` and ` next` remain their values during each
82 context switch (['yield(a)]). This is possible because `ctx` owns a stack
83 (exchanged by context switch).
85 [heading inverting the control flow]
90 * E ---> T {('+'|'-') T}
91 * T ---> S {('*'|'/') S}
92 * S ---> digit | '(' E ')'
95 // implementation omitted; see examples directory
98 std::istringstream is("1+1");
100 std::exception_ptr except;
102 // create handle to main execution context
103 auto main_ctx(boost::context::v1::execution_context::current());
104 // execute parser in new execution context
105 boost::context::v1::execution_context source(
106 [&sink,&is,&done,&except](void*){
107 // create parser with callback function
110 // resume main execution context
114 // start recursive parsing
117 // store other exceptions in exception-pointer
118 except = std::current_exception();
120 // set termination flag
122 // resume main execution context
126 // user-code pulls parsed data from parser
127 // invert control flow
130 std::rethrow_exception(except);
133 printf("Parsed: %c\n",* static_cast<char*>(vp));
136 std::rethrow_exception(except);
146 In this example a recursive descent parser uses a callback to emit a newly
147 passed symbol. Using __econtext__ the control flow can be inverted, e.g. the
148 user-code pulls parsed symbols from the parser - instead to get pushed from the
149 parser (via callback).
151 The data (character) is transferred between the two __econtext__.
153 If the code executed by __econtext__ emits an exception, the application is
154 terminated. ['std::exception_ptr] can be used to transfer exceptions between
155 different execution contexts.
157 [heading stack unwinding]
158 Sometimes it is necessary to unwind the stack of an unfinished context to
159 destroy local stack variables so they can release allocated resources (RAII
160 pattern). The user is responsible for this task.
163 [heading allocating control structures on top of stack]
164 Allocating control structures on top of the stack requires to allocated the
165 __stack_context__ and create the control structure with placement new before
166 __econtext__ is created.
167 [note The user is responsible for destructing the control structure at the top
170 // stack-allocator used for (de-)allocating stack
171 fixedsize_stack salloc( 4048);
172 // allocate stack space
173 stack_context sctx( salloc.allocate() );
174 // reserve space for control structure on top of the stack
175 void * sp = static_cast< char * >( sctx.sp) - sizeof( my_control_structure);
176 std::size_t size = sctx.size - sizeof( my_control_structure);
177 // placement new creates control structure on reserved space
178 my_control_structure * cs = new ( sp) my_control_structure( sp, size, sctx, salloc);
180 // destructing the control structure
181 cs->~my_control_structure();
183 struct my_control_structure {
185 execution_context ectx;
187 template< typename StackAllocator >
188 my_control_structure( void * sp, std::size_t size, stack_context sctx, StackAllocator salloc) :
189 // create execution context
190 ectx( std::allocator_arg, preallocated( sp, size, sctx), salloc, entry_func) {
195 [heading exception handling]
196 If the function executed inside a __econtext__ emits ans exception, the
197 application is terminated by calling ['std::terminate()]. ['std::exception_ptr]
198 can be used to transfer exceptions between different execution contexts.
200 [important Do not jump from inside a catch block and then re-throw the exception
201 in another execution context.]
203 [heading parameter passing]
204 The void pointer argument passed to __ec_op__, in one context, is passed as
205 the last argument of the __context_fn__ if the context is started for the
207 In all following invocations of __ec_op__ the void pointer passed to
208 __ec_op__, in one context, is returned by __ec_op__ in the other context.
212 std::exception_ptr excptr_;
213 boost::context::v1::execution_context caller_;
214 boost::context::v1::execution_context callee_;
219 caller_( boost::context::v1::execution_context::current() ),
220 callee_( [=] (void * vp) {
222 int i = * static_cast< int * >( vp);
223 std::string str = boost::lexical_cast<std::string>(i);
225 } catch (std::bad_cast const&) {
226 excptr_=std::current_exception();
231 std::string operator()( int i) {
232 void * ret = callee_( & i);
234 std::rethrow_exception(excptr_);
236 return * static_cast< std::string * >( ret);
241 std::cout << x( 7) << std::endl;
247 [heading Class `execution_context`]
249 class execution_context {
251 static execution_context current() noexcept;
253 template< typename Fn, typename ... Args >
254 execution_context( Fn && fn, Args && ... args);
256 template< typename StackAlloc, typename Fn, typename ... Args >
257 execution_context( std::allocator_arg_t, StackAlloc salloc, Fn && fn, Args && ... args);
259 template< typename StackAlloc, typename Fn, typename ... Args >
260 execution_context( std::allocator_arg_t, preallocated palloc, StackAlloc salloc, Fn && fn, Args && ... args);
262 execution_context( execution_context const& other) noexcept;
263 execution_context( execution_context && other) noexcept;
265 execution_context & operator=( execution_context const& other) noexcept;
266 execution_context & operator=( execution_context && other) noexcept;
268 explicit operator bool() const noexcept;
269 bool operator!() const noexcept;
271 void * operator()( void * vp = nullptr);
273 template< typename Fn >
274 void * operator()( exec_ontop_arg_t, Fn && fn, void * vp = nullptr);
276 bool operator==( execution_context const& other) const noexcept;
278 bool operator!=( execution_context const& other) const noexcept;
280 bool operator<( execution_context const& other) const noexcept;
282 bool operator>( execution_context const& other) const noexcept;
284 bool operator<=( execution_context const& other) const noexcept;
286 bool operator>=( execution_context const& other) const noexcept;
288 template< typename charT, class traitsT >
289 friend std::basic_ostream< charT, traitsT > &
290 operator<<( std::basic_ostream< charT, traitsT > & os, execution_context const& other);
293 [static_member_heading ecv1..current]
295 static execution_context current() noexcept;
298 [[Returns:] [Returns an instance of excution_context pointing to the active
300 [[Throws:] [Nothing.]]
303 [constructor_heading ecv1..constructor]
305 template< typename Fn, typename ... Args >
306 execution_context( Fn && fn, Args && ... args);
308 template< typename StackAlloc, typename Fn, typename ... Args >
309 execution_context( std::allocator_arg_t, StackAlloc salloc, Fn && fn, Args && ... args);
311 template< typename StackAlloc, typename Fn, typename ... Args >
312 execution_context( std::allocator_arg_t, preallocated palloc, StackAlloc salloc, Fn && fn, Args && ... args);
315 [[Effects:] [Creates a new execution context and prepares the context to execute
316 `fn`. `fixedsize_stack` is used as default stack allocator
317 (stack size == fixedsize_stack::traits::default_size()).
318 The constructor with argument type `preallocated`, is used to create a user
319 defined data [link ecv1_prealloc (for instance additional control structures)] on
323 [copy_constructor_heading ecv1..copy constructor]
325 execution_context( execution_context const& other) noexcept;
328 [[Effects:] [Copies `other`, e.g. underlying control structure is shared with
330 [[Throws:] [Nothing.]]
333 [move_constructor_heading ecv1..move constructor]
335 execution_context( execution_context && other) noexcept;
338 [[Effects:] [Moves underlying control structure to `*this`.]]
339 [[Throws:] [Nothing.]]
342 [copy_assignment_heading ecv1..copy assignment]
344 execution_context & operator=( execution_context const& other) noexcept;
347 [[Effects:] [Copies the state of `other` to `*this`, control structure is
349 [[Throws:] [Nothing.]]
352 [move_assignment_heading ecv1..move assignment]
354 execution_context & operator=( execution_context && other) noexcept;
357 [[Effects:] [Moves the control structure of `other` to `*this` using move
359 [[Throws:] [Nothing.]]
362 [operator_heading ecv1..operator_bool..operator bool]
364 explicit operator bool() const noexcept;
367 [[Returns:] [`true` if `*this` points to a control structure.]]
368 [[Throws:] [Nothing.]]
371 [operator_heading ecv1..operator_not..operator!]
373 bool operator!() const noexcept;
376 [[Returns:] [`true` if `*this` does not point to a control structure.]]
377 [[Throws:] [Nothing.]]
380 [operator_heading ecv1..operator_call..operator()]
382 void * operator()( void * vp = nullptr) noexcept;
385 [[Effects:] [Stores internally the current context data (stack pointer,
386 instruction pointer, and CPU registers) of the current active context and
387 restores the context data from `*this`, which implies jumping to `*this`'s
389 The void pointer argument, `vp`, is passed to the current context to be returned
390 by the most recent call to `execution_context::operator()` in the same thread.
391 `fn` is executed with arguments `args` on top of the stack of `this`.]]
392 [[Note:] [The behaviour is undefined if `operator()()` is called while
393 __ec_current__ returns `*this` (e.g. resuming an already running context). If
394 the top-level context function returns, `std::exit()` is called.]]
395 [[Returns:] [The void pointer argument passed to the most recent call to
399 [operator_heading ecv1..operator_call_ontop..operator(exec_ontop_arg_t)]
401 template< typename Fn >
402 void * operator()( exec_ontop_arg_t, Fn && fn, void * vp = nullptr);
405 [[Effects:] [Same as __ec_op__. Additionally, function `fn` is executed with
406 arguments `vp` in the context of `*this` (e.g. the stack frame of `fn` is
407 allocated on stack of `*this`).]]
408 [[Returns:] [The void pointer argument passed to the most recent call to
412 [operator_heading ecv1..operator_equal..operator==]
414 bool operator==( execution_context const& other) const noexcept;
417 [[Returns:] [`true` if `*this` and `other` represent the same execution context,
419 [[Throws:] [Nothing.]]
422 [operator_heading ecv1..operator_notequal..operator!=]
424 bool operator!=( execution_context const& other) const noexcept;
427 [[Returns:] [[`! (other == * this)]]]
428 [[Throws:] [Nothing.]]
431 [operator_heading ecv1..operator_less..operator<]
433 bool operator<( execution_context const& other) const noexcept;
436 [[Returns:] [`true` if `*this != other` is true and the
437 implementation-defined total order of `execution_context` values places `*this`
438 before `other`, false otherwise.]]
439 [[Throws:] [Nothing.]]
442 [operator_heading ecv1..operator_greater..operator>]
444 bool operator>( execution_context const& other) const noexcept;
447 [[Returns:] [`other < * this`]]
448 [[Throws:] [Nothing.]]
451 [operator_heading ecv1..operator_lesseq..operator<=]
453 bool operator<=( execution_context const& other) const noexcept;
456 [[Returns:] [`! (other < * this)`]]
457 [[Throws:] [Nothing.]]
460 [operator_heading ecv1..operator_greatereq..operator>=]
462 bool operator>=( execution_context const& other) const noexcept;
465 [[Returns:] [`! (* this < other)`]]
466 [[Throws:] [Nothing.]]
469 [hding ecv1_..Non-member function [`operator<<()]]
471 template< typename charT, class traitsT >
472 std::basic_ostream< charT, traitsT > &
473 operator<<( std::basic_ostream< charT, traitsT > & os, execution_context const& other);
476 [[Efects:] [Writes the representation of `other` to stream `os`.]]