+++ /dev/null
-[/
- Copyright Oliver Kowalke 2014.
- Distributed under the Boost Software License, Version 1.0.
- (See accompanying file LICENSE_1_0.txt or copy at
- http://www.boost.org/LICENSE_1_0.txt
-]
-
-[#ecv2]
-[section:ecv2 Class execution_context (version 2)]
-
-[note This class is enabled per default.]
-
-Class __econtext__ encapsulates context switching and manages the associated
-context' stack (allocation/deallocation).
-
-__econtext__ allocates the context stack (using its [link stack
-__stack_allocator__] argument) and creates a control structure on top of it.
-This structure is responsible for managing context' stack. The address of the
-control structure is stored in the first frame of context' stack (e.g. it can
-not directly accessed from within __econtext__). In contrast to __ecv1__ the
-ownership of the control structure is not shared (no member variable to control
-structure in __econtext__). __econtext__ keeps internally a state that is moved
-by a call of __ec_op__ (`*this` will be invalidated), e.g. after a calling
-__ec_op__, `*this` can not be used for an additional context switch.
-
-__econtext__ is only move-constructible and move-assignable.
-
-The moved state is assigned to a new instance of __econtext__. This object
-becomes the first argument of the context-function, if the context was resumed
-the first time, or the first element in a tuple returned by __ec_op__ that has
-been called in the resumed context.
-In contrast to __ecv1__, the context switch is faster because no global pointer
-etc. is involved.
-
-[important Segmented stacks are not supported by __econtext__ (v2).]
-
-On return the context-function of the current context has to specify an
-__econtext__ to which the execution control is transferred after termination
-of the current context.
-
-If an instance with valid state goes out of scope and the context-function has
-not yet returned, the stack is traversed in order to access the control
-structure (address stored at the first stack frame) and context' stack is
-deallocated via the __stack_allocator__. The stack walking makes the destruction
-of __econtext__ slow and should be prevented if possible.
-
-__econtext__ expects a __context_fn__ with signature
-`execution_context(execution_context ctx, Args ... args)`. The parameter `ctx`
-represents the context from which this context was resumed (e.g. that has called
-__ec_op__ on `*this`) and `args` are the data passed to __ec_op__. The return
-value represents the execution_context that has to be resumed, after termiantion
-of this context.
-
-Benefits of __ecv2__ over __ecv1__ are: faster context switch, type-safety of
-passed/returned arguments.
-
-
-[heading usage of __econtext__]
-
- int n=35;
- ctx::execution_context<int> source(
- [n](ctx::execution_context<int> sink, int) mutable {
- int a=0;
- int b=1;
- while(n-->0){
- auto result=sink(a);
- sink=std::move(std::get<0>(result));
- auto next=a+b;
- a=b;
- b=next;
- }
- return sink;
- });
- for(int i=0;i<10;++i){
- auto result=source(i);
- source=std::move(std::get<0>(result));
- std::cout<<std::get<1>(result)<<" ";
- }
-
- output:
- 0 1 1 2 3 5 8 13 21 34
-
-This simple example demonstrates the basic usage of __econtext__ as a generator.
-The context `sink` represents the ['main]-context (function ['main()] running).
-`sink` is generated by the framework (first element of lambda's parameter list).
-Because the state is invalidated (== changed) by each call of __ec_op__, the new
-state of the __econtext__, returned by __ec_op__, needs to be assigned to `sink`
-after each call.
-
-The lambda that calculates the Fibonacci numbers is executed inside
-the context represented by `source`. Calculated Fibonacci numbers are
-transferred between the two context' via expression ['sink(a)] (and returned by
-['source()]). Note that this example represents a ['generator] thus the value
-transferred into the lambda via ['source()] is not used. Using
-['boost::optional<>] as transferred type, might also appropriate to express this
-fact.
-
-The locale variables `a`, `b` and ` next` remain their values during each
-context switch (['yield(a)]). This is possible due `source` has its own stack
-and the stack is exchanged by each context switch.
-
-
-[heading parameter passing]
-With `execution_context<void>` no data will be transferred, only the context
-switch is executed.
-
- boost::context::execution_context<void> ctx1([](boost::context::execution_context<void> ctx2){
- std::printf("inside ctx1\n");
- return ctx2();
- });
- ctx1();
-
- output:
- inside ctx1
-
-`ctx1()` resumes `ctx1`, e.g. the lambda passed at the constructor of `ctx1` is
-entered. Argument `ctx2` represents the context that has been suspended with the
-invocation of `ctx1()`. When the lambda returns `ctx2`, context `ctx1` will be
-terminated while the context represented by `ctx2` is resumed, hence the control
-of execution returns from `ctx1()`.
-
-The arguments passed to __ec_op__, in one context, is passed as the last
-arguments of the __context_fn__ if the context is started for the first time.
-In all following invocations of __ec_op__ the arguments passed to __ec_op__, in
-one context, is returned by __ec_op__ in the other context.
-
- boost::context::execution_context<int> ctx1([](boost::context::execution_context<int> ctx2, int j){
- std::printf("inside ctx1, j == %d\n", j);
- return ctx2(j+1);
- });
- int i = 1;
- std::tie(ctx1, i) = ctx1(i);
- std::printf("i == %d\n", i);
-
- output:
- inside ctx1, j == 1
- i == 2
-
-`ctx1(i)` enters the lambda in context `ctx1` with argument `j=1`. The
-expression `ctx2(j+1)` resumes the context represented by `ctx2` and transfers
-back an integer of `j+1`. On return of `ctx1(i)`, the variable `i` contains the
-value of `j+1`.
-
-If more than one argument has to be transferred, the signature of the
-context-function is simply extended.
-
- boost::context::execution_context<int,int> ctx1([](boost::context::execution_context<int,int> ctx2, int i, int j){
- std::printf("inside ctx1, i == %d j == %d\n", i, j);
- return ctx2(i+j,i-j);
- });
- int i = 2, j = 1;
- std::tie(ctx1, i, j) = ctx1(i,j);
- std::printf("i == %d j == %d\n", i, j);
-
- output:
- inside ctx1, i == 2 j == 1
- i == 3 j == 1
-
-For use-cases, that require to transfer data of different type in each
-direction, ['boost::variant<>] could be used.
-
- class X{
- private:
- std::exception_ptr excptr_;
- boost::context::execution_context<boost::variant<int,std::string>> ctx_;
-
- public:
- X():
- excptr_(),
- ctx_(
- [=](boost::context::execution_context<boost::variant<int,std::string>> ctx, boost::variant<int,std::string> data){
- try {
- for (;;) {
- int i = boost::get<int>(data);
- data = boost::lexical_cast<std::string>(i);
- auto result = ctx( data);
- ctx = std::move( std::get<0>( result) );
- data = std::get<1>( result);
- } catch (std::bad_cast const&) {
- excptr_=std::current_exception();
- }
- return ctx;
- })
- {}
-
- std::string operator()(int i){
- boost::variant<int,std::string> data = i;
- auto result = ctx_( data);
- ctx_ = std::move( std::get<0>( result) );
- data = std::get<1>( result);
- if(excptr_){
- std::rethrow_exception(excptr_);
- }
- return boost::get<std::string>(data);
- }
- };
-
- X x;
- std::cout << x( 7) << std::endl;
-
- output:
- 7
-
-In the case of unidirectional transfer of data, ['boost::optional<>] or a
-pointer are appropriate.
-
-
-[heading exception handling]
-If the function executed inside a __econtext__ emits ans exception, the
-application is terminated by calling ['std::terminate()]. ['std::exception_ptr]
-can be used to transfer exceptions between different execution contexts.
-
-[important Do not jump from inside a catch block and then re-throw the exception
-in another execution context.]
-
-[#ecv2_ontop]
-[heading Executing function on top of a context]
-Sometimes it is useful to execute a new function on top of a resumed context.
-For this purpose __ec_op__ with first argument `exec_ontop_arg` has to be used.
-The function passed as argument must return a tuple of execution_context and
-arguments.
-
- boost::context::execution_context<int> f1(boost::context::execution_context<int> ctx,int data) {
- std::cout << "f1: entered first time: " << data << std::endl;
- std::tie(ctx,data) = ctx(data+1);
- std::cout << "f1: entered second time: " << data << std::endl;
- std::tie(ctx,data) = ctx(data+1);
- std::cout << "f1: entered third time: " << data << std::endl;
- return ctx;
- }
-
- std::tuple<boost::context::execution_context<int>,int> f2(boost::context::execution_context<int> ctx,int data) {
- std::cout << "f2: entered: " << data << std::endl;
- return std::make_tuple(std::move(ctx),-1);
- }
-
- int data = 0;
- ctx::execution_context< int > ctx(f1);
- std::tie(ctx,data) = ctx(data+1);
- std::cout << "f1: returned first time: " << data << std::endl;
- std::tie(ctx,data) = ctx(data+1);
- std::cout << "f1: returned second time: " << data << std::endl;
- std::tie(ctx,data) = ctx(ctx::exec_ontop_arg,f2,data+1);
-
- output:
- f1: entered first time: 1
- f1: returned first time: 2
- f1: entered second time: 3
- f1: returned second time: 4
- f2: entered: 5
- f1: entered third time: -1
-
-The expression `ctx(ctx::exec_ontop_arg,f2,data+1)` executes `f2()` on top of
-context `ctx`, e.g. an additional stack frame is allocated on top of the context
-stack (in front of `f1()`). `f2()` returns argument `-1` that will returned by
-the second invocation of `ctx(data+1)` in `f1()`.
-
-Another option is to execute a function on top of the context that throws an
-exception.
-
- struct interrupt {
- boost::context::execution_context< void > ctx;
-
- interrupt( boost::context::execution_context< void > && ctx_) :
- ctx( std::forward< boost::context::execution_context< void > >( ctx_) ) {
- }
- };
-
- boost::context::execution_context<void> f1(boost::context::execution_context<void> ctx) {
- try {
- for (;;) {
- std::cout << "f1()" << std::endl;
- ctx = ctx();
- }
- } catch (interrupt & e) {
- std::cout << "f1(): interrupted" << std::endl;
- ctx = std::move( e.ctx);
- }
- return ctx;
- }
-
- boost::context::execution_context<void> f2(boost::context::execution_context<void> ctx) {
- throw interrupt(std::move(ctx));
- return ctx;
- }
-
- boost::context::execution_context< void > ctx(f1);
- ctx = ctx();
- ctx = ctx();
- ctx = ctx(boost::context::exec_ontop_arg,f2);
-
- output:
- f1()
- f1()
- f1(): interrupted
-
-In this example `f2()` is used to interrupt the `for`-loop in `f1()`.
-
-
-[heading stack unwinding]
-On construction of __econtext__ a stack is allocated.
-If the __context_fn__ returns the stack will be destructed.
-If the __context_fn__ has not yet returned and the destructor of an valid
-__econtext__ instance (e.g. ['execution_context::operator bool()] returns
-`true`) is called, the stack will be destructed too.
-
-[important Code executed by __context_fn__ must not prevent the propagation of the
-__forced_unwind__ exception. Absorbing that exception will cause stack
-unwinding to fail. Thus, any code that catches all exceptions must re-throw any
-pending __forced_unwind__ exception.]
-
-
-[#ecv2_prealloc]
-[heading allocating control structures on top of stack]
-Allocating control structures on top of the stack requires to allocated the
-__stack_context__ and create the control structure with placement new before
-__econtext__ is created.
-[note The user is responsible for destructing the control structure at the top
-of the stack.]
-
- // stack-allocator used for (de-)allocating stack
- fixedsize_stack salloc( 4048);
- // allocate stack space
- stack_context sctx( salloc.allocate() );
- // reserve space for control structure on top of the stack
- void * sp = static_cast< char * >( sctx.sp) - sizeof( my_control_structure);
- std::size_t size = sctx.size - sizeof( my_control_structure);
- // placement new creates control structure on reserved space
- my_control_structure * cs = new ( sp) my_control_structure( sp, size, sctx, salloc);
- ...
- // destructing the control structure
- cs->~my_control_structure();
- ...
- struct my_control_structure {
- // captured context
- execution_context cctx;
-
- template< typename StackAllocator >
- my_control_structure( void * sp, std::size_t size, stack_context sctx, StackAllocator salloc) :
- // create captured context
- cctx( std::allocator_arg, preallocated( sp, size, sctx), salloc, entry_func) {
- }
- ...
- };
-
-
-[heading inverting the control flow]
-
- /*
- * grammar:
- * P ---> E '\0'
- * E ---> T {('+'|'-') T}
- * T ---> S {('*'|'/') S}
- * S ---> digit | '(' E ')'
- */
- class Parser{
- // implementation omitted; see examples directory
- };
-
- std::istringstream is("1+1");
- bool done=false;
- std::exception_ptr except;
-
- // execute parser in new execution context
- boost::context::execution_context<char> source(
- [&is,&done,&except](ctx::execution_context<char> sink,char){
- // create parser with callback function
- Parser p( is,
- [&sink](char ch){
- // resume main execution context
- auto result = sink(ch);
- sink = std::move(std::get<0>(result));
- });
- try {
- // start recursive parsing
- p.run();
- } catch (...) {
- // store other exceptions in exception-pointer
- except = std::current_exception();
- }
- // set termination flag
- done=true;
- // resume main execution context
- return sink;
- });
-
- // user-code pulls parsed data from parser
- // invert control flow
- auto result = source('\0');
- source = std::move(std::get<0>(result));
- char c = std::get<1>(result);
- if ( except) {
- std::rethrow_exception(except);
- }
- while( ! done) {
- printf("Parsed: %c\n",c);
- std::tie(source,c) = source('\0');
- if (except) {
- std::rethrow_exception(except);
- }
- }
-
- output:
- Parsed: 1
- Parsed: +
- Parsed: 1
-
-In this example a recursive descent parser uses a callback to emit a newly
-passed symbol. Using __econtext__ the control flow can be inverted, e.g. the
-user-code pulls parsed symbols from the parser - instead to get pushed from the
-parser (via callback).
-
-The data (character) is transferred between the two __econtext__.
-
-If the code executed by __econtext__ emits an exception, the application is
-terminated. ['std::exception_ptr] can be used to transfer exceptions between
-different execution contexts.
-
-Sometimes it is necessary to unwind the stack of an unfinished context to
-destroy local stack variables so they can release allocated resources (RAII
-pattern). The user is responsible for this task.
-
-
-[heading Class `execution_context`]
-
- struct exec_ontop_arg_t {};
- const exec_ontop_arg_t exec_ontop_arg{};
-
- template< typename ... Args >
- class execution_context {
- public:
- template< typename Fn, typename ... Params >
- execution_context( Fn && fn, Params && ... params);
-
- template< typename StackAlloc, typename Fn, typename ... Params >
- execution_context( std::allocator_arg_t, StackAlloc salloc, Fn && fn, Params && ... params);
-
- template< typename StackAlloc, typename Fn, typename ... Params >
- execution_context( std::allocator_arg_t, preallocated palloc, StackAlloc salloc, Fn && fn, Params && ... params);
-
- template< typename Fn, typename ... Params >
- execution_context( std::allocator_arg_t, segemented_stack, Fn && fn, Params && ... params) = delete;
-
- template< typename Fn, typename ... Params >
- execution_context( std::allocator_arg_t, preallocated palloc, segmented, Fn && fn, Params && ... params)= delete;
-
- ~execution_context();
-
- execution_context( execution_context && other) noexcept;
- execution_context & operator=( execution_context && other) noexcept;
-
- execution_context( execution_context const& other) noexcept = delete;
- execution_context & operator=( execution_context const& other) noexcept = delete;
-
- explicit operator bool() const noexcept;
- bool operator!() const noexcept;
-
- std::tuple< execution_context, Args ... > operator()( Args ... args);
-
- template< typename Fn >
- std::tuple< execution_context, Args ... > operator()( exec_ontop_arg_t, Fn && fn, Args ... args);
-
- bool operator==( execution_context const& other) const noexcept;
-
- bool operator!=( execution_context const& other) const noexcept;
-
- bool operator<( execution_context const& other) const noexcept;
-
- bool operator>( execution_context const& other) const noexcept;
-
- bool operator<=( execution_context const& other) const noexcept;
-
- bool operator>=( execution_context const& other) const noexcept;
-
- template< typename charT, class traitsT >
- friend std::basic_ostream< charT, traitsT > &
- operator<<( std::basic_ostream< charT, traitsT > & os, execution_context const& other);
- };
-
-[constructor_heading ecv2..constructor]
-
- template< typename Fn, typename ... Params >
- execution_context( Fn && fn, Params && ... params);
-
- template< typename StackAlloc, typename Fn, typename ... Params >
- execution_context( std::allocator_arg_t, StackAlloc salloc, Fn && fn, Params && ... params);
-
- template< typename StackAlloc, typename Fn, typename ... Params >
- execution_context( std::allocator_arg_t, preallocated palloc, StackAlloc salloc, Fn && fn, Params && ... params);
-
-[variablelist
-[[Effects:] [Creates a new execution context and prepares the context to execute
-`fn`. `fixedsize_stack` is used as default stack allocator
-(stack size == fixedsize_stack::traits::default_size()).
-The constructor with argument type `preallocated`, is used to create a user
-defined data [link ecv2_prealloc (for instance additional control structures)] on
-top of the stack.]]
-]
-[destructor_heading ecv2..destructor destructor]
-
- ~execution_context();
-
-[variablelist
-[[Effects:] [Destructs the associated stack if `*this` is a valid context,
-e.g. ['execution_context::operator bool()] returns `true`.]]
-[[Throws:] [Nothing.]]
-]
-
-[move_constructor_heading ecv2..move constructor]
-
- execution_context( execution_context && other) noexcept;
-
-[variablelist
-[[Effects:] [Moves underlying capture record to `*this`.]]
-[[Throws:] [Nothing.]]
-]
-
-[move_assignment_heading ecv2..move assignment]
-
- execution_context & operator=( execution_context && other) noexcept;
-
-[variablelist
-[[Effects:] [Moves the state of `other` to `*this` using move semantics.]]
-[[Throws:] [Nothing.]]
-]
-
-[operator_heading ecv2..operator_bool..operator bool]
-
- explicit operator bool() const noexcept;
-
-[variablelist
-[[Returns:] [`true` if `*this` points to a capture record.]]
-[[Throws:] [Nothing.]]
-]
-
-[operator_heading ecv2..operator_not..operator!]
-
- bool operator!() const noexcept;
-
-[variablelist
-[[Returns:] [`true` if `*this` does not point to a capture record.]]
-[[Throws:] [Nothing.]]
-]
-
-[operator_heading ecv2..operator_call..operator()]
-
- std::tuple< execution_context< Args ... >, Args ... > operator()( Args ... args); // member of generic execution_context template
-
- execution_context< void > operator()(); // member of execution_context< void >
-
-[variablelist
-[[Effects:] [Stores internally the current context data (stack pointer,
-instruction pointer, and CPU registers) of the current active context and
-restores the context data from `*this`, which implies jumping to `*this`'s
-context.
-The arguments, `... args`, are passed to the current context to be returned
-by the most recent call to `execution_context::operator()` in the same thread.]]
-[[Returns:] [The tuple of execution_context and returned arguments passed to the
-most recent call to `execution_context::operator()`, if any and a
-execution_context representing the context that has been suspended.]]
-[[Note:] [The returned execution_context indicates if the suspended context has
-terminated (return from context-function) via `bool operator()`. If the returned
-execution_context has terminated no data are transferred in the returned tuple.]]
-]
-
-[operator_heading ecv2..operator_call_ontop..operator()]
-
- template< typename Fn >
- std::tuple< execution_context< Args ... >, Args ... > operator()( exec_ontop_arg_t, Fn && fn, Args ... args); // member of generic execution_context
-
- template< typename Fn >
- execution_context< void > operator()( exec_ontop_arg_t, Fn && fn); // member of execution_context< void >
-
-[variablelist
-[[Effects:] [Same as __ec_op__. Additionally, function `fn` is executed
-in the context of `*this` (e.g. the stack frame of `fn` is allocated on
-stack of `*this`).]]
-[[Returns:] [The tuple of execution_context and returned arguments passed to the
-most recent call to `execution_context::operator()`, if any and a
-execution_context representing the context that has been suspended .]]
-[[Note:] [The tuple of execution_context and returned arguments from `fn` are
-passed as arguments to the context-function of resumed context (if the context
-is entered the first time) or those arguments are returned from
-`execution_context::operator()` within the resumed context.]]
-[[Note:] [Function `fn` needs to return a tuple of execution_context and
-arguments ([link ecv2_ontop see description]).]]
-[[Note:] [The context calling this function must not be destroyed before the
-arguments, that will be returned from `fn`, are preserved at least in the stack
-frame of the resumed context.]]
-[[Note:] [The returned execution_context indicates if the suspended context has
-terminated (return from context-function) via `bool operator()`. If the returned
-execution_context has terminated no data are transferred in the returned tuple.]]
-]
-
-[operator_heading ecv2..operator_equal..operator==]
-
- bool operator==( execution_context const& other) const noexcept;
-
-[variablelist
-[[Returns:] [`true` if `*this` and `other` represent the same execution context,
-`false` otherwise.]]
-[[Throws:] [Nothing.]]
-]
-
-[operator_heading ecv2..operator_notequal..operator!=]
-
- bool operator!=( execution_context const& other) const noexcept;
-
-[variablelist
-[[Returns:] [[`! (other == * this)]]]
-[[Throws:] [Nothing.]]
-]
-
-[operator_heading ecv2..operator_less..operator<]
-
- bool operator<( execution_context const& other) const noexcept;
-
-[variablelist
-[[Returns:] [`true` if `*this != other` is true and the
-implementation-defined total order of `execution_context` values places `*this`
-before `other`, false otherwise.]]
-[[Throws:] [Nothing.]]
-]
-
-[operator_heading ecv2..operator_greater..operator>]
-
- bool operator>( execution_context const& other) const noexcept;
-
-[variablelist
-[[Returns:] [`other < * this`]]
-[[Throws:] [Nothing.]]
-]
-
-[operator_heading ecv2..operator_lesseq..operator<=]
-
- bool operator<=( execution_context const& other) const noexcept;
-
-[variablelist
-[[Returns:] [`! (other < * this)`]]
-[[Throws:] [Nothing.]]
-]
-
-[operator_heading ecv2..operator_greatereq..operator>=]
-
- bool operator>=( execution_context const& other) const noexcept;
-
-[variablelist
-[[Returns:] [`! (* this < other)`]]
-[[Throws:] [Nothing.]]
-]
-
-[hding ecv2_..Non-member function [`operator<<()]]
-
- template< typename charT, class traitsT >
- std::basic_ostream< charT, traitsT > &
- operator<<( std::basic_ostream< charT, traitsT > & os, execution_context const& other);
-
-[variablelist
-[[Efects:] [Writes the representation of `other` to stream `os`.]]
-[[Returns:] [`os`]]
-]
-
-
-[endsect]