diff options
Diffstat (limited to 'libs/context/doc/execution_context.qbk')
-rw-r--r-- | libs/context/doc/execution_context.qbk | 268 |
1 files changed, 268 insertions, 0 deletions
diff --git a/libs/context/doc/execution_context.qbk b/libs/context/doc/execution_context.qbk new file mode 100644 index 000000000..81535f7aa --- /dev/null +++ b/libs/context/doc/execution_context.qbk @@ -0,0 +1,268 @@ +[/ + 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 +] + +[section:econtext Class execution_context] + +[important __econtext__ is supported only by C++14.] + +Class __econtext__ encapsulates __fcontext__ and related functions ( +__jump_fcontext__ and __make_fcontext__) as well as stack management. +__econtext__ permits access to the current, active context via +`execution_context::current()`. + + /* + * grammar: + * P ---> E '\0' + * E ---> T {('+'|'-') T} + * T ---> S {('*'|'/') S} + * S ---> digit | '(' E ')' + */ + class Parser{ + // implementation omitted; see examples directory + }; + + int main() { + std::istringstream is("1+1"); + bool done=false; + char c; + + // create handle to main execution context + boost::context::execution_context main_ctx( + boost::context::execution_context::current() ); + + // executes parser in new execution context + boost::context::execution_context parser_ctx( + boost::context::fixedsize_stack(), + [&main_ctx,&is,&c,&done](){ + // create parser with callback function + Parser p(is, + [&main_ctx,&c](char ch){ + c=ch; + // resume main execution context + main_ctx.resume(); + }); + // start recursive parsing + p.run(); + done=true; + // return to main execution context + main_ctx.resume(); + }); + + // user-code pulls parsed data from parser + // inverted control flow + parser_ctx.resume(); + do { + printf("Parsed: %c\n",c); + parser_ctx.resume(); + } while( ! done); + } + + 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 interface of __econtext__ does not transfer data. This is not required +because usually sharing data's address (pointer/reference) sufficient. + +If the code executed by __econtext__ emits an exception, `std::terminate()` will +be called. + +[important Do not jump from inside a catch block and than re-throw exceptions in +another execution context.] + +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 allocating control strutures 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-alloctor 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 { + // execution context + execution_context ectx; + + template< typename StackAllocator > + my_control_structure( void * sp, std::size_t size, stack_context sctx, StackAllocator salloc) : + // create execution context + ectx( preallocated( sp, size, sctx), salloc, entry_func) { + } + ... + }; + +[heading exception handling] +If the function executed inside a __econtext__ emitts ans exception, `std::terminate()` is +called - `std::exception_ptr` can used to store exceptions thrown inside the other context. + +[heading parameter passing] +Input and output parameters are transfered via a lambda capture list and references/pointers. + + class X { + private: + int * inp_; + std::string outp_; + std::exception_ptr excptr_; + boost::context::execution_context caller_; + boost::context::execution_context callee_; + + public: + X() : + inp_( nullptr), + outp_(), + excptr_(), + caller_( boost::context::execution_context::current() ), + callee_( boost::context::fixedsize_stack(), + [=] () { + try { + int i = * inp_; + outp_ = boost::lexical_cast< std::string >( i); + caller_.resume(); + } catch (...) { + excptr_ = std::current_exception(); + } + }) + {} + + std::string operator()( int i) { + inp_ = & i; + callee_.resume(); + if ( excptr_) { + std::rethrow_exception( excptr_); + } + return outp_; + } + }; + + int main() { + X x; + std::cout << x( 7) << std::endl; + std::cout << "done" << std::endl; + } + + +[heading Class `execution_context`] + + class execution_context { + public: + static execution_context current() noexcept; + + template< typename StackAlloc, typename Fn > + execution_context( StackAlloc salloc, Fn && fn); + + template< typename StackAlloc, typename Fn, typename ... Args > + execution_context( StackAlloc salloc, Fn && fn, Args && ... args); + + template< typename StackAlloc, typename Fn > + execution_context( preallocated palloc, StackAlloc salloc, Fn && fn); + + template< typename StackAlloc, typename Fn, typename ... Args > + execution_context( preallocated palloc, StackAlloc salloc, Fn && fn, Args && ... args); + + void resume() noexcept; + + explicit operator bool() const noexcept; + + bool operator!() const noexcept; + }; + +[heading `static execution_context current()`] +[variablelist +[[Returns:] [Returns an instance of excution_context pointing to the active +execution context.]] +[[Throws:] [Nothing.]] +] + +[heading `template< typename StackAlloc, typname Fn > execution_context( StackAlloc salloc, Fn && fn)`] +[variablelist +[[Effects:] [Creates a new execution context and prepares the context to execute +`fn`.]] +] + +[heading `template< typename StackAlloc, typname Fn, typename ... Args > execution_context( StackAlloc salloc, Fn && fn, Args && ... args)`] +[variablelist +[[Effects:] [Creates a new execution context and prepares the context to execute +`fn`.]] +] + +[heading `template< typename StackAlloc, typname Fn > execution_context( preallocated palloc, StackAlloc salloc, Fn && fn)`] +[variablelist +[[Effects:] [Creates a new execution context and prepares the context to execute +`fn`. Used to store control structures on top of the stack.]] +] + +[heading `template< typename StackAlloc, typname Fn, typename ... Args > execution_context( preallocated palloc, StackAlloc salloc, Fn && fn, Args && ... args)`] +[variablelist +[[Effects:] [Creates a new execution context and prepares the context to execute +`fn`. Used to store control structures on top of the stack.]] +] + +[heading `void resume()`] +[variablelist +[[Effects:] [Stores internally the current context data (stack pointer, +instruction pointer, and CPU registers) to the current active context and +restores the context data from `*this`, which implies jumping to `*this`'s +execution context.]] +[[Note:] [The behaviour is undefined if `resume()` is called while `execution_context::current()` +returns `*this` (e.g. resuming an alredy running cotnext).]] +[[Throws:] [Nothing.]] +] + +[heading `explicit operator bool() const`] +[variablelist +[[Returns:] [If `*this` refers to an invalid context or the context-function +has returned (completed), the function returns `false`. Otherwise `true`.]] +[[Throws:] [Nothing.]] +] + +[heading `bool operator!() const`] +[variablelist +[[Returns:] [If `*this` refers to an invalid context or the context-function +has returned (completed), the function returns `true`. Otherwise `false`.]] +[[Throws:] [Nothing.]] +] + + +[heading Struct `preallocated`] + + struct preallocated { + void * sp; + std::size_t size; + stack_context sctx; + + preallocated( void * sp, std:size_t size, stack_allocator sctx) noexcept; + }; + +[heading `preallocated( void * sp, std:size_t size, stack_allocator sctx)`] +[variablelist +[[Effects:] [Crreates an object of preallocated.]] +] + + +[endsect] |