From 1bf9f6dfdb8068f82a04eb05fc43b842d5567cee Mon Sep 17 00:00:00 2001
From: Oliver Kowalke
Date: Tue, 12 Jan 2016 20:29:58 +0100
Subject: [PATCH] documentation updated
---
doc/captured_context.qbk | 410 ++++
doc/context.qbk | 92 +-
doc/context.xml | 1658 ++++++++++-------
doc/execution_context.qbk | 237 ++-
doc/fcontext.qbk | 4 +-
doc/html/context/abstract_context.html | 72 +
.../context/abstract_context/ccontext.html | 739 ++++++++
.../context/abstract_context/econtext.html | 737 ++++++++
doc/html/context/abstract_context/stack.html | 188 ++
.../abstract_context/stack/fixedsize.html | 113 ++
.../stack/protected_fixedsize.html | 136 ++
.../abstract_context/stack/segmented.html | 139 ++
.../abstract_context/stack/stack_context.html | 85 +
.../abstract_context/stack/stack_traits.html | 159 ++
.../abstract_context/stack/valgrind.html | 49 +
doc/html/context/ccontext.html | 707 +++++++
doc/html/context/context.html | 75 +-
doc/html/context/econtext.html | 465 +++--
doc/html/context/overview.html | 34 +-
doc/html/context/performance.html | 67 +-
doc/html/context/requirements.html | 6 +-
doc/html/context/stack.html | 8 +-
doc/html/context/stack/valgrind.html | 6 +-
doc/html/context/struct__preallocated_.html | 66 +
doc/html/context_HTML.manifest | 4 +-
doc/html/index.html | 6 +-
doc/overview.qbk | 21 +-
doc/performance.qbk | 14 +-
doc/preallocated.qbk | 27 +
doc/stack.qbk | 1 +
30 files changed, 5243 insertions(+), 1082 deletions(-)
create mode 100644 doc/captured_context.qbk
create mode 100644 doc/html/context/abstract_context.html
create mode 100644 doc/html/context/abstract_context/ccontext.html
create mode 100644 doc/html/context/abstract_context/econtext.html
create mode 100644 doc/html/context/abstract_context/stack.html
create mode 100644 doc/html/context/abstract_context/stack/fixedsize.html
create mode 100644 doc/html/context/abstract_context/stack/protected_fixedsize.html
create mode 100644 doc/html/context/abstract_context/stack/segmented.html
create mode 100644 doc/html/context/abstract_context/stack/stack_context.html
create mode 100644 doc/html/context/abstract_context/stack/stack_traits.html
create mode 100644 doc/html/context/abstract_context/stack/valgrind.html
create mode 100644 doc/html/context/ccontext.html
create mode 100644 doc/html/context/struct__preallocated_.html
create mode 100644 doc/preallocated.qbk
diff --git a/doc/captured_context.qbk b/doc/captured_context.qbk
new file mode 100644
index 0000000..ad69bf7
--- /dev/null
+++ b/doc/captured_context.qbk
@@ -0,0 +1,410 @@
+[/
+ 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
+]
+
+[#ccontext]
+[section:ccontext Class captured_context]
+
+Class __ccontext__ encapsulates __fcontext__ and manages the context' stack
+(allocation/deallocation).
+
+__ccontext__ allocates the context stack (using its __stack_allocator__
+argument) and creates a control structure on top of it. This structure
+controls the life time of the stack. The address of the control structure is
+stored in the first frame of context' stack (e.g. it can not accessed by
+instances of __ccontext__ directly). In contrast to __econtext__ the ownership
+of the control structure is not shared
+A call of__cc_op__ enters the context represented by `*this` and invalidates
+`*this`. The context that has been suspended by calling __cc_op__ is passed to
+the resumed context, e.g. as argument of the context-function if the context was
+resumed the first time or returned by __cc_op__.
+__ccontext__ is only move-constructible and move-assignable.
+If the last reference (__ccontext__) goes out of scope, the control structure
+is destroyed and the stack gets deallocated via the __stack_allocator__.
+__ccontext__ maintains a static, thread-local pointer (smart pointer),
+accessed by __ec_current__, pointing to the active context.
+On each context switch the static, thread-local pointer is updated. This makes
+the context switch a little bit slower, but enables faster context destruction
+(stack unwinding) compared to __ccontext__.
+
+__ccontext__ expects a function/functor with signature
+`captured_context( captured_context ctx, void * vp)`. The parameter `ctx`
+represents the context from which this context was resumed (e.g. that has called
+__cc_op__ on `*this`) and `vp` is the data passed to __cc_op__. The
+function/functor has to return the captured_context that has to be resumed,
+while this context terminates.
+
+[important Segemnted stacks are not supported together with __ccontext__.]
+
+
+[heading usage of __ccontext__]
+
+ /*
+ * 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::captured_context pctx(
+ [&is,&done,&except](ctx::captured_context mctx,void* ignored){
+ // create parser with callback function
+ Parser p( is,
+ [&mctx](char ch){
+ // resume main execution context
+ auto result = mctx( & ch);
+ mctx = 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 mctx;
+ });
+
+ // user-code pulls parsed data from parser
+ // invert control flow
+ auto result = pctx();
+ pctx = std::move( std::get<0>( result) );
+ void * vp = std::get<1>( result);
+ if ( except) {
+ std::rethrow_exception( except);
+ }
+ while( ! done) {
+ printf("Parsed: %c\n",* static_cast< char* >( vp) );
+ std::tie(pctx,vp) = pctx();
+ 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 __ccontext__ 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 __ccontext__.
+
+If the code executed by __ccontext__ 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 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
+__ccontext__ 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
+ captured_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 exception handling]
+If the function executed inside a __ccontext__ emits ans exception, the
+application is terminated by calling ['std::terminate(). ['std::exception_ptr]
+can be used to transfer exceptions between different execution contexts.
+
+[heading parameter passing]
+The void pointer argument passed to __cc_op__, in one context, is passed as
+the last argument of the __context_fn__ if the context is started for the
+first time.
+In all following invocations of __cc_op__ the void pointer passed to
+__cc_op__, in one context, is returned by __cc_op__ in the other context.
+
+ class X {
+ private:
+ std::exception_ptr excptr_;
+ boost::context::captured_context ctx_;
+
+ public:
+ X() :
+ excptr_(),
+ ctx_( [=](ctx::captured_context ctx, void * vp)->ctx::captured_context{
+ try {
+ for (;;) {
+ int i = * static_cast< int * >( vp);
+ std::string str = boost::lexical_cast(i);
+ auto result = ctx( & str);
+ ctx = std::move( std::get<0>( result) );
+ vp = std::get<1>( result);
+ }
+ } catch ( ctx::detail::forced_unwind const&) {
+ throw;
+ } catch (...) {
+ excptr_=std::current_exception();
+ }
+ return ctx;
+ })
+ {}
+
+ std::string operator()( int i) {
+ auto result = ctx_( & i);
+ ctx_ = std::move( std::get<0>( result) );
+ void * ret = std::get<1>( result);
+ if(excptr_){
+ std::rethrow_exception(excptr_);
+ }
+ return * static_cast< std::string * >( ret);
+ }
+ };
+
+ X x;
+ std::cout << x( 7) << std::endl;
+
+ output:
+ 7
+
+
+[heading Class `captured_context`]
+
+ class captured_context {
+ public:
+ template< typename Fn, typename ... Args >
+ captured_context( Fn && fn, Args && ... args);
+
+ template< typename StackAlloc, typename Fn, typename ... Args >
+ captured_context( std::allocator_arg_t, StackAlloc salloc, Fn && fn, Args && ... args);
+
+ template< typename StackAlloc, typename Fn, typename ... Args >
+ captured_context( std::allocator_arg_t, preallocated palloc, StackAlloc salloc, Fn && fn, Args && ... args);
+
+ ~captured_context();
+
+ captured_context( captured_context && other) noexcept;
+ captured_context & operator=( captured_context && other) noexcept;
+
+ captured_context( captured_context const& other) noexcept = delete;
+ captured_context & operator=( captured_context const& other) noexcept = delete;
+
+ explicit operator bool() const noexcept;
+ bool operator!() const noexcept;
+
+ std::tuple< captured_context, void * > operator()( void * data = nullptr);
+
+ template< typename Fn, typename ... Args >
+ std::tuple< captured_context, void * > operator()( exec_ontop_arg_t, Fn && fn, Args && ... args);
+
+ template< typename Fn, typename ... Args >
+ std::tuple< captured_context, void * > operator()( void * data, exec_ontop_arg_t, Fn && fn, Args && ... args);
+
+ bool operator==( captured_context const& other) const noexcept;
+
+ bool operator!=( captured_context const& other) const noexcept;
+
+ bool operator<( captured_context const& other) const noexcept;
+
+ bool operator>( captured_context const& other) const noexcept;
+
+ bool operator<=( captured_context const& other) const noexcept;
+
+ bool operator>=( captured_context const& other) const noexcept;
+
+ template< typename charT, class traitsT >
+ friend std::basic_ostream< charT, traitsT > &
+ operator<<( std::basic_ostream< charT, traitsT > & os, captured_context const& other);
+ };
+
+[constructor_heading captured_context..constructor]
+
+ template< typename Fn, typename ... Args >
+ captured_context( Fn && fn, Args && ... args);
+
+ template< typename StackAlloc, typename Fn, typename ... Args >
+ captured_context( std::allocator_arg_t, StackAlloc salloc, Fn && fn, Args && ... args);
+
+ template< typename StackAlloc, typename Fn, typename ... Args >
+ captured_context( std::allocator_arg_t, preallocated palloc, StackAlloc salloc, Fn && fn, Args && ... args);
+
+[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().]]
+]
+[[Effects:] [Creates a new execution context and prepares the context to execute
+`fn`.]]
+]
+[[Effects:] [Creates a new execution context and prepares the context to execute
+`fn`. Used to store control structures on top of the stack.]]
+]
+
+[move_constructor_heading captured_context..move constructor]
+
+ captured_context( captured_context && other) noexcept;
+
+[variablelist
+[[Effects:] [Moves underlying capture record to `*this`.]]
+[[Throws:] [Nothing.]]
+]
+
+[move_assignment_heading captured_context..move assignment]
+
+ captured_context & operator=( captured_context && other) noexcept;
+
+[variablelist
+[[Effects:] [Moves the state of `other` to `*this` using move semantics.]]
+[[Throws:] [Nothing.]]
+]
+
+[operator_heading captured_context..operator_bool..operator bool]
+
+ explicit operator bool() const noexcept;
+
+[variablelist
+[[Returns:] [`true` if `*this` points to a capture record.]]
+[[Throws:] [Nothing.]]
+]
+
+[operator_heading captured_context..operator_not..operator!]
+
+ bool operator!() const noexcept;
+
+[variablelist
+[[Returns:] [`true` if `*this` does not point to a capture record.]]
+[[Throws:] [Nothing.]]
+]
+
+[operator_heading captured_context..operator_call..operator()]
+
+ std::tuple< captured_context, void * > operator()( void * data = nullptr);
+
+ template< typename Fn, typename ... Args >
+ std::tuple< captured_context, void * > operator()( exec_ontop_arg_t, Fn && fn, Args && ... args);
+
+ template< typename Fn, typename ... Args >
+ std::tuple< captured_context, void * > operator()( void * data, exec_ontop_arg_t, Fn && fn, Args && ... args);
+
+[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 void pointer argument, `vp`, is passed to the current context to be returned
+by the most recent call to `captured_context::operator()` in the same thread.
+`fn` is executed with arguments `args` on top of the stack of `this`.
+[[Note:] [The behaviour is undefined if `operator()()` is called while `captured_context::current()`
+returns `*this` (e.g. resuming an already running context). If the top-level context
+function returns, `std::exit()` is called.]]
+[[Returns:] [The tuple of void pointer argument passed to the most recent call to
+`captured_context::operator()`, if any and a captured_context representing the context
+that has been suspended .]]
+]
+
+[operator_heading captured_context..operator_equal..operator==]
+
+ bool operator==( captured_context const& other) const noexcept;
+
+[variablelist
+[[Returns:] [`true` if `*this` and `other` represent the same execution context,
+`false` otherwise.]]
+[[Throws:] [Nothing.]]
+]
+
+[operator_heading captured_context..operator_notequal..operator!=]
+
+ bool operator!=( captured_context const& other) const noexcept;
+
+[variablelist
+[[Returns:] [[`! (other == * this)]]]
+[[Throws:] [Nothing.]]
+]
+
+[operator_heading captured_context..operator_less..operator<]
+
+ bool operator<( captured_context const& other) const noexcept;
+
+[variablelist
+[[Returns:] [`true` if `*this != other` is true and the
+implementation-defined total order of `captured_context` values places `*this` before
+`other`, false otherwise.]]
+[[Throws:] [Nothing.]]
+]
+
+[operator_heading captured_context..operator_greater..operator>]
+
+ bool operator>( captured_context const& other) const noexcept;
+
+[variablelist
+[[Returns:] [`other < * this`]]
+[[Throws:] [Nothing.]]
+]
+
+[operator_heading captured_context..operator_lesseq..operator<=]
+
+ bool operator<=( captured_context const& other) const noexcept;
+
+[variablelist
+[[Returns:] [`! (other < * this)`]]
+[[Throws:] [Nothing.]]
+]
+
+[operator_heading captured_context..operator_greatereq..operator>=]
+
+ bool operator>=( captured_context const& other) const noexcept;
+
+[variablelist
+[[Returns:] [`! (* this < other)`]]
+[[Throws:] [Nothing.]]
+]
+
+[hding captured_context..Non-member function [`operator<<()]]
+
+ template< typename charT, class traitsT >
+ std::basic_ostream< charT, traitsT > &
+ operator<<( std::basic_ostream< charT, traitsT > & os, captured_context const& other);
+
+[variablelist
+[[Efects:] [Writes the representation of `other` to stream `os`.]]
+[[Returns:] [`os`]]
+]
+
+
+[endsect]
diff --git a/doc/context.qbk b/doc/context.qbk
index 7c80f7e..37d695e 100644
--- a/doc/context.qbk
+++ b/doc/context.qbk
@@ -23,10 +23,84 @@
[def __boost_build__ [*Boost.Build]]
[def __boost_context__ [*Boost.Context]]
-[template cs_example_link[link_text] [link context.examples.enumerator [link_text]]]
-[template context_link[link_text] [link context.context.context [link_text]]]
-[template stack_link[link_text] [link context.stack [link_text]]]
-[template preformance_link[link_text] [link context.performance [link_text]]]
+[template mdash[] '''—''']
+[template superscript[exp] ''''''[exp]'''''']
+
+[template class_heading[class_name]
+[hding class_[class_name]..Class [`[class_name]]]
+]
+[template class_link[class_name] [dblink class_[class_name]..[`[class_name]]]]
+
+[template template_heading[class_name]
+[hding class_[class_name]..Template [`[class_name]<>]]
+]
+[template template_link[class_name] [dblink class_[class_name]..[`[class_name]<>]]]
+
+[template member_heading[class_name method_name]
+[operator_heading [class_name]..[method_name]..[method_name]]
+]
+[template member_link[class_name method_name] [operator_link [class_name]..[method_name]..[method_name]]]
+
+[template operator_heading[class_name method_name method_text]
+[hding [class_name]_[method_name]..Member function [`[method_text]]()]
+]
+[template operator_link[class_name method_name method_text] [dblink [class_name]_[method_name]..[`[class_name]::[method_text]()]]]
+
+[template template_member_heading[class_name method_name]
+[hding [class_name]_[method_name]..Templated member function [`[method_name]]()]
+]
+[template template_member_link[class_name method_name] [member_link [class_name]..[method_name]]]
+
+[template static_member_heading[class_name method_name]
+[hding [class_name]_[method_name]..Static member function [`[method_name]]()]
+]
+[template static_member_link[class_name method_name] [member_link [class_name]..[method_name]]]
+
+[template data_member_heading[class_name member_name]
+[hding [class_name]_[member_name]..Data member [`[member_name]]]
+]
+[template data_member_link[class_name member_name] [dblink [class_name]_[member_name]..[`[class_name]::[member_name]]]]
+
+[template function_heading[function_name]
+[hding [function_name]..Non-member function [`[function_name]()]]
+]
+[template function_link[function_name] [dblink [function_name]..[`[function_name]()]]]
+
+[template ns_function_heading[namespace function_name]
+[hding [namespace]_[function_name]..Non-member function [`[namespace]::[function_name]()]]
+]
+[template ns_function_link[namespace function_name] [dblink [namespace]_[function_name]..[`[namespace]::[function_name]()]]]
+
+[template constructor_heading[class_name constructor_name]
+[hding [class_name]_[constructor_name]..Constructor]
+]
+
+[template copy_constructor_heading[class_name copy_constructor_name]
+[hding [class_name]_[copy_constructor_name]..Copy constructor]
+]
+
+[template move_constructor_heading[class_name move_constructor_name]
+[hding [class_name]_[move_constructor_name]..Move constructor]
+]
+
+[template copy_assignment_heading[class_name copy_assignment_name]
+[hding [class_name]_[copy_assignment_name]..Copy assignment operator]
+]
+
+[template move_assignment_heading[class_name move_assignment_name]
+[hding [class_name]_[move_assignment_name]..Move assignment operator]
+]
+
+[template anchor[name] '''''']
+[template hding[name title]
+'''
+
+ '''[title]'''
+'''
+]
+[template dblink[id text] ''''''[text]'''''']
+[template `[text] ''''''[text]'''''']
+
[def __context_fn__ ['context-function]]
[def __coroutine__ ['coroutine]]
@@ -35,14 +109,16 @@
[def __fls__ ['fiber-local storage]]
[def __guard_page__ ['guard-page]]
[def __not_a_context__ ['not-a-context]]
-[def __stack__ [stack_link ['stack]]]
+[def __stack__ ['stack]]
[def __thread__ ['thread]]
[def __threads__ ['threads]]
[def __tls__ ['thread-local storage]]
+[def __toe__ ['thread-of-execution]]
[def __stack_allocator__ ['StackAllocator]]
[def __stack_allocator_concept__ ['stack-allocator concept]]
[def __stack_traits__ ['stack-traits]]
+[def __ccontext__ ['captured_context]]
[def __econtext__ ['execution_context]]
[def __fcontext__ ['fcontext_t]]
[def __ucontext__ ['ucontext_t]]
@@ -55,6 +131,8 @@
[def __fls_free__ ['::FlsFree()]]
[def __bad_alloc__ ['std::bad_alloc]]
+[def __cc_op__ ['captured_context::operator()]]
+[def __ec_current__ ['execution_context::current()]]
[def __ec_op__ ['execution_context::operator()]]
[def __fc_base__ ['fc_base]]
[def __fc_link__ ['fc_link]]
@@ -70,9 +148,11 @@
[include overview.qbk]
[include requirements.qbk]
-[include fcontext.qbk]
+[/[include fcontext.qbk]]
[include execution_context.qbk]
+[include captured_context.qbk]
[include stack.qbk]
+[include preallocated.qbk]
[include performance.qbk]
[include architectures.qbk]
[include rationale.qbk]
diff --git a/doc/context.xml b/doc/context.xml
index fb3525b..2f09b8f 100644
--- a/doc/context.xml
+++ b/doc/context.xml
@@ -1,6 +1,6 @@
-
@@ -30,21 +30,22 @@
provides a sort of cooperative multitasking on a single thread. By providing
an abstraction of the current execution state in the current thread, including
the stack (with local variables) and stack pointer, all registers and CPU flags,
- and the instruction pointer, a fcontext_t instance represents
- a specific point in the application's execution path. This is useful for building
- higher-level abstractions, like coroutines, cooperative
- threads (userland threads) or an equivalent to C#
+ and the instruction pointer, a execution_context or captured_context
+ instance represents a specific point in the application's execution path. This
+ is useful for building higher-level abstractions, like coroutines,
+ cooperative threads (userland threads) or an equivalent
+ to C#
keyword yield in C++.
- A fcontext_t provides the means to suspend the current
- execution path and to transfer execution control, thereby permitting another
- fcontext_t to run on the current thread. This state full
- transfer mechanism enables a fcontext_t to suspend execution
- from within nested functions and, later, to resume from where it was suspended.
- While the execution path represented by a fcontext_t only
- runs on a single thread, it can be migrated to another thread at any given
- time.
+ execution_context and captured_context
+ provide the means to suspend the current execution path and to transfer execution
+ control, thereby permitting another context to run on the current thread. This
+ state full transfer mechanism enables a context to suspend execution from within
+ nested functions and, later, to resume from where it was suspended. While the
+ execution path represented by a execution_context or
+ captured_context only runs on a single thread, it can
+ be migrated to another thread at any given time.
A context switch between threads requires system calls (involving the OS kernel),
@@ -65,6 +66,11 @@
All functions and classes are contained in the namespace boost::context.
+
+
+ execution_context requires C++11!
+
+ Requirements
@@ -108,383 +114,76 @@
-
- Struct fcontext_t
-
- Each instance of fcontext_t represents a context (CPU
- registers and stack space). Together with its related functions jump_fcontext()
- and make_fcontext() it provides a execution control transfer
- mechanism similar interface like ucontext_t.
- fcontext_t and its functions are located in boost::context
- and the functions are declared as extern "C".
-
-
-
- If fcontext_t is used in a multi threaded application,
- it can migrated between threads, but must not reference thread-local
- storage.
-
-
-
-
- The low level API is the part to port to new platforms.
-
-
-
-
- If fiber-local storage is used on Windows, the user
- is responsible for calling ::FlsAlloc(), ::FlsFree().
-
-
-
- Executing
- a context
-
-
- A new context supposed to execute a context-function (returning
- void and accepting intptr_t as argument) will be created on top of the stack
- (at 16 byte boundary) by function make_fcontext().
-
-// context-function
-voidf(intptr);
-
-// creates a new stack
-std::size_tsize=8192;
-void*sp(std::malloc(size));
-
-// context fc uses f() as context function
-// fcontext_t is placed on top of context stack
-// a pointer to fcontext_t is returned
-fcontext_tfc(make_fcontext(sp,size,f));
-
-
- Calling jump_fcontext() invokes the context-function
- in a newly created context complete with registers, flags, stack and instruction
- pointers. When control should be returned to the original calling context,
- call jump_fcontext(). The current context information
- (registers, flags, and stack and instruction pointers) is saved and the original
- context information is restored. Calling jump_fcontext()
- again resumes execution in the second context after saving the new state of
- the original context.
-
-boost::context::fcontext_tfcm,fc1,fc2;
-
-voidf1(intptr_t)
-{
- std::cout<<"f1: entered"<<std::endl;
- std::cout<<"f1: call jump_fcontext( & fc1, fc2, 0)"<<std::endl;
- boost::context::jump_fcontext(&fc1,fc2,0);
- std::cout<<"f1: return"<<std::endl;
- boost::context::jump_fcontext(&fc1,fcm,0);
-}
-
-voidf2(intptr_t)
-{
- std::cout<<"f2: entered"<<std::endl;
- std::cout<<"f2: call jump_fcontext( & fc2, fc1, 0)"<<std::endl;
- boost::context::jump_fcontext(&fc2,fc1,0);
- BOOST_ASSERT(false&&!"f2: never returns");
-}
-
-std::size_tsize(8192);
-void*sp1(std::malloc(size));
-void*sp2(std::malloc(size));
-
-fc1=boost::context::make_fcontext(sp1,size,f1);
-fc2=boost::context::make_fcontext(sp2,size,f2);
-
-std::cout<<"main: call jump_fcontext( & fcm, fc1, 0)"<<std::endl;
-boost::context::jump_fcontext(&fcm,fc1,0);
-
-output:
- main:calljump_fcontext(&fcm,fc1,0)
- f1:entered
- f1:calljump_fcontext(&fc1,fc2,0)
- f2:entered
- f2:calljump_fcontext(&fc2,fc1,0)
- f1:return
-
-
- First call of jump_fcontext() enters the context-function
- f1()
- by starting context fc1 (context fcm saves the registers of main()). For jumping between context's fc1 and fc2
- jump_fcontext()
- is called. Because context fcm is chained to fc1, main() is entered (returning from jump_fcontext())
- after context fc1 becomes complete (return from f1()).
-
-
-
- Calling jump_fcontext() to the same context from inside
- the same context results in undefined behaviour.
-
-
-
-
- The size of the stack is required to be larger than the size of fcontext_t.
-
-
-
-
- In contrast to threads, which are preemtive, fcontext_t
- switches are cooperative (programmer controls when switch will happen). The
- kernel is not involved in the context switches.
-
-
-
- Transfer
- of data
-
-
- The third argument passed to jump_fcontext(), in one context,
- is passed as the first argument of the context-function
- if the context is started for the first time. In all following invocations
- of jump_fcontext() the intptr_t passed to jump_fcontext(),
- in one context, is returned by jump_fcontext() in the
- other context.
-
-boost::context::fcontext_tfcm,fc;
-
-typedefstd::pair<int,int>pair_t;
-
-voidf(intptr_tparam)
-{
- pair_t*p=(pair_t*)param;
- p=(pair_t*)boost::context::jump_fcontext(&fc,fcm,(intptr_t)(p->first+p->second));
- boost::context::jump_fcontext(&fc,fcm,(intptr_t)(p->first+p->second));
-}
-
-std::size_tsize(8192);
-void*sp(std::malloc(size));
-
-pair_tp(std::make_pair(2,7));
-fc=boost::context::make_fcontext(sp,size,f);
-
-intres=(int)boost::context::jump_fcontext(&fcm,fc,(intptr_t)&p);
-std::cout<<p.first<<" + "<<p.second<<" == "<<res<<std::endl;
-
-p=std::make_pair(5,6);
-res=(int)boost::context::jump_fcontext(&fcm,fc,(intptr_t)&p);
-std::cout<<p.first<<" + "<<p.second<<" == "<<res<<std::endl;
-
-output:
- 2+7==9
- 5+6==11
-
-
- Exceptions
- in context-function
-
-
- If the context-function emits an exception, the behaviour
- is undefined.
-
-
-
- context-function should wrap the code in a try/catch
- block.
-
-
-
-
- Do not jump from inside a catch block and then re-throw the exception in
- another execution context.
-
-
-
- Preserving
- floating point registers
-
-
- Preserving the floating point registers increases the cycle count for a context
- switch (see performance tests). The fourth argument of jump_fcontext()
- controls if fpu registers should be preserved by the context jump.
-
-
-
- The use of the fpu controlling argument of jump_fcontext()
- must be consistent in the application. Otherwise the behaviour is undefined.
-
-
-
- Stack
- unwinding
-
-
- 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.
-
-
- fcontext_t and related functions
-
-structstack_t
-{
- void*sp;
- std::size_tsize;
-};
-
-typedef<opaquepointer>fcontext_t;
-
-intptr_tjump_fcontext(fcontext_t*ofc,fcontext_tnfc,intptr_tvp,boolpreserve_fpu=true);
-fcontext_tmake_fcontext(void*sp,std::size_tsize,void(*fn)(intptr_t));
-
-
- sp
-
-
-
-
- Member:
-
-
- Pointer to the beginning of the stack (depending of the architecture
- the stack grows downwards or upwards).
-
-
-
-
-
- size
-
-
-
-
- Member:
-
-
- Size of the stack in bytes.
-
-
-
-
-
- fc_stack
-
-
-
-
- Member:
-
-
- Tracks the memory for the context's stack.
-
-
-
-
-
- intptr_tjump_fcontext(fcontext_t*ofc,fcontext_tnfc,intptr_tp,bool
- preserve_fpu=true)
-
-
-
-
- Effects:
-
-
- Stores the current context data (stack pointer, instruction pointer,
- and CPU registers) to *ofc and restores the context data from
- nfc, which implies jumping
- to nfc's execution context.
- The intptr_t argument, p,
- is passed to the current context to be returned by the most recent call
- to jump_fcontext()
- in the same thread. The last argument controls if fpu registers have
- to be preserved.
-
-
-
-
- Returns:
-
-
- The third pointer argument passed to the most recent call to jump_fcontext(),
- if any.
-
-
-
-
-
- fcontext_tmake_fcontext(void*
- sp,std::size_t
- size,void(*fn)(intptr_t))
-
-
-
-
- Precondition:
-
-
- Stack sp and function
- pointer fn are valid
- (depending on the architecture sp
- points to the top or bottom of the stack) and size
- > 0.
-
-
-
-
- Effects:
-
-
- Creates an fcontext_t on top of the stack and prepares the stack to execute
- the context-functionfn.
-
-
-
-
- Returns:
-
-
- Returns a fcontext_t which is placed on the stack.
-
-
-
-
-
- Class execution_context
-
-
- execution_context requires C++14.
-
-
+ Class execution_context
Class execution_context encapsulates fcontext_t
- and related functions ( jump_fcontext() and make_fcontext())
- as well as stack management. execution_context permits
- access to the current, active context via execution_context::current().
+ and manages the context' stack (allocation/deallocation).
+
+ execution_context allocates the context stack (using its
+ StackAllocator argument)
+ and creates a control structure on top of it. This structure controls the life
+ time of the stack. Instances of execution_context, associated
+ with a specific context, share the ownership of the control structure. If the
+ last reference goes out of scope, the control structure is destroyed and the
+ stack gets deallocated via the StackAllocator.
+
+
+ execution_context is copy-constructible, move-constructible,
+ copy-assignable and move-assignable.
+
+
+ execution_context maintains a static, thread-local pointer
+ (smart pointer), accessed by execution_context::current(),
+ pointing to the active context. On each context switch the static thread-local
+ pointer is updated. The usage of this global pointer makes the context switch
+ a little bit slower (due access of thread local storage) but has some advantages.
+ It allows to access the control structure of the current active context from
+ arbitrary code paths required in order to support segmented stacks, which need
+ to call certain maintenance functions (__splitstack_getcontext() etc.) before
+ each context switch (each context switch exchanges the stack). Additionally
+ the destruction of execution_context and thus the stack
+ deallocation is faster compared to captured_context.
+
+
+ execution_context expects a function/functor with signature
+ void(void*vp) ( vp
+ is the data passed at the first invocation of execution_context::operator()()).
+
+
+ usage
+ of execution_context
+
+intn=35;
+intp=0;
+boost::context::execution_contextmctx(boost::context::execution_context::current());
+boost::context::execution_contextctx(
+ [n,&p,&mctx](void*)mutable{
+ inta=0;
+ intb=1;
+ while(n-->0){
+ yield(a);
+ autonext=a+b;
+ a=b;
+ b=next;
+ }
+ });
+for(inti=0;i<10;++i){
+ ctx();
+ std::cout<<p<<" ";
+}
+
+output:
+ 0112358132134
+
+
+ inverting
+ the control flow
+ /*
* grammar:
* P ---> E '\0'
@@ -569,7 +268,7 @@
local stack variables so they can release allocated resources (RAII pattern).
The user is responsible for this task.
-
+ allocating
control structures on top of stack
@@ -610,7 +309,7 @@
...};
-
+ exception
handling
@@ -620,7 +319,7 @@
std::exception_ptr can be used to transfer exceptions
between different execution contexts.
-
+ parameter
passing
@@ -662,13 +361,13 @@
}};
-intmain(){
- Xx;
- std::cout<<x(7)<<std::endl;
- std::cout<<"done"<<std::endl;
-}
+Xx;
+std::cout<<x(7)<<std::endl;
+
+output:
+ 7
-
+ Class
execution_context
@@ -695,7 +394,13 @@
explicitoperatorbool()constnoexcept;booloperator!()constnoexcept;
- void*operator()(void*vp=nullptr)noexcept;
+ void*operator()(void*vp=nullptr);
+
+ template<typenameFn,typename...Args>
+ void*operator()(exec_ontop_arg_t,Fn&&fn,Args&&...args);
+
+ template<typenameFn,typename...Args>
+ void*operator()(void*vp,exec_ontop_arg_t,Fn&&fn,Args&&...args);booloperator==(execution_contextconst&other)constnoexcept;
@@ -714,12 +419,15 @@
operator<<(std::basic_ostream<charT,traitsT>&os,execution_contextconst&other);};
-
- staticexecution_context
- current()
-
+
+
+
+ Static
+ member function current()
+
+
+staticexecution_contextcurrent()noexcept;
+
@@ -740,20 +448,21 @@
-
- template<typnameFn,typename...Args>execution_context(Fn&&
- fn,Args&&
- ...args)
-
+
+
+
+ Constructor
+
+
+template<typenameFn,typename...Args>
+execution_context(Fn&&fn,Args&&...args);
+
+template<typenameStackAlloc,typenameFn,typename...Args>
+execution_context(std::allocator_arg_t,StackAllocsalloc,Fn&&fn,Args&&...args);
+
+template<typenameStackAlloc,typenameFn,typename...Args>
+execution_context(std::allocator_arg_t,preallocatedpalloc,StackAllocsalloc,Fn&&fn,Args&&...args);
+
@@ -767,76 +476,20 @@
-
- template<typenameStackAlloc,typnameFn,typename
- ...Args
- >execution_context(std::allocator_arg_t,
- StackAllocsalloc,Fn&&
- fn,Args&&
- ...args)
-
-
-
-
- Effects:
-
-
- Creates a new execution context and prepares the context to execute
- fn.
-
-
-
-
-
- template<typenameStackAlloc,typnameFn,typename
- ...Args
- >execution_context(std::allocator_arg_t,
- preallocatedpalloc,StackAllocsalloc,Fn&&fn,Args
- &&...
- args)
-
-
-
-
- Effects:
-
-
- Creates a new execution context and prepares the context to execute
- fn. Used to store control
- structures on top of the stack.
-
-
-
-
-
- execution_context(
- execution_contextconst&other)
-
+
+ [[Effects:] [Creates a new execution context and prepares the context to execute
+ fn. Used to store control structures
+ on top of the stack.]] ]
+
+
+
+
+ Copy constructor
+
+
+execution_context(execution_contextconst&other)noexcept;
+
@@ -858,13 +511,15 @@
-
- execution_context(
- execution_context&&
- other)
-
+
+
+
+ Move constructor
+
+
+execution_context(execution_context&&other)noexcept;
+
@@ -885,15 +540,15 @@
-
- execution_context&
- operator=(
- execution_contextconst&other)
-
+
+
+
+ Copy assignment operator
+
+
+execution_context&operator=(execution_contextconst&other)noexcept;
+
@@ -915,14 +570,15 @@
-
- execution_context&
- operator=(
- execution_context&&
- other)
-
+
+
+
+ Move assignment operator
+
+
+execution_context&operator=(execution_context&&other)noexcept;
+
@@ -944,13 +600,15 @@
-
- explicitoperator
- bool()constnoexcept
-
+
+
+
+ Member
+ function operator bool()
+
+
+explicitoperatorbool()constnoexcept;
+
@@ -971,12 +629,15 @@
-
- booloperator!()constnoexcept
-
+
+
+
+ Member
+ function operator!()
+
+
+booloperator!()constnoexcept;
+
@@ -997,14 +658,21 @@
-
- void*operator()(void*
- vp)noexcept
-
+
+
+
+ Member
+ function operator()()
+
+
+void*operator()(void*vp=nullptr)noexcept;
+
+template<typenameFn,typename...Args>
+void*operator()(exec_ontop_arg_t,Fn&&fn,Args&&...args);
+
+template<typenameFn,typename...Args>
+void*operator()(void*data,exec_ontop_arg_t,Fn&&fn,Args&&...args);
+
@@ -1012,15 +680,18 @@
Stores internally the current context data (stack pointer, instruction
- pointer, and CPU registers) to the current active context and restores
+ pointer, and CPU registers) of the current active context and restores
the context data from *this, which implies jumping to *this's
- execution context. The void pointer argument, vp,
+ context. The void pointer argument, vp,
is passed to the current context to be returned by the most recent call
to execution_context::operator() in the same thread. [[Note:
+ role="special">() in the same thread. fn
+ is executed with arguments args
+ on top of the stack of this.
+ [[Note:
The behaviour is undefined if operator
-
- Throws:
-
-
- Nothing.
-
-
-
-
- operator==
-
+
+
+
+ Member
+ function operator==()
+
+ booloperator==(execution_contextconst&other)constnoexcept;
@@ -1083,10 +748,13 @@
-
- operator!=
-
+
+
+
+ Member
+ function operator!=()
+
+ booloperator!=(execution_contextconst&other)constnoexcept;
@@ -1095,19 +763,26 @@
Returns:
- [`! (other == * this)
+ ! (other == * this)
+
+
+
+
+ Throws:
+
+
+ Nothing.
- [[Throws:] [Nothing.]] ]
+
+
+ Member
+ function operator<()
+
-
- operator<
- booloperator<(execution_contextconst&other)constnoexcept;
@@ -1134,11 +809,13 @@
-
- operator>
-
+
+
+
+ Member
+ function operator>()
+
+ booloperator>(execution_contextconst&other)constnoexcept;
@@ -1161,11 +838,13 @@
-
- operator<=
-
+
+
+
+ Member
+ function operator<=()
+
+ booloperator<=(execution_contextconst&other)constnoexcept;
@@ -1190,11 +869,13 @@
-
- operator>=
-
+
+
+
+ Member
+ function operator>=()
+
+ booloperator>=(execution_contextconst&other)constnoexcept;
@@ -1218,11 +899,13 @@
-
- operator<<
-
+
+
+
+ Non-member function
+ operator<<()
+
+ template<typenamecharT,classtraitsT>std::basic_ostream<charT,traitsT>&operator<<(std::basic_ostream<charT,traitsT>&os,execution_contextconst&other);
@@ -1247,65 +930,722 @@
-
- Struct
- preallocated
+
+
+ Class captured_context
+
+ Class captured_context encapsulates fcontext_t
+ and manages the context' stack (allocation/deallocation).
+
+
+ captured_context allocates the context stack (using its
+ StackAllocator argument) and creates a control structure
+ on top of it. This structure controls the life time of the stack. The address
+ of the control structure is stored in the first frame of context' stack (e.g.
+ it can not accessed by instances of captured_context directly).
+ In contrast to execution_context the ownership of the
+ control structure is not shared A call ofcaptured_context::operator()
+ enters the context represented by *this and invalidates *this. The context that has been suspended by
+ calling captured_context::operator() is passed to the
+ resumed context, e.g. as argument of the context-function if the context was
+ resumed the first time or returned by captured_context::operator().
+ captured_context is only move-constructible and move-assignable.
+ If the last reference (captured_context) goes out of scope,
+ the control structure is destroyed and the stack gets deallocated via the
+ StackAllocator. captured_context
+ maintains a static, thread-local pointer (smart pointer), accessed by execution_context::current(),
+ pointing to the active context. On each context switch the static, thread-local
+ pointer is updated. This makes the context switch a little bit slower, but
+ enables faster context destruction (stack unwinding) compared to captured_context.
+
+
+ captured_context expects a function/functor with signature
+ captured_context(
+ captured_contextctx,void*
+ vp).
+ The parameter ctx represents
+ the context from which this context was resumed (e.g. that has called captured_context::operator()
+ on *this)
+ and vp is the data passed to
+ captured_context::operator(). The function/functor has
+ to return the captured_context that has to be resumed, while this context terminates.
+
+
+
+ Segemnted stacks are not supported together with captured_context.
+
+
+
+ usage
+ of captured_context
-structpreallocated{
- void*sp;
- std::size_tsize;
- stack_contextsctx;
+/*
+ * grammar:
+ * P ---> E '\0'
+ * E ---> T {('+'|'-') T}
+ * T ---> S {('*'|'/') S}
+ * S ---> digit | '(' E ')'
+ */
+classParser{
+ // implementation omitted; see examples directory
+};
- preallocated(void*sp,std:size_tsize,stack_allocatorsctx)noexcept;
+std::istringstreamis("1+1");
+booldone=false;
+std::exception_ptrexcept;
+
+// execute parser in new execution context
+boost::context::captured_contextpctx(
+ [&is,&done,&except](ctx::captured_contextmctx,void*ignored){
+ // create parser with callback function
+ Parserp(is,
+ [&mctx](charch){
+ // resume main execution context
+ autoresult=mctx(&ch);
+ mctx=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
+ returnmctx;
+ });
+
+// user-code pulls parsed data from parser
+// invert control flow
+autoresult=pctx();
+pctx=std::move(std::get<0>(result));
+void*vp=std::get<1>(result);
+if(except){
+ std::rethrow_exception(except);
+}
+while(!done){
+ printf("Parsed: %c\n",*static_cast<char*>(vp));
+ std::tie(pctx,vp)=pctx();
+ 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 captured_context 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 captured_context.
+
+
+ If the code executed by captured_context 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.
+
+
+ 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 captured_context is created.
+
+
+
+ The user is responsible for destructing the control structure at the top
+ of the stack.
+
+
+// stack-allocator used for (de-)allocating stack
+fixedsize_stacksalloc(4048);
+// allocate stack space
+stack_contextsctx(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_tsize=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();
+...
+structmy_control_structure{
+ // captured context
+ captured_contextcctx;
+
+ template<typenameStackAllocator>
+ my_control_structure(void*sp,std::size_tsize,stack_contextsctx,StackAllocatorsalloc):
+ // create captured context
+ cctx(std::allocator_arg,preallocated(sp,size,sctx),salloc,entry_func){
+ }
+ ...};
-
- preallocated(void*sp,std:size_tsize,stack_allocator
- sctx)
+
+ exception
+ handling
+
+ If the function executed inside a captured_context emits
+ ans exception, the application is terminated by calling ['std::terminate().
+ std::exception_ptr can be used to transfer exceptions
+ between different execution contexts.
+
+
+ parameter
+ passing
+
+
+ The void pointer argument passed to captured_context::operator(),
+ in one context, is passed as the last argument of the context-function
+ if the context is started for the first time. In all following invocations
+ of captured_context::operator() the void pointer passed
+ to captured_context::operator(), in one context, is returned
+ by captured_context::operator() in the other context.
+
+classX{
+private:
+ std::exception_ptrexcptr_;
+ boost::context::captured_contextctx_;
+
+public:
+ X():
+ excptr_(),
+ ctx_([=](ctx::captured_contextctx,void*vp)->ctx::captured_context{
+ try{
+ for(;;){
+ inti=*static_cast<int*>(vp);
+ std::stringstr=boost::lexical_cast<std::string>(i);
+ autoresult=ctx(&str);
+ ctx=std::move(std::get<0>(result));
+ vp=std::get<1>(result);
+ }
+ }catch(ctx::detail::forced_unwindconst&){
+ throw;
+ }catch(...){
+ excptr_=std::current_exception();
+ }
+ returnctx;
+ })
+ {}
+
+ std::stringoperator()(inti){
+ autoresult=ctx_(&i);
+ ctx_=std::move(std::get<0>(result));
+ void*ret=std::get<1>(result);
+ if(excptr_){
+ std::rethrow_exception(excptr_);
+ }
+ return*static_cast<std::string*>(ret);
+ }
+};
+
+Xx;
+std::cout<<x(7)<<std::endl;
+
+output:
+7
+
+
+ Class
+ captured_context
+
+classcaptured_context{
+public:
+ template<typenameFn,typename...Args>
+ captured_context(Fn&&fn,Args&&...args);
+
+ template<typenameStackAlloc,typenameFn,typename...Args>
+ captured_context(std::allocator_arg_t,StackAllocsalloc,Fn&&fn,Args&&...args);
+
+ template<typenameStackAlloc,typenameFn,typename...Args>
+ captured_context(std::allocator_arg_t,preallocatedpalloc,StackAllocsalloc,Fn&&fn,Args&&...args);
+
+ ~captured_context();
+
+ captured_context(captured_context&&other)noexcept;
+ captured_context&operator=(captured_context&&other)noexcept;
+
+ captured_context(captured_contextconst&other)noexcept=delete;
+ captured_context&operator=(captured_contextconst&other)noexcept=delete;
+
+ explicitoperatorbool()constnoexcept;
+ booloperator!()constnoexcept;
+
+ std::tuple<captured_context,void*>operator()(void*data=nullptr);
+
+ template<typenameFn,typename...Args>
+ std::tuple<captured_context,void*>operator()(exec_ontop_arg_t,Fn&&fn,Args&&...args);
+
+ template<typenameFn,typename...Args>
+ std::tuple<captured_context,void*>operator()(void*data,exec_ontop_arg_t,Fn&&fn,Args&&...args);
+
+ booloperator==(captured_contextconst&other)constnoexcept;
+
+ booloperator!=(captured_contextconst&other)constnoexcept;
+
+ booloperator<(captured_contextconst&other)constnoexcept;
+
+ booloperator>(captured_contextconst&other)constnoexcept;
+
+ booloperator<=(captured_contextconst&other)constnoexcept;
+
+ booloperator>=(captured_contextconst&other)constnoexcept;
+
+ template<typenamecharT,classtraitsT>
+ friendstd::basic_ostream<charT,traitsT>&
+ operator<<(std::basic_ostream<charT,traitsT>&os,captured_contextconst&other);
+};
+
+
+
+
+ Constructor
+
+
+template<typenameFn,typename...Args>
+captured_context(Fn&&fn,Args&&...args);
+
+template<typenameStackAlloc,typenameFn,typename...Args>
+captured_context(std::allocator_arg_t,StackAllocsalloc,Fn&&fn,Args&&...args);
+
+template<typenameStackAlloc,typenameFn,typename...Args>
+captured_context(std::allocator_arg_t,preallocatedpalloc,StackAllocsalloc,Fn&&fn,Args&&...args);
+Effects:
- Creates an object of preallocated.
+ 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().
+
+
+
+
+
+ [[Effects:] [Creates a new execution context and prepares the context to execute
+ fn.]] ] [[Effects:] [Creates
+ a new execution context and prepares the context to execute fn.
+ Used to store control structures on top of the stack.]] ]
+
+
+
+
+ Move constructor
+
+
+captured_context(captured_context&&other)noexcept;
+
+
+
+
+ Effects:
+
+
+ Moves underlying capture record to *this.
+
+
+
+
+ Throws:
+
+
+ Nothing.
+
+
+
+
+
+
+
+ Move assignment operator
+
+
+captured_context&operator=(captured_context&&other)noexcept;
+
+
+
+
+ Effects:
+
+
+ Moves the state of other
+ to *this
+ using move semantics.
+
+
+
+
+ Throws:
+
+
+ Nothing.
+
+
+
+
+
+
+
+ Member
+ function operator bool()
+
+
+explicitoperatorbool()constnoexcept;
+
+
+
+
+ Returns:
+
+
+ true if *this points to a capture record.
+
+
+
+
+ Throws:
+
+
+ Nothing.
+
+
+
+
+
+
+
+ Member
+ function operator!()
+
+
+booloperator!()constnoexcept;
+
+
+
+
+ Returns:
+
+
+ true if *this does not point to a capture record.
+
+
+
+
+ Throws:
+
+
+ Nothing.
+
+
+
+
+
+
+
+ Member
+ function operator()()
+
+
+std::tuple<captured_context,void*>operator()(void*data=nullptr);
+
+template<typenameFn,typename...Args>
+std::tuple<captured_context,void*>operator()(exec_ontop_arg_t,Fn&&fn,Args&&...args);
+
+template<typenameFn,typename...Args>
+std::tuple<captured_context,void*>operator()(void*data,exec_ontop_arg_t,Fn&&fn,Args&&...args);
+
+
+
+
+ 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 void pointer argument, vp,
+ is passed to the current context to be returned by the most recent call
+ to captured_context::operator()
+ in the same thread. fn
+ is executed with arguments args
+ on top of the stack of this.
+ [[Note:
+
+
+ The behaviour is undefined if operator()() is called while captured_context::current() returns *this (e.g. resuming an already running
+ context). If the top-level context function returns, std::exit() is called.
+
+
+
+
+ Returns:
+
+
+ The tuple of void pointer argument passed to the most recent call to
+ captured_context::operator(),
+ if any and a captured_context representing the context that has been
+ suspended .
+
+
+
+
+
+
+
+ Member
+ function operator==()
+
+
+booloperator==(captured_contextconst&other)constnoexcept;
+
+
+
+
+ Returns:
+
+
+ true if *this and other
+ represent the same execution context, false
+ otherwise.
+
+
+
+
+ Throws:
+
+
+ Nothing.
+
+
+
+
+
+
+
+ Member
+ function operator!=()
+
+
+booloperator!=(captured_contextconst&other)constnoexcept;
+
+
+
+
+ Returns:
+
+
+ ! (other == * this)
+
+
+
+
+ Throws:
+
+
+ Nothing.
+
+
+
+
+
+
+
+ Member
+ function operator<()
+
+
+booloperator<(captured_contextconst&other)constnoexcept;
+
+
+
+
+ Returns:
+
+
+ true if *this!=other is true and the implementation-defined
+ total order of captured_context
+ values places *this
+ before other, false otherwise.
+
+
+
+
+ Throws:
+
+
+ Nothing.
+
+
+
+
+
+
+
+ Member
+ function operator>()
+
+
+booloperator>(captured_contextconst&other)constnoexcept;
+
+
+
+
+ Returns:
+
+
+ other<
+ *this
+
+
+
+
+ Throws:
+
+
+ Nothing.
+
+
+
+
+
+
+
+ Member
+ function operator<=()
+
+
+booloperator<=(captured_contextconst&other)constnoexcept;
+
+
+
+
+ Returns:
+
+
+ !(other<
+ *this)
+
+
+
+
+ Throws:
+
+
+ Nothing.
+
+
+
+
+
+
+
+ Member
+ function operator>=()
+
+
+booloperator>=(captured_contextconst&other)constnoexcept;
+
+
+
+
+ Returns:
+
+
+ !(*
+ this<
+ other)
+
+
+
+
+ Throws:
+
+
+ Nothing.
+
+
+
+
+
+
+
+ Non-member function
+ operator<<()
+
+
+template<typenamecharT,classtraitsT>
+std::basic_ostream<charT,traitsT>&
+operator<<(std::basic_ostream<charT,traitsT>&os,captured_contextconst&other);
+
+
+
+
+ Efects:
+
+
+ Writes the representation of other
+ to stream os.
+
+
+
+
+ Returns:
+
+
+ os
-
- Using WinFiber-API
-
- Because the TIB (thread information block) is not fully described in the
- MSDN, it might be possible that not all required TIB-parts are swapped. With
- compiler flag BOOST_USE_WINFIBERS
- execution_context uses internally
- the Windows Fiber API.
-
-
-
- The first call of execution_context::operator() converts the thread into a Windows fiber
- by invoking ConvertThreadToFiber(). If desired, ConvertFiberToThread() has to be called by the user explicitly
- in order to release resources allocated by ConvertThreadToFiber() (e.g. after using boost.context).
-
-
-
- Stack allocation
+ Stack allocation
The memory used by the stack is allocated/deallocated via a StackAllocator
which is required to model a stack-allocator concept.
@@ -2017,6 +2357,33 @@
+
+ Struct preallocated
+structpreallocated{
+ void*sp;
+ std::size_tsize;
+ stack_contextsctx;
+
+ preallocated(void*sp,std:size_tsize,stack_allocatorsctx)noexcept;
+};
+
+
+ Constructor
+
+preallocated(void*sp,std:size_tsize,stack_allocatorsctx)noexcept;
+
+
+
+
+ Effects:
+
+
+ Creates an object of preallocated.
+
+
+
+
+ Performance
@@ -2031,7 +2398,7 @@
Performance of context switch
-
+
@@ -2044,11 +2411,6 @@
ucontext_t
-
-
- fcontext_t
-
-
execution_context
@@ -2056,7 +2418,7 @@
- windows fibers
+ captured_context
@@ -2065,38 +2427,7 @@
- i386
-
- AMD Athlon 64 DualCore 4400+
-
-
-
-
-
-
- 708 ns / 754 cycles
-
-
-
-
- 37 ns / 37 cycles
-
-
-
-
- ns / cycles
-
-
-
-
- ns / cycles
-
-
-
-
-
-
- x86_64
+ x86_64
Intel Core2 Q6700
@@ -2110,17 +2441,12 @@
- 8 ns / 23 cycles
+ 51 ns / 141 cycles
- 16 ns / 46 cycles
-
-
-
-
- ns / cycles
+ 7 ns / 18 cycles
diff --git a/doc/execution_context.qbk b/doc/execution_context.qbk
index 9936163..00a0c17 100644
--- a/doc/execution_context.qbk
+++ b/doc/execution_context.qbk
@@ -5,14 +5,66 @@
http://www.boost.org/LICENSE_1_0.txt
]
+[#econtext]
[section:econtext Class execution_context]
-[important __econtext__ requires C++11!]
+Class __econtext__ encapsulates __fcontext__ and manages the context' stack
+(allocation/deallocation).
-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()`.
+__econtext__ allocates the context stack (using its [link stack __stack_allocator__]
+argument) and creates a control structure on top of it. This structure
+controls the life time of the stack. Instances of __econtext__, associated
+with a specific context, share the ownership of the control structure.
+If the last reference goes out of scope, the control structure is destroyed and
+the stack gets deallocated via the __stack_allocator__.
+
+__econtext__ is copy-constructible, move-constructible, copy-assignable and
+move-assignable.
+
+__econtext__ maintains a static, thread-local pointer (smart pointer),
+accessed by __ec_current__, pointing to the active context.
+On each context switch the static thread-local pointer is updated.
+The usage of this global pointer makes the context switch a little bit slower
+(due access of thread local storage) but has some advantages. It allows to access
+the control structure of the current active context from arbitrary code paths
+required in order to support segmented stacks, which need to call certain
+maintenance functions (__splitstack_getcontext() etc.) before each context switch
+(each context switch exchanges the stack).
+Additionally the destruction of __econtext__ and thus the stack deallocation is
+faster compared to [link ccontext __ccontext__].
+
+__econtext__ expects a function/functor with signature `void(void* vp)` (
+`vp` is the data passed at the first invocation of
+[operator_link execution_context operator_call operator()]).
+
+
+[heading usage of __econtext__]
+
+ int n=35;
+ int p=0;
+ boost::context::execution_context mctx( boost::context::execution_context::current() );
+ boost::context::execution_context ctx(
+ [n,&p,&mctx](void*)mutable{
+ int a=0;
+ int b=1;
+ while(n-->0){
+ yield(a);
+ auto next=a+b;
+ a=b;
+ b=next;
+ }
+ });
+ for(int i=0;i<10;++i){
+ ctx();
+ std::cout<
+ void * operator()( exec_ontop_arg_t, Fn && fn, Args && ... args);
+
+ template< typename Fn, typename ... Args >
+ void * operator()( void * vp, 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 >
@@ -219,87 +277,128 @@ __ec_op__, in one context, is returned by __ec_op__ in the other context.
operator<<( std::basic_ostream< charT, traitsT > & os, execution_context const& other);
};
-[heading `static execution_context current()`]
+[static_member_heading execution_context..current]
+
+ static execution_context current() noexcept;
+
[variablelist
[[Returns:] [Returns an instance of excution_context pointing to the active
execution context.]]
[[Throws:] [Nothing.]]
]
-[heading `template< typname Fn, typename ... Args > execution_context( Fn && fn, Args && ... args)`]
+[constructor_heading execution_context..constructor]
+
+ template< typename Fn, typename ... Args >
+ execution_context( Fn && fn, Args && ... args);
+
+ template< typename StackAlloc, typename Fn, typename ... Args >
+ execution_context( std::allocator_arg_t, StackAlloc salloc, Fn && fn, Args && ... args);
+
+ template< typename StackAlloc, typename Fn, typename ... Args >
+ execution_context( std::allocator_arg_t, preallocated palloc, StackAlloc salloc, Fn && fn, Args && ... args);
+
[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().]]
+`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 store control
+structures on top of the stack.]]
]
-[heading `template< typename StackAlloc, typname Fn, typename ... Args > execution_context( std::allocator_arg_t, StackAlloc salloc, Fn && fn, Args && ... args)`]
-[variablelist
-[[Effects:] [Creates a new execution context and prepares the context to execute
-`fn`.]]
-]
+[copy_constructor_heading execution_context..copy constructor]
-[heading `template< typename StackAlloc, typname Fn, typename ... Args > execution_context( std::allocator_arg_t, 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.]]
-]
+ execution_context( execution_context const& other) noexcept;
-[heading `execution_context( execution_context const& other)`]
[variablelist
[[Effects:] [Copies `other`, e.g. underlying capture record is shared
with `*this`.]]
[[Throws:] [Nothing.]]
]
-[heading `execution_context( execution_context && other)`]
+[move_constructor_heading execution_context..move constructor]
+
+ execution_context( execution_context && other) noexcept;
+
[variablelist
[[Effects:] [Moves underlying capture record to `*this`.]]
[[Throws:] [Nothing.]]
]
-[heading `execution_context & operator=( execution_context const& other)`]
+[copy_assignment_heading execution_context..copy assignment]
+
+ execution_context & operator=( execution_context const& other) noexcept;
+
[variablelist
[[Effects:] [Copies the state of `other` to `*this`, state (capture record) is shared.]]
[[Throws:] [Nothing.]]
]
-[heading `execution_context & operator=( execution_context && other)`]
+[move_assignment_heading execution_context..move assignment]
+
+ execution_context & operator=( execution_context && other) noexcept;
+
[variablelist
[[Effects:] [Moves the state of `other` to `*this` using move semantics.]]
[[Throws:] [Nothing.]]
]
-[heading `explicit operator bool() const noexcept`]
+[operator_heading execution_context..operator_bool..operator bool]
+
+ explicit operator bool() const noexcept;
+
[variablelist
[[Returns:] [`true` if `*this` points to a capture record.]]
[[Throws:] [Nothing.]]
]
-[heading `bool operator!() const noexcept`]
+[operator_heading execution_context..operator_not..operator!]
+
+ bool operator!() const noexcept;
+
[variablelist
[[Returns:] [`true` if `*this` does not point to a capture record.]]
[[Throws:] [Nothing.]]
]
-[heading `void * operator()( void * vp) noexcept`]
+[operator_heading execution_context..operator_call..operator()]
+
+ void * operator()( void * vp = nullptr) noexcept;
+
[variablelist
[[Effects:] [Stores internally the current context data (stack pointer,
-instruction pointer, and CPU registers) to the current active context and
+instruction pointer, and CPU registers) of the current active context and
restores the context data from `*this`, which implies jumping to `*this`'s
-execution context.
+context.
The void pointer argument, `vp`, is passed to the current context to be returned
by the most recent call to `execution_context::operator()` in the same thread.
+`fn` is executed with arguments `args` on top of the stack of `this`.
[[Note:] [The behaviour is undefined if `operator()()` is called while `execution_context::current()`
returns `*this` (e.g. resuming an already running context). If the top-level context
function returns, `std::exit()` is called.]]
[[Returns:] [The void pointer argument passed to the most recent call to
`execution_context::operator()`, if any.]]
-[[Throws:] [Nothing.]]
]
-[heading `operator==`]
+[operator_heading execution_context..operator_call..operator(exec_ontop_arg_t)]
- bool operator==( execution_context const& other) const noexcept;
+ template< typename Fn, typename ... Args >
+ void * operator()( exec_ontop_arg_t, Fn && fn, Args && ... args);
+
+ template< typename Fn, typename ... Args >
+ void * operator()( void * data, exec_ontop_arg_t, Fn && fn, Args && ... args);
+
+[variablelist
+[[Effects:] [Same as `operator()(void*)`, additionally function `fn` is executed
+with arguments `args` in the context of `*this` (e.g. the stack frame of `fn` is
+allocated on stack of `*this`.]]
+[[Returns:] [The void pointer argument passed to the most recent call to
+`execution_context::operator()`, if any.]]
+]
+
+[operator_heading execution_context..operator_equal..operator==]
+
+ bool operator==( execution_context const& other) const noexcept;
[variablelist
[[Returns:] [`true` if `*this` and `other` represent the same execution context,
@@ -307,18 +406,18 @@ function returns, `std::exit()` is called.]]
[[Throws:] [Nothing.]]
]
-[heading `operator!=`]
+[operator_heading execution_context..operator_notequal..operator!=]
- bool operator!=( execution_context const& other) const noexcept;
+ bool operator!=( execution_context const& other) const noexcept;
[variablelist
[[Returns:] [[`! (other == * this)]]]
[[Throws:] [Nothing.]]
]
-[heading `operator<`]
+[operator_heading execution_context..operator_less..operator<]
- bool operator<( execution_context const& other) const noexcept;
+ bool operator<( execution_context const& other) const noexcept;
[variablelist
[[Returns:] [`true` if `*this != other` is true and the
@@ -327,38 +426,38 @@ implementation-defined total order of `execution_context` values places `*this`
[[Throws:] [Nothing.]]
]
-[heading `operator>`]
+[operator_heading execution_context..operator_greater..operator>]
- bool operator>( execution_context const& other) const noexcept;
+ bool operator>( execution_context const& other) const noexcept;
[variablelist
[[Returns:] [`other < * this`]]
[[Throws:] [Nothing.]]
]
-[heading `operator<=`]
+[operator_heading execution_context..operator_lesseq..operator<=]
- bool operator<=( execution_context const& other) const noexcept;
+ bool operator<=( execution_context const& other) const noexcept;
[variablelist
[[Returns:] [`! (other < * this)`]]
[[Throws:] [Nothing.]]
]
-[heading `operator>=`]
+[operator_heading execution_context..operator_greatereq..operator>=]
- bool operator>=( execution_context const& other) const noexcept;
+ bool operator>=( execution_context const& other) const noexcept;
[variablelist
[[Returns:] [`! (* this < other)`]]
[[Throws:] [Nothing.]]
]
-[heading `operator<<`]
+[hding execution_context..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);
+ 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`.]]
@@ -366,20 +465,4 @@ implementation-defined total order of `execution_context` values places `*this`
]
-[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:] [Creates an object of preallocated.]]
-]
-
-
[endsect]
diff --git a/doc/fcontext.qbk b/doc/fcontext.qbk
index d4a6f3b..2b6f3ed 100644
--- a/doc/fcontext.qbk
+++ b/doc/fcontext.qbk
@@ -187,11 +187,11 @@ downwards or upwards).]]
[[Member:] [Tracks the memory for the context's stack.]]
]
-[heading `void * jump_fcontext(fcontext_t* ofc,fcontext_t nfc,void * p)
+[heading `void * jump_fcontext(fcontext_t* ofc,fcontext_t nfc,void * p)`]
[variablelist
[[Effects:] [Stores the current context data (stack pointer, instruction
pointer, and CPU registers) to `*ofc` and restores the context data from `nfc`,
-which implies jumping to `nfc`'s execution context. The void * argument, `p`,
+which implies jumping to execution context `nfc`. The void * argument, `p`,
is passed to the current context to be returned by the most recent call to
`jump_fcontext()` in the same thread.]]
[[Returns:] [The third pointer argument passed to the most recent call to
diff --git a/doc/html/context/abstract_context.html b/doc/html/context/abstract_context.html
new file mode 100644
index 0000000..8deea6b
--- /dev/null
+++ b/doc/html/context/abstract_context.html
@@ -0,0 +1,72 @@
+
+
+ Boost.Context provides two classes encapsulating
+ fcontext_t and related functions (jump_fcontext()
+ and make_fcontext()) and stack management - execution_context
+ and captured_context. execution_context
+ and captured_context represent one thread-of-execution.
+ A thread-of-execution is a single flow of control within
+ a programm. Each class maintains a control structure, containing the preserved
+ registers, the stack and the stack allocator. The main difference between
+ execution_context and captured_context
+ consists in maintaining context' control structure.
+
+ Class captured_context encapsulates fcontext_t
+ and manages the context' stack (allocation/deallocation).
+
+
+ captured_context allocates the context stack (using
+ its StackAllocator argument) and creates a control structure
+ on top of it. This structure controls the life time of the stack. The address
+ of the control structure is stored in the first frame of context' stack (e.g.
+ it can not accessed by instances of captured_context
+ directly). In contrast to execution_context the ownership
+ of the control structure is not shared A call ofcaptured_context::operator()
+ enters the context represented by *this and invalidates *this. The context that has been suspended
+ by calling captured_context::operator() is passed to
+ the resumed context, e.g. as argument of the context-function if the context
+ was resumed the first time or returned by captured_context::operator().
+ captured_context is only move-constructible and move-assignable.
+ If the last reference (captured_context) goes out of
+ scope, the control structure is destroyed and the stack gets deallocated
+ via the StackAllocator. captured_context
+ maintains a static, thread-local pointer (smart pointer), accessed by execution_context::current(),
+ pointing to the active context. On each context switch the static, thread-local
+ pointer is updated. This makes the context switch a little bit slower, but
+ enables faster context destruction (stack unwinding) compared to captured_context.
+
+
+ captured_context expects a function/functor with signature
+ captured_context(
+ captured_contextctx,void*
+ vp).
+ The parameter ctx represents
+ the context from which this context was resumed (e.g. that has called captured_context::operator()
+ on *this)
+ and vp is the data passed
+ to captured_context::operator(). The function/functor
+ has to return the captured_context that has to be resumed, while this context
+ terminates.
+
+
+
+
+
Important
+
+
+ Segemnted stacks are not supported together with captured_context.
+
/*
+ * grammar:
+ * P ---> E '\0'
+ * E ---> T {('+'|'-') T}
+ * T ---> S {('*'|'/') S}
+ * S ---> digit | '(' E ')'
+ */
+classParser{
+ // implementation omitted; see examples directory
+};
+
+std::istringstreamis("1+1");
+booldone=false;
+std::exception_ptrexcept;
+
+// execute parser in new execution context
+boost::context::captured_contextpctx(
+ [&is,&done,&except](ctx::captured_contextmctx,void*ignored){
+ // create parser with callback function
+ Parserp(is,
+ [&mctx](charch){
+ // resume main execution context
+ autoresult=mctx(&ch);
+ mctx=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
+ returnmctx;
+ });
+
+// user-code pulls parsed data from parser
+// invert control flow
+autoresult=pctx();
+pctx=std::move(std::get<0>(result));
+void*vp=std::get<1>(result);
+if(except){
+ std::rethrow_exception(except);
+}
+while(!done){
+ printf("Parsed: %c\n",*static_cast<char*>(vp));
+ std::tie(pctx,vp)=pctx();
+ 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 captured_context 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 captured_context.
+
+
+ If the code executed by captured_context 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.
+
+ Allocating control structures on top of the stack requires to allocated the
+ stack_context and create the control structure with
+ placement new before captured_context 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_stacksalloc(4048);
+// allocate stack space
+stack_contextsctx(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_tsize=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();
+...
+structmy_control_structure{
+ // captured context
+ captured_contextcctx;
+
+ template<typenameStackAllocator>
+ my_control_structure(void*sp,std::size_tsize,stack_contextsctx,StackAllocatorsalloc):
+ // create captured context
+ cctx(std::allocator_arg,preallocated(sp,size,sctx),salloc,entry_func){
+ }
+ ...
+};
+
+ If the function executed inside a captured_context emits
+ ans exception, the application is terminated by calling ['std::terminate().
+ std::exception_ptr can be used to transfer exceptions
+ between different execution contexts.
+
+ The void pointer argument passed to captured_context::operator(),
+ in one context, is passed as the last argument of the context-function
+ if the context is started for the first time. In all following invocations
+ of captured_context::operator() the void pointer passed
+ to captured_context::operator(), in one context, is
+ returned by captured_context::operator() in the other
+ context.
+
+ 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().
+
+ 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. The void pointer argument, vp,
+ is passed to the current context to be returned by the most recent
+ call to captured_context::operator() in the same thread. [[Note:
+
+
+ The behaviour is undefined if operator()() is called while captured_context::current() returns *this (e.g. resuming an already running
+ context). If the top-level context function returns, std::exit()
+ is called.
+
+
+
Returns:
+
+ The void pointer argument passed to the most recent call to captured_context::operator(),
+ if any.
+
+ 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 void pointer argument, vp,
+ is passed to the current context to be returned by the most recent
+ call to captured_context::operator() in the same thread. fn is executed with arguments args on top of the stack of this. [[Note:
+
+
+ The behaviour is undefined if operator()() is called while captured_context::current() returns *this (e.g. resuming an already running
+ context). If the top-level context function returns, std::exit()
+ is called.
+
+
+
Returns:
+
+ The tuple of void pointer argument passed to the most recent call to
+ captured_context::operator(),
+ if any and a captured_context representing the context that has been
+ suspended .
+
+ true if *this!=other
+ is true and the implementation-defined total order of captured_context values places *this
+ before other, false
+ otherwise.
+
+ Class execution_context encapsulates fcontext_t
+ and manages the context' stack (allocation/deallocation).
+
+
+ execution_context allocates the context stack (using
+ its StackAllocator argument) and creates a control structure
+ on top of it. This structure controls the life time of the stack. Instances
+ of execution_context, associated with a specific context,
+ share the ownership of the control structure. If the last reference goes
+ out of scope, the control structure is destroyed and the stack gets deallocated
+ via the StackAllocator. execution_context
+ is copy-constructible, move-constructible, copy-assignable and move-assignable.
+ execution_context maintains a static, thread-local pointer
+ (smart pointer), accessed by execution_context::current(),
+ pointing to the active context. On each context switch the static thread-local
+ pointer is updated. The usage of this global pointer makes the context switch
+ a little bit slower (due access of thread local storage) but has some advantages.
+ It allows to access the control structure of the current active context from
+ arbitrary code paths required in order to support segmented stacks, which
+ need to call certain maintenance functions (__splitstack_getcontext etc.)
+ before each context switch (each context switch exchanges the stack). Additionally
+ the destruction of execution_context and thus the stack
+ deallocation is faster compared to captured_context.
+
+
+ execution_context expects a function/functor with signature
+ void(
+ void*vp) (
+ vp is the data passed at
+ the first invocation of execution_context::operator()).
+
/*
+ * grammar:
+ * P ---> E '\0'
+ * E ---> T {('+'|'-') T}
+ * T ---> S {('*'|'/') S}
+ * S ---> digit | '(' E ')'
+ */
+classParser{
+ // implementation omitted; see examples directory
+};
+
+intmain(){
+ std::istringstreamis("1+1");
+ booldone=false;
+ std::exception_ptrexcept;
+
+ // create handle to main execution context
+ automain_ctx(boost::context::execution_context::current());
+
+ // execute parser in new execution context
+ boost::context::execution_contextparser_ctx(
+ std::allocator_arg,
+ boost::context::fixedsize_stack(4096),
+ [&main_ctx,&is,&done,&except](void*){
+ // create parser with callback function
+ Parserp(is,
+ [&main_ctx,&c](charch){
+ // resume main execution context
+ main_ctx(&ch);
+ });
+ 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
+ main_ctx();
+ });
+
+ // user-code pulls parsed data from parser
+ // invert control flow
+ void*vp=parser_ctx();
+ if(except){
+ std::rethrow_exception(except);
+ }
+ while(!done){
+ printf("Parsed: %c\n",*static_cast<char*>(vp));
+ parser_ctx();
+ if(except){
+ std::rethrow_exception(except);
+ }
+ }
+
+ std::cout<<"main: done"<<std::endl;
+}
+
+output:
+ Parsed:1
+ Parsed:+
+ Parsed:1
+
+
+ In this example a recursive descent parser uses a callback to emit a newly
+ passed symbol. Using execution_context 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 execution_context.
+
+
+ If the code executed by execution_context 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.
+
+ Allocating control structures on top of the stack requires to allocated the
+ stack_context and create the control structure with
+ placement new before execution_context 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_stacksalloc(4048);
+// allocate stack space
+stack_contextsctx(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_tsize=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();
+...
+structmy_control_structure{
+ // execution context
+ execution_contextectx;
+
+ template<typenameStackAllocator>
+ my_control_structure(void*sp,std::size_tsize,stack_contextsctx,StackAllocatorsalloc):
+ // create execution context
+ ectx(std::allocator_arg,preallocated(sp,size,sctx),salloc,entry_func){
+ }
+ ...
+};
+
+ If the function executed inside a execution_context
+ emits ans exception, the application is terminated by calling ['std::terminate().
+ std::exception_ptr can be used to transfer exceptions
+ between different execution contexts.
+
+ The void pointer argument passed to execution_context::operator(),
+ in one context, is passed as the last argument of the context-function
+ if the context is started for the first time. In all following invocations
+ of execution_context::operator() the void pointer passed
+ to execution_context::operator(), in one context, is
+ returned by execution_context::operator() in the other
+ context.
+
+ 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().
+
+ 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 void pointer argument, vp,
+ is passed to the current context to be returned by the most recent
+ call to execution_context::operator() in the same thread. fn is executed with arguments args on top of the stack of this. [[Note:
+
+
+ The behaviour is undefined if operator()() is called while execution_context::current() returns *this (e.g. resuming an already running
+ context). If the top-level context function returns, std::exit()
+ is called.
+
+
+
Returns:
+
+ The void pointer argument passed to the most recent call to execution_context::operator(),
+ if any.
+
+ true if *this!=other
+ is true and the implementation-defined total order of execution_context values places
+ *this
+ before other, false
+ otherwise.
+
+ A StackAllocator must satisfy the stack-allocator
+ concept requirements shown in the following table, in which a is an object of a StackAllocator
+ type, sctx is a stack_context, and size
+ is a std::size_t:
+
+
+
+
+
+
+
+
+
+
+ expression
+
+
+
+
+ return type
+
+
+
+
+ notes
+
+
+
+
+
+
+
+ a(size)
+
+
+
+
+
+
+ creates a stack allocator
+
+
+
+
+
+
+ a.allocate()
+
+
+
+
+ stack_context
+
+
+
+
+ creates a stack
+
+
+
+
+
+
+ a.deallocate(
+ sctx)
+
+
+
+
+ void
+
+
+
+
+ deallocates the stack created by a.allocate()
+
+
+
+
+
+
+
+
+
Important
+
+
+ The implementation of allocate() might include logic to protect against
+ exceeding the context's available stack size rather than leaving it as
+ undefined behaviour.
+
+
+
+
+
+
Important
+
+
+ Calling deallocate()
+ with a stack_context not
+ set by allocate()
+ results in undefined behaviour.
+
+
+
+
+
+
Note
+
+
+ The stack is not required to be aligned; alignment takes place inside
+ execution_context.
+
+
+
+
+
+
Note
+
+
+ Depending on the architecture allocate() stores an address from the top of the
+ stack (growing downwards) or the bottom of the stack (growing upwards).
+
+ Boost.Context provides the class fixedsize_stack
+ which models the stack-allocator concept. In contrast
+ to protected_fixedsize_stack it does not append a
+ guard page at the end of each stack. The memory is simply managed by std::malloc()
+ and std::free().
+
+ Allocates memory of at least size
+ Bytes and stores a pointer to the stack and its actual size in sctx. Depending on the architecture
+ (the stack grows downwards/upwards) the stored address is the highest/lowest
+ address of the stack.
+
+ Boost.Context provides the class protected_fixedsize_stack
+ which models the stack-allocator concept. It appends
+ a guard page at the end of each stack to protect against exceeding the
+ stack. If the guard page is accessed (read or write operation) a segmentation
+ fault/access violation is generated by the operating system.
+
+
+
+
+
Important
+
+
+ Using protected_fixedsize_stack is expensive. That
+ is, launching a new coroutine with a new stack is expensive; the allocated
+ stack is just as efficient to use as any other stack.
+
+
+
+
+
+
Note
+
+
+ The appended guardpage
+ is not mapped to physical memory, only
+ virtual addresses are used.
+
+ Allocates memory of at least size
+ Bytes and stores a pointer to the stack and its actual size in sctx. Depending on the architecture
+ (the stack grows downwards/upwards) the stored address is the highest/lowest
+ address of the stack.
+
+ Boost.Context supports usage of a segmented_stack,
+ e. g. the size of the stack grows on demand. The coroutine is created with
+ a minimal stack size and will be increased as required. Class segmented_stack
+ models the stack-allocator concept. In contrast to
+ protected_fixedsize_stack and fixedsize_stack
+ it creates a stack which grows on demand.
+
+
+
+
+
Note
+
+
+ Segmented stacks are currently only supported by gcc
+ from version 4.7clang
+ from version 3.4 onwards. In order to
+ use a __segmented_stack__ Boost.Context
+ must be built with toolset=gcc segmented-stacks=on
+ at b2/bjam command-line. Applications must be compiled with compiler-flags
+ -fsplit-stack -DBOOST_USE_SEGMENTED_STACKS.
+
+ Allocates memory of at least size
+ Bytes and stores a pointer to the stack and its actual size in sctx. Depending on the architecture
+ (the stack grows downwards/upwards) the stored address is the highest/lowest
+ address of the stack.
+
+ Boost.Context provides the class stack_context
+ which will contain the stack pointer and the size of the stack. In case
+ of a segmented_stack, stack_context
+ contains some extra control structures.
+
+ stack_traits models a stack-traits
+ providing a way to access certain properites defined by the enironment.
+ Stack allocators use stack-traits to allocate stacks.
+
+ Returns a default stack size, which may be platform specific. If
+ the stack is unbounded then the present implementation returns the
+ maximum of 64kB
+ and minimum_size().
+
+ Running programs that switch stacks under valgrind causes problems. Property
+ (b2 command-line) valgrind=on let
+ valgrind treat the memory regions as stack space which suppresses the errors.
+
+ Class captured_context encapsulates fcontext_t
+ and manages the context' stack (allocation/deallocation).
+
+
+ captured_context allocates the context stack (using its
+ StackAllocator argument) and creates a control structure
+ on top of it. This structure controls the life time of the stack. The address
+ of the control structure is stored in the first frame of context' stack (e.g.
+ it can not accessed by instances of captured_context directly).
+ In contrast to execution_context the ownership of the
+ control structure is not shared A call ofcaptured_context::operator()
+ enters the context represented by *this and invalidates *this. The context that has been suspended by
+ calling captured_context::operator() is passed to the
+ resumed context, e.g. as argument of the context-function if the context was
+ resumed the first time or returned by captured_context::operator().
+ captured_context is only move-constructible and move-assignable.
+ If the last reference (captured_context) goes out of scope,
+ the control structure is destroyed and the stack gets deallocated via the
+ StackAllocator. captured_context
+ maintains a static, thread-local pointer (smart pointer), accessed by execution_context::current(),
+ pointing to the active context. On each context switch the static, thread-local
+ pointer is updated. This makes the context switch a little bit slower, but
+ enables faster context destruction (stack unwinding) compared to captured_context.
+
+
+ captured_context expects a function/functor with signature
+ captured_context(
+ captured_contextctx,void*
+ vp).
+ The parameter ctx represents
+ the context from which this context was resumed (e.g. that has called captured_context::operator()
+ on *this)
+ and vp is the data passed to
+ captured_context::operator(). The function/functor has
+ to return the captured_context that has to be resumed, while this context terminates.
+
+
+
+
+
Important
+
+
+ Segemnted stacks are not supported together with captured_context.
+
/*
+ * grammar:
+ * P ---> E '\0'
+ * E ---> T {('+'|'-') T}
+ * T ---> S {('*'|'/') S}
+ * S ---> digit | '(' E ')'
+ */
+classParser{
+ // implementation omitted; see examples directory
+};
+
+std::istringstreamis("1+1");
+booldone=false;
+std::exception_ptrexcept;
+
+// execute parser in new execution context
+boost::context::captured_contextpctx(
+ [&is,&done,&except](ctx::captured_contextmctx,void*ignored){
+ // create parser with callback function
+ Parserp(is,
+ [&mctx](charch){
+ // resume main execution context
+ autoresult=mctx(&ch);
+ mctx=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
+ returnmctx;
+ });
+
+// user-code pulls parsed data from parser
+// invert control flow
+autoresult=pctx();
+pctx=std::move(std::get<0>(result));
+void*vp=std::get<1>(result);
+if(except){
+ std::rethrow_exception(except);
+}
+while(!done){
+ printf("Parsed: %c\n",*static_cast<char*>(vp));
+ std::tie(pctx,vp)=pctx();
+ 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 captured_context 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 captured_context.
+
+
+ If the code executed by captured_context 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.
+
+ Allocating control structures on top of the stack requires to allocated the
+ stack_context and create the control structure with placement
+ new before captured_context 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_stacksalloc(4048);
+// allocate stack space
+stack_contextsctx(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_tsize=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();
+...
+structmy_control_structure{
+ // captured context
+ captured_contextcctx;
+
+ template<typenameStackAllocator>
+ my_control_structure(void*sp,std::size_tsize,stack_contextsctx,StackAllocatorsalloc):
+ // create captured context
+ cctx(std::allocator_arg,preallocated(sp,size,sctx),salloc,entry_func){
+ }
+ ...
+};
+
+ If the function executed inside a captured_context emits
+ ans exception, the application is terminated by calling ['std::terminate().
+ std::exception_ptr can be used to transfer exceptions
+ between different execution contexts.
+
+ The void pointer argument passed to captured_context::operator(),
+ in one context, is passed as the last argument of the context-function
+ if the context is started for the first time. In all following invocations
+ of captured_context::operator() the void pointer passed
+ to captured_context::operator(), in one context, is returned
+ by captured_context::operator() in the other context.
+
+ 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().
+
+
+
+
+ [[Effects:] [Creates a new execution context and prepares the context to execute
+ fn.]] ] [[Effects:] [Creates
+ a new execution context and prepares the context to execute fn.
+ Used to store control structures on top of the stack.]] ]
+
+ 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 void pointer argument, vp,
+ is passed to the current context to be returned by the most recent call
+ to captured_context::operator()
+ in the same thread. fn
+ is executed with arguments args
+ on top of the stack of this.
+ [[Note:
+
+
+ The behaviour is undefined if operator()() is called while captured_context::current() returns *this (e.g. resuming an already running
+ context). If the top-level context function returns, std::exit() is called.
+
+
+
Returns:
+
+ The tuple of void pointer argument passed to the most recent call to
+ captured_context::operator(),
+ if any and a captured_context representing the context that has been
+ suspended .
+
+ true if *this!=other is true and the implementation-defined
+ total order of captured_context
+ values places *this
+ before other, false otherwise.
+
A new context supposed to execute a context-function (returning
- void and accepting intptr_t as argument) will be created on top of the stack
+ void and accepting void * as argument) will be created on top of the stack
(at 16 byte boundary) by function make_fcontext().
// context-function
@@ -98,7 +98,7 @@
boost::context::fcontext_tfcm,fc1,fc2;
-voidf1(intptr_t)
+voidf1(void*){std::cout<<"f1: entered"<<std::endl;std::cout<<"f1: call jump_fcontext( & fc1, fc2, 0)"<<std::endl;
@@ -107,7 +107,7 @@
boost::context::jump_fcontext(&fc1,fcm,0);}
-voidf2(intptr_t)
+voidf2(void*){std::cout<<"f2: entered"<<std::endl;std::cout<<"f2: call jump_fcontext( & fc2, fc1, 0)"<<std::endl;
@@ -180,7 +180,7 @@
The third argument passed to jump_fcontext(), in one context,
is passed as the first argument of the context-function
if the context is started for the first time. In all following invocations
- of jump_fcontext() the intptr_t passed to jump_fcontext(),
+ of jump_fcontext() the void * passed to jump_fcontext(),
in one context, is returned by jump_fcontext() in the
other context.
@@ -188,11 +188,11 @@
typedefstd::pair<int,int>pair_t;
-voidf(intptr_tparam)
+voidf(void*param){pair_t*p=(pair_t*)param;
- p=(pair_t*)boost::context::jump_fcontext(&fc,fcm,(intptr_t)(p->first+p->second));
- boost::context::jump_fcontext(&fc,fcm,(intptr_t)(p->first+p->second));
+ p=(pair_t*)boost::context::jump_fcontext(&fc,fcm,(void*)(p->first+p->second));
+ boost::context::jump_fcontext(&fc,fcm,(void*)(p->first+p->second));}std::size_tsize(8192);
@@ -201,11 +201,11 @@
pair_tp(std::make_pair(2,7));fc=boost::context::make_fcontext(sp,size,f);
-intres=(int)boost::context::jump_fcontext(&fcm,fc,(intptr_t)&p);
+intres=(int)boost::context::jump_fcontext(&fcm,fc,(void*)&p);std::cout<<p.first<<" + "<<p.second<<" == "<<res<<std::endl;p=std::make_pair(5,6);
-res=(int)boost::context::jump_fcontext(&fcm,fc,(intptr_t)&p);
+res=(int)boost::context::jump_fcontext(&fcm,fc,(void*)&p);std::cout<<p.first<<" + "<<p.second<<" == "<<res<<std::endl;output:
@@ -243,26 +243,6 @@
- Preserving the floating point registers increases the cycle count for a context
- switch (see performance tests). The fourth argument of jump_fcontext()
- controls if fpu registers should be preserved by the context jump.
-
-
-
-
-
Important
-
-
- The use of the fpu controlling argument of jump_fcontext()
- must be consistent in the application. Otherwise the behaviour is undefined.
-
@@ -339,12 +319,11 @@
Stores the current context data (stack pointer, instruction pointer,
and CPU registers) to *ofc and restores the context data from
nfc, which implies jumping
- to nfc's execution context.
- The intptr_t argument, p,
+ to execution context nfc.
+ The void * argument, p,
is passed to the current context to be returned by the most recent call
to jump_fcontext()
- in the same thread. The last argument controls if fpu registers have
- to be preserved.
+ in the same thread.