diff --git a/build/Jamfile.v2 b/build/Jamfile.v2
new file mode 100644
index 0000000..dd366a5
--- /dev/null
+++ b/build/Jamfile.v2
@@ -0,0 +1,225 @@
+
+# Boost.Context Library Build Jamfile
+
+# Copyright Oliver Kowalke 2009.
+# 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)
+
+import common ;
+import feature ;
+import indirect ;
+import modules ;
+import os ;
+import toolset ;
+
+project boost/context
+ : source-location ../src
+ : usage-requirements
+ shared:BOOST_CONTEXT_DYN_LINK=1
+ ;
+
+local rule default_binary_format ( )
+{
+ local tmp = elf ;
+ if [ os.name ] = "MACOSX" { tmp = mach-o ; }
+ if [ os.name ] = "NT" { tmp = pe ; }
+ return $(tmp) ;
+}
+
+feature.feature binary-format
+ : elf
+ mach-o
+ pe
+ : propagated
+ ;
+feature.set-default binary-format : [ default_binary_format ] ;
+
+
+local rule default_abi ( )
+{
+ local tmp = sysv ;
+ if [ os.name ] = "NT" { tmp = ms ; }
+ else if [ os.platform ] = "ARM" { tmp = aapcs ; }
+ else if [ os.platform ] = "MIPS" { tmp = o32 ; }
+ return $(tmp) ;
+}
+
+feature.feature abi
+ : aapcs
+ eabi
+ ms
+ n32
+ n64
+ o32
+ o64
+ sysv
+ : propagated
+ ;
+feature.set-default abi : [ default_abi ] ;
+
+
+alias asm_context_sources
+ : stack_allocator_posix.cpp
+ asm/fcontext_arm_aapcs_elf_gas.S
+ stack_utils_posix.cpp
+ : aapcs
+ arm
+ elf
+ ;
+
+alias asm_context_sources
+ : stack_allocator_posix.cpp
+ asm/fcontext_mips32_o32_elf_gas.S
+ stack_utils_posix.cpp
+ : o32
+ mips1
+ elf
+ ;
+
+#alias asm_context_sources
+# : stack_allocator_posix.cpp
+# asm/fcontext_mips64_n32_elf_gas.S
+# stack_utils_posix.cpp
+# : n32
+# mips1
+# elf
+# ;
+#
+#alias asm_context_sources
+# : stack_allocator_posix.cpp
+# asm/fcontext_mips64_n64_elf_gas.S
+# stack_utils_posix.cpp
+# : n64
+# mips1
+# elf
+# ;
+#
+#alias asm_context_sources
+# : stack_allocator_posix.cpp
+# asm/fcontext_mips64_o64_elf_gas.S
+# stack_utils_posix.cpp
+# : o64
+# mips1
+# elf
+# ;
+
+alias asm_context_sources
+ : stack_allocator_posix.cpp
+ asm/fcontext_ppc32_eabi_elf_gas.S
+ stack_utils_posix.cpp
+ : eabi
+ 32
+ power
+ elf
+ ;
+
+alias asm_context_sources
+ : stack_allocator_posix.cpp
+ asm/fcontext_ppc32_sysv_elf_gas.S
+ stack_utils_posix.cpp
+ : sysv
+ 32
+ power
+ elf
+ ;
+
+alias asm_context_sources
+ : stack_allocator_posix.cpp
+ asm/fcontext_ppc64_eabi_elf_gas.S
+ stack_utils_posix.cpp
+ : eabi
+ 64
+ power
+ elf
+ ;
+
+alias asm_context_sources
+ : stack_allocator_posix.cpp
+ asm/fcontext_ppc64_sysv_elf_gas.S
+ stack_utils_posix.cpp
+ : sysv
+ 64
+ power
+ elf
+ ;
+
+alias asm_context_sources
+ : stack_allocator_posix.cpp
+ asm/fcontext_i386_sysv_elf_gas.S
+ stack_utils_posix.cpp
+ : sysv
+ 32
+ x86
+ elf
+ ;
+
+alias asm_context_sources
+ : stack_allocator_posix.cpp
+ asm/fcontext_x86_64_sysv_elf_gas.S
+ stack_utils_posix.cpp
+ : sysv
+ 64
+ x86
+ elf
+ ;
+
+alias asm_context_sources
+ : stack_allocator_posix.cpp
+ asm/fcontext_x86_64_sysv_macho_gas.S
+ stack_utils_posix.cpp
+ : sysv
+ 64
+ x86
+ mach-o
+ darwin
+ ;
+
+alias asm_context_sources
+ : stack_allocator_windows.cpp
+ asm/fcontext_i386_ms_pe_masm.asm
+ stack_utils_windows.cpp
+ : ms
+ 32
+ x86
+ pe
+ windows
+ ;
+
+alias asm_context_sources
+ : stack_allocator_windows.cpp
+ asm/fcontext_x86_64_ms_pe_masm.asm
+ stack_utils_windows.cpp
+ : ms
+ 64
+ x86
+ pe
+ windows
+ ;
+
+explicit asm_context_sources ;
+
+
+alias context_sources
+ : stack_allocator_windows.cpp
+ stack_utils_windows.cpp
+ seh.cpp
+ : windows
+ ;
+
+alias context_sources
+ : stack_allocator_posix.cpp
+ stack_utils_posix.cpp
+ ;
+
+explicit context_sources ;
+
+
+lib boost_context
+ : asm_context_sources
+ context_sources
+ fcontext.cpp
+ : shared:BOOST_CONTEXT_DYN_LINK=1
+ ;
+
+boost-install boost_context ;
diff --git a/doc/Jamfile.v2 b/doc/Jamfile.v2
new file mode 100644
index 0000000..dfc9d6e
--- /dev/null
+++ b/doc/Jamfile.v2
@@ -0,0 +1,33 @@
+# (C) Copyright 2008 Anthony Williams
+#
+# 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)
+
+path-constant boost-images : ../../../doc/src/images ;
+
+xml context : context.qbk ;
+
+boostbook standalone
+ :
+ context
+ :
+ # HTML options first:
+ # Use graphics not text for navigation:
+ navig.graphics=1
+ # How far down we chunk nested sections, basically all of them:
+ chunk.section.depth=3
+ # Don't put the first section on the same page as the TOC:
+ chunk.first.sections=1
+ # How far down sections get TOC's
+ toc.section.depth=10
+ # Max depth in each TOC:
+ toc.max.depth=3
+ # How far down we go with TOC's
+ generate.section.toc.level=10
+ # Path for links to Boost:
+ boost.root=../../../..
+ # Path for libraries index:
+ boost.libraries=../../../../libs/libraries.htm
+ # Use the main Boost stylesheet:
+ html.stylesheet=../../../../doc/html/boostbook.css
+ ;
diff --git a/doc/acknowledgements.qbk b/doc/acknowledgements.qbk
new file mode 100644
index 0000000..7ce88d8
--- /dev/null
+++ b/doc/acknowledgements.qbk
@@ -0,0 +1,15 @@
+[/
+ Copyright Oliver Kowalke 2009.
+ Distributed under the Boost Software License, Version 1.0.
+ (See accompanying file LICENSE_1_0.txt or copy at
+ http://www.boost.org/LICENSE_1_0.txt
+]
+
+[section:acknowledgements Acknowledgments]
+
+I'd like to thank Adreas Fett, Artyom Beilis, Fernando Pelliccioni,
+Giovanni Piero Deretta, Gordon Woodhull, Helge Bahmann, Holger Grund,
+Jeffrey Lee Hellrung (Jr.), Keith Jeffery, Phil Endecott, Robert Stewart,
+Vicente J. Botet Escriba.
+
+[endsect]
diff --git a/doc/asio.qbk b/doc/asio.qbk
new file mode 100644
index 0000000..dbfa3da
--- /dev/null
+++ b/doc/asio.qbk
@@ -0,0 +1,168 @@
+[/
+ Copyright Oliver Kowalke 2009.
+ Distributed under the Boost Software License, Version 1.0.
+ (See accompanying file LICENSE_1_0.txt or copy at
+ http://www.boost.org/LICENSE_1_0.txt
+]
+
+[section:asio ASIO]
+
+Adapted from [@http://blog.think-async.com/2009/08/secret-sauce-revealed.html
+Thinking Asynchronously in C++] using boost.asio to implement a simple echo
+server.
+
+Chris Kohlhoff uses stackless coroutines based on preprocessor-based switch
+statements to implement an echo server using boost.asio.
+Class `continuation` provides a simple coroutine implemenatation using
+boost.context.
+
+ class continuation
+ {
+ private:
+ boost::contexts::context ctx_;
+ boost::function< void( continuation &) > fn_;
+ bool started_;
+
+ void trampoline_()
+ { fn_( * this); }
+
+ public:
+ continuation( boost::function< void( continuation &) > const& fn) :
+ ctx_(
+ & continuation::trampoline_, this,
+ boost::contexts::default_stacksize(),
+ boost::contexts::no_stack_unwind, boost::contexts::return_to_caller),
+ fn_( fn), started_( false)
+ {}
+
+ void resume()
+ {
+ if ( ! started_)
+ {
+ started_ = true;
+ ctx_.start();
+ }
+ else ctx_.resume();
+ }
+
+ void suspend()
+ { ctx_.suspend(); }
+
+ bool is_complete() const
+ { return ctx_.is_complete(); }
+ };
+
+The operator `server::operator()` is called by the asio reactor and jumps to
+coroutine (which is entering or resuming in function `server::do_()`).
+In function `sever::do_()` the control flow is all in one place and easy to
+follow; *this is queued for accepting a connection before control flow
+is passed back to the reactor via `suspend()`. If `server::do_()` is reentered,
+a local buffer is created and an asynch. read operation is queued into the
+reactor.
+The control flow is then returned to the reactor for io-multiplexing again.
+After some bytes were received `server::do_()` is reentred again starting a
+asynch. write operation.
+
+The advantage over the ordinary `bind()` version is that the state of execution
+is preserved so that variables used by the read/write operations may have local
+scope. Stackfull coroutines (as boost.context provides) enable
+application-protocol stacks which can be more easily implemented.
+For instance, if only part of a message is received (some bytes are outstanding
+before message can be parsed or serviced), then the current state can be easily
+suspended/preserved (even from nested functions of the protocol stack)
+until the remaining bytes are received.
+
+ class server : public boost::enable_shared_from_this< server >,
+ private continuation
+ {
+ private:
+ boost::asio::ip::tcp::acceptor acceptor_;
+ boost::system::error_code ec_;
+ std::size_t n_;
+
+ void do_()
+ {
+ for (;;)
+ {
+ boost::asio::ip::tcp::socket socket( acceptor_.get_io_service() );
+ // accept connection request and enque *this for reading data from conenction
+ acceptor_.async_accept( socket, boost::bind( & server::operator(), this->shared_from_this(), _1, 0) );
+ // suspend() jumps back to operator() which returns to asio reactor
+ suspend();
+
+ while ( ! ec_)
+ {
+ // buffer has local scope!
+ boost::array< char, 1024 > buffer;
+
+ // enqueue *this to read some bytes from the connection into buffer
+ socket.async_read_some(
+ boost::asio::buffer( buffer),
+ boost::bind( & server::operator(), this->shared_from_this(), _1, _2) );
+ // suspend() jumps back to operator(); asio reactor
+ suspend();
+
+ // finish and return
+ if ( ec_) break;
+
+ // enqueue *this to write data from buffer to connection
+ boost::asio::async_write(
+ socket,
+ boost::asio::buffer( buffer, n_),
+ boost::bind( & server::operator(), this->shared_from_this(), _1, _2) );
+ // suspend() jumps back to operator(); asio reactor
+ suspend();
+ }
+ }
+ }
+
+ server( boost::asio::io_service & io_service, short port) :
+ continuation( boost::bind( & server::do_, this) ),
+ acceptor_( io_service, boost::asio::ip::tcp::endpoint( boost::asio::ip::tcp::v4(), port) ),
+ ec_(), n_( 0)
+ {}
+
+ public:
+ typedef boost::shared_ptr< server > ptr_t;
+
+ static ptr_t create( boost::asio::io_service & io_service, short port)
+ { return ptr_t( new server( io_service, port) ); }
+
+ void operator()( boost::system::error_code ec, size_t n)
+ {
+ ec_ = ec;
+ n_ = n;
+ // jump to coroutine (do_())
+ resume();
+ }
+ };
+
+ int main( int argc, char * argv[])
+ {
+ try
+ {
+ if ( argc != 2)
+ {
+ std::cerr << "Usage: echo_server \n";
+ return 1;
+ }
+ {
+ boost::asio::io_service io_service;
+ io_service.post(
+ boost::bind(
+ & server::operator(),
+ server::create( io_service, boost::lexical_cast< short >( argv[1]) ),
+ boost::system::error_code(), 0) );
+ io_service.run();
+ }
+ std::cout << "Done" << std::endl;
+
+ return EXIT_SUCCESS;
+ }
+ catch ( std::exception const& e)
+ { std::cerr << "Exception: " << e.what() << std::endl; }
+
+ return EXIT_FAILURE;
+ }
+
+[endsect]
diff --git a/doc/config.qbk b/doc/config.qbk
new file mode 100644
index 0000000..7716a48
--- /dev/null
+++ b/doc/config.qbk
@@ -0,0 +1,60 @@
+[/
+ Copyright Oliver Kowalke 2009.
+ Distributed under the Boost Software License, Version 1.0.
+ (See accompanying file LICENSE_1_0.txt or copy at
+ http://www.boost.org/LICENSE_1_0.txt
+]
+
+[section:installtion How to build and install]
+
+__boost_context__ must be built for the particular compiler(s) and CPU architecture(s)s
+being targeted. __boost_context__ includes assembly code and, therefore, requires
+GNU AS for supported POSIX systems, and MASM for Windows systems.
+
+[note The architecture, instruction set, and address model are optional __boost_build__
+properties that must be given on the bjam command line, as shown in the table below.]
+
+[table
+ [[][]]
+ [
+ [ARM, UNIX, aapcs, elf]
+ [bjam toolset = gcc architecture = arm]
+ ]
+ [
+ [MIPS (32bit), UNIX, o32, elf]
+ [bjam toolset = gcc architecture = mips1]
+ ]
+ [
+ [I386, UNIX, sysv, elf]
+ [bjam toolset = gcc architecture = x86 instruction-set = i686 address-model = 32]
+ ]
+ [
+ [I386, UNIX, sysv, elf]
+ [bjam toolset = intel architecture = x86 instruction-set = i686 address-model = 32]
+ ]
+ [
+ [I386, Windows, ms, pe]
+ [bjam toolset = msvc-9.0 architecture = x86 instruction-set = i686 address-model = 32]
+ ]
+ [
+ [PowerPc (32bit), UNIX, sysv, elf]
+ [bjam toolset = gcc architecture = power address-model = 32]
+ ]
+ [
+ [PowerPc (64bit), UNIX, sysv, elf]
+ [bjam toolset = gcc architecture = power address-model = 64]
+ ]
+ [
+ [X86_64, UNIX, sysv, elf]
+ [bjam toolset = gcc architecture = x86 instruction-set = yorksfield address-model = 64]
+ ]
+ [
+ [X86_64, UNIX, sysv, elf]
+ [bjam toolset = intel architecture = x86 instruction-set = yorksfield address-model = 64]
+ ]
+ [
+ [X86_64, Windows, ms, pe]
+ [bjam toolset = msvc-10.0 architecture = x86 instruction-set = yorksfield address-model = 64]
+ ]
+]
+[endsect]
diff --git a/doc/context.qbk b/doc/context.qbk
new file mode 100644
index 0000000..1165380
--- /dev/null
+++ b/doc/context.qbk
@@ -0,0 +1,79 @@
+[/
+ Copyright Oliver Kowalke 2009.
+ 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
+]
+
+[article Context
+ [quickbook 1.4]
+ [authors [Kowalke, Oliver]]
+ [copyright 2009 Oliver Kowalke]
+ [purpose C++ Library for swiching different user contexts]
+ [category text]
+ [license
+ 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])
+ ]
+]
+
+
+[def __boost_build__ [*Boost.Build]]
+[def __boost_context__ [*Boost.Context]]
+[def __boost_fiber__ [*Boost.Fiber]]
+[def __boost_move__ [*Boost.Move]]
+[def __boost_task__ [*Boost.Task]]
+
+[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]]]
+
+[def __context_fn__ ['context-function]]
+[def __coroutine__ ['coroutine]]
+[def __coroutines__ ['coroutines]]
+[def __coop_threads__ ['cooperative threads (userland threads)]]
+[def __fls__ ['fiber-local storage]]
+[def __guard_page__ ['guard-page]]
+[def __not_a_context__ ['not-a-context]]
+[def __stack__ [stack_link ['stack]]]
+[def __thread__ ['thread]]
+[def __threads__ ['threads]]
+[def __tls__ ['thread-local storage]]
+[def __stack_allocator__ ['StackAllocator]]
+[def __stack_allocator_concept__ ['StackAllocator concept]]
+[def __yield__ ['yield]]
+
+[def __fcontext__ ['fcontext_t]]
+[def __ucontext__ ['ucontext_t]]
+
+[def __fls_alloc__ ['::FlsAlloc()]]
+[def __fls_free__ ['::FlsFree()]]
+
+[def __bad_alloc__ ['std::bad_alloc]]
+[def __context__ [context_link ['context]]]
+[def __context_start__ ['context::start()]]
+[def __context_resume__ ['context::resume()]]
+[def __context_suspend__ ['context::suspend()]]
+[def __context_unwind__ ['context::unwind_stack()]]
+[def __forced_unwind__ ['forced_unwind]]
+[def __invalid_argument__ ['std::invalid_argument]]
+[def __stack_alloc__ ['allocate()]]
+[def __stack_dealloc__ ['deallocate()]]
+[def __stack_helper__ ['stack_helper]]
+
+[def __context_ns__ ['boost::contexts]]
+
+[include overview.qbk]
+[include config.qbk]
+[include context_ref.qbk]
+[include stack_ref.qbk]
+[include fcontext.qbk]
+[include examples.qbk]
+[include performance.qbk]
+[include tested.qbk]
+[include rationale.qbk]
+[include reference.qbk]
+[include todo.qbk]
+[include acknowledgements.qbk]
diff --git a/doc/context.xml b/doc/context.xml
new file mode 100644
index 0000000..7da58b7
--- /dev/null
+++ b/doc/context.xml
@@ -0,0 +1,3473 @@
+
+
+
+ Context
+
+
+
+ OliverKowalke
+
+
+
+ 2009Oliver Kowalke
+
+
+
+ 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)
+
+
+
+ C++ Library for swiching different user contexts
+
+
+
+
+ Overview
+
+ Boost.Context is a foundational library that
+ 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 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 aquivalent
+ to C#
+ keyword yield in C++ ( see
+ enumerator example).
+
+
+ A context
+ provides the means to suspend the current execution path and to transfer execution
+ control, thereby permitting another context
+ to run on the current thread. This stateful 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 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),
+ which can cost thousands of CPU cycles on x86 CPUs. By contrast, transferring
+ control among them requires only a few hundred CPU cycles because it does not
+ involve system calls as it is done within a single thread.
+
+
+ In order to use the classes and functions described here, you can either include
+ the specific headers specified by the descriptions of each class or function,
+ or include the master library header:
+
+
+#include<boost/context/all.hpp>
+
+
+ which includes all the other headers in turn.
+
+
+ All functions and classes are contained in the namespace boost::contexts.
+
+
+ Boost.Context depends on Boost.Move.
+
+
+
+ How to build and install
+
+ Boost.Context must be built for the particular
+ compiler(s) and CPU architecture(s)s being targeted. Boost.Context
+ includes assembly code and, therefore, requires GNU AS for supported POSIX
+ systems, and MASM for Windows systems.
+
+
+
+ The architecture, instruction set, and address model are optional Boost.Build properties that must be given on the bjam
+ command line, as shown in the table below.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ARM, UNIX, aapcs, elf
+
+
+
+
+ bjam toolset = gcc architecture = arm
+
+
+
+
+
+
+ MIPS (32bit), UNIX, o32, elf
+
+
+
+
+ bjam toolset = gcc architecture = mips1
+
+ Because of a bug in shared_ptr you have to apply a patch 'patches/llsc-mips.patch'
+ to shared_ptr or provide cxxflags="-mips2" at bjam command-line.
+
+
+
+
+
+
+
+
+ I386, UNIX, sysv, elf
+
+
+
+
+ bjam toolset = gcc architecture = x86 instruction-set = i686 address-model
+ = 32
+
+
+
+
+
+
+ I386, UNIX, sysv, elf
+
+
+
+
+ bjam toolset = intel architecture = x86 instruction-set = i686 address-model
+ = 32
+
+
+
+
+
+
+ I386, Windows, ms, pe
+
+
+
+
+ bjam toolset = msvc-9.0 architecture = x86 instruction-set = i686
+ address-model = 32
+
+
+
+
+
+
+ PowerPc (32bit), UNIX, sysv, elf
+
+
+
+
+ bjam toolset = gcc architecture = power address-model = 32
+
+
+
+
+
+
+ PowerPc (64bit), UNIX, sysv, elf
+
+
+
+
+ bjam toolset = gcc architecture = power address-model = 64
+
+
+
+
+
+
+ X86_64, UNIX, sysv, elf
+
+
+
+
+ bjam toolset = gcc architecture = x86 instruction-set = yorksfield
+ address-model = 64
+
+
+
+
+
+
+ X86_64, UNIX, sysv, elf
+
+
+
+
+ bjam toolset = intel architecture = x86 instruction-set = yorksfield
+ address-model = 64
+
+
+
+
+
+
+ X86_64, Windows, ms, pe
+
+
+
+
+ bjam toolset = msvc-10.0 architecture = x86 instruction-set = yorksfield
+ address-model = 64
+
+
+
+
+
+
+
+
+ Context
+
+ Each instance of context
+ represents a context (CPU registers and stack space) of execution or not-a-context.
+ Objects of type context
+ are moveable but not copyable and can be returned by a function.
+
+
+boost::contexts::contextmake_context();
+
+voidf()
+{
+ boost::contexts::contextctx(make_context());
+ ctx.resume();
+}
+
+
+
+ Boost.Move is used to emulate rvalue references.
+
+
+
+
+ If contexts are used in a multithreaded application, they can migrated between
+ threads, but must not reference thread-local storage.
+
+
+
+
+ If fiber-local storage is used on Windows, the user
+ is responsible for calling ::FlsAlloc(), ::FlsFree().
+
+
+
+
+ Executing a context
+
+
+ A new context is created from a callable object (known as the context-function).
+ The stack size, stack unwinding behavior, and context termination behavior
+ are determined by additional arguments.
+
+
+ The context
+ constructor uses a StackAllocator concept to allocate
+ an associated stack, and the destructor uses the same StackAllocator
+ concept to deallocate the stack. The default StackAllocator
+ concept is StackAllocator, but a custom stack-allocator
+ can be passed to the constructor.
+
+
+ Additional arguments needed by a context-function are
+ supplied as additional arguments to the context constructor.
+
+
+voidf(inti);
+
+boost::contexts::contextctx(f,42,
+ boost::contexts::default_stacksize(),
+ boost::contexts::no_stack_unwind,
+ boost::contexts::return_to_caller);
+
+
+ The context-function, as well as its arguments, if any,
+ are copied into the context's state. If a reference is required, use boost::ref.
+
+
+ The maximum number of arguments of context-function is
+ defined by BOOST_CONTEXT_ARITY (default is 10 and might be increased).
+
+
+ Calling context::start() 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 context::suspend(). The current context information
+ (registers, flags, and stack and instruction pointers) is saved and the original
+ context information is restored. Calling context::resume()
+ resumes execution in the second context after saving the new state of the original
+ context. Note that context::start() must be called first
+ and only once.
+
+
+boost::contexts::contextctx;
+
+voidfn(intj)
+{
+ for(inti=0;i<j;++i)
+ {
+ std::cout<<"fn(): local variable i == "<<i<<std::endl;
+
+ // save current context
+// value of local variable is preserved
+// transfer execution control back to main()
+ctx.suspend();
+
+ // ctx.resume() was called
+// execution control transfered back from main()
+}
+}
+
+intmain(intargc,char*argv[])
+{
+ ctx=boost::contexts::context(fn,7,
+ boost::contexts::default_stacksize(),
+ boost::contexts::no_stack_unwind,
+ boost::contexts::return_to_caller);
+
+ std::cout<<"main() starts context ctx"<<std::endl;
+ // start the context ctx for the first time; enter fn()
+ctx.start();
+
+ // ctx.suspend() was called so we returned from start()
+while(!ctx.is_complete())
+ {
+ std::cout<<"main() calls context ctx"<<std::endl;
+ // resume inside fn()
+// execution control is transfered to ctx
+ctx.resume();
+
+ // ctx.suspend() was called within fn()
+}
+
+ std::cout<<"Done"<<std::endl;
+
+ returnEXIT_SUCCESS;
+}
+
+output:
+ main()startscontextctx
+ fn():localvariablei==0
+ main()callscontextctx
+ fn():localvariablei==1
+ main()callscontextctx
+ fn():localvariablei==2
+ main()callscontextctx
+ fn():localvariablei==3
+ main()callscontextctx
+ fn():localvariablei==4
+ main()callscontextctx
+ fn():localvariablei==5
+ main()callscontextctx
+ fn():localvariablei==6
+ main()callscontextctx
+ Done
+
+
+
+ Calling context::resume() from inside the same context
+ results in undefined behaviour.
+
+
+
+
+ In contrast to threads, which are preemtive, context
+ switches are cooperative (programmer controls when switch will happen). The
+ kernel is not involved in the context switches.
+
+
+
+
+ Transfer of data
+
+
+ The argument passed to context::resume(), in one context,
+ is returned by context::suspend() in the other context.
+ The intptr_t passed to context::suspend(), in one context,
+ is returned by context::resume() (or context::start(),
+ depending upon which function was called previously) in the other context.
+ context::start() has no argument because it enters the
+ start of the function, and there is no previous call to context::suspend()
+ to return a value.
+
+
+boost::contexts::contextctx;
+
+voidfn()
+{
+ inti=7;
+ std::cout<<"fn(): local variable i == "<<i<<std::endl;
+
+ // save current context
+// transfer execution control back to caller
+// pass content of variable back
+intj=ctx.suspend(i);
+ // j == 10 because x == 10 in main()
+
+ std::cout<<"transfered value: "<<j<<std::endl;
+}
+
+intmain(intargc,char*argv[])
+{
+ ctx=boost::contexts::context(fn,
+ boost::contexts::default_stacksize(),
+ boost::contexts::no_stack_unwind,
+ boost::contexts::return_to_caller);
+
+ std::cout<<"main() calls context ctx"<<std::endl;
+
+ // start the context ctx for the first time
+// enter fn()
+intx=ctx.start();
+ // x == 7 because i == 7 in fn()
+
+ std::cout<<"transfered value: "<<x<<std::endl;
+
+ x=10;
+ // ctx.suspend() was called so we returned from start()
+ctx.resume(x);
+
+ std::cout<<"Done"<<std::endl;
+
+ returnEXIT_SUCCESS;
+}
+
+output:
+ main()callscontextctx
+ fn():localvariablei==7
+ transferedvalue:7
+ transferedvalue:10
+ Done
+
+
+
+ Exceptions
+ in context-function
+
+
+ If the context-function emits an exception, std::terminate()
+ is called.
+
+
+
+ Code executed by context must not prevent the propagation of the forced_unwind
+ exception. Absorbing that exception will cause stack unwinding to fail. Thus,
+ any code that catches all exceptions must rethrow the pending exception.
+
+
+
+try
+{
+ // code that might throw
+}
+catch(forced_unwind)
+{
+ throw;
+}
+catch(...)
+{
+ // possibly not rethrow pending exception
+}
+
+
+
+ Handling
+ execution context termination
+
+
+ The last context constructor argument is an enumerated type which determines
+ what should happen when the context is complete (that is, when the context-function
+ returns). The available values are return_to_caller
+ and exit_application.
+
+
+ When the last constructor argument is return_to_caller,
+ execution control is transferred back to the last invocation of context::start()
+ or context::resume() when the context is complete, as
+ shown in this example:
+
+
+boost::contexts::contextctx;
+
+voidfn(intj)
+{
+ for(inti=0;i<j;++i)
+ {
+ std::cout<<"fn(): local variable i == "<<i<<std::endl;
+ ctx.suspend();
+ }
+
+ // fn() returns
+// execution control is transfered back to main()
+}
+
+intmain(intargc,char*argv[])
+{
+ ctx=boost::contexts::context(fn,7,
+ boost::contexts::default_stacksize(),
+ boost::contexts::no_stack_unwind,
+ boost::contexts::return_to_caller);
+
+ ctx.start();
+ while(!ctx.is_complete())
+ {
+ std::cout<<"main() calls context ctx"<<std::endl;
+ ctx.resume();
+ }
+
+ // fn() returned; the context is complete
+
+ std::cout<<"Done"<<std::endl;
+
+ returnEXIT_SUCCESS;
+}
+
+output:
+ fn():localvariablei==0
+ main()callscontextctx
+ fn():localvariablei==1
+ main()callscontextctx
+ fn():localvariablei==2
+ main()callscontextctx
+ fn():localvariablei==3
+ main()callscontextctx
+ fn():localvariablei==4
+ main()callscontextctx
+ fn():localvariablei==5
+ main()callscontextctx
+ fn():localvariablei==6
+ main()callscontextctx
+ Done
+
+
+ When the last constructor argument is exit_application,
+ the application terminates with an exit status of zero when the context is
+ complete, as shown in the following example:
+
+
+boost::contexts::contextctx;
+
+voidfn(intj)
+{
+ for(inti=0;i<j;++i)
+ {
+ std::cout<<"fn(): local variable i == "<<i<<std::endl;
+ ctx.suspend();
+ }
+
+ // fn() returns
+// application will terminate
+}
+
+intmain(intargc,char*argv[])
+{
+ ctx=boost::contexts::context(fn,7,
+ boost::contexts::default_stacksize(),
+ boost::contexts::no_stack_unwind,
+ boost::contexts::exit_application);
+
+ ctx.start();
+ while(!ctx.is_complete())
+ {
+ std::cout<<"main() calls context ctx"<<std::endl;
+ ctx.resume();
+ }
+
+ // never reached
+returnEXIT_SUCCESS;
+}
+
+output:
+ fn()callscontextctx
+ fn():localvariablei==1
+ main()callscontextctx
+ fn():localvariablei==2
+ main()callscontextctx
+ fn():localvariablei==3
+ main()callscontextctx
+ fn():localvariablei==4
+ main()callscontextctx
+ fn():localvariablei==5
+ main()callscontextctx
+ fn():localvariablei==6
+ main()callscontextctx
+
+
+
+ Chaining contexts
+
+
+ Boost.Context provides the ability to chain
+ context instances by passing a reference to another context, other than that
+ not-a-context, as the last constructor argument. In this
+ way, it is possible to create a chain of contexts. Depending on the do_return argument in the constructor of
+ the last context in the chain, the application terminates or execution control
+ is transferred back to the last invocation of context::resume().
+ Each call of context::suspend(), for the chained context
+ instances, will return to context::resume().
+
+
+boost::contexts::contextctx2;
+
+voidfn1()
+{
+ std::cout<<"fn1(): when this function returns fn2() will be entered"<<std::endl;
+}
+
+voidfn2()
+{
+ std::cout<<"fn2(): first time entered"<<std::endl;
+ ctx2.suspend();
+ std::cout<<"fn2(): second time entered\n";
+ std::cout<<"fn2(): return to main()"<<std::endl;
+}
+
+intmain(intargc,char*argv[])
+{
+ {
+ // ctx2 will return to ctx2.resume()
+ctx2=boost::contexts::context(
+ fn2,
+ boost::contexts::default_stacksize(),
+ boost::contexts::no_stack_unwind,
+ boost::contexts::return_to_caller);
+
+ // ctx1 defines ctx2 as its successor
+boost::contexts::contextctx1(
+ fn1,
+ boost::contexts::default_stacksize(),
+ boost::contexts::no_stack_unwind,
+ ctx2);
+
+ // transfer execution control to ctx1
+// fn1() is entered
+ctx1.start();
+
+ // ctx2.suspend() was called
+}
+
+ // ctx2 was already started by ctx1
+ctx2.resume();
+
+ std::cout<<"Done"<<std::endl;
+
+ returnEXIT_SUCCESS;
+}
+
+output:
+ fn1():whenthisfunctionreturnsfn2()willbeentered
+ fn2():firsttimeentered
+ fn2():secondtimeentered
+ fnd2():returntomain()
+ Done
+
+
+
+ Call context::start() only on the first context in a
+ chain.
+
+
+
+
+ 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 next to last argument of the context constructor, do_unwind,
+ indicates whether the destructor should unwind the stack. Stack unwinding can
+ also be triggered by calling context::unwind_stack().
+
+
+ Stack unwinding assumes the following preconditions:
+
+
+
+
+ The context is not not-a-context
+
+
+
+
+ The context is not complete
+
+
+
+
+ The context is not running
+
+
+
+
+ The context owns a stack
+
+
+
+
+ After unwinding, a context
+ is complete.
+
+
+boost::contexts::contextctx;
+
+structX
+{
+ X()
+ {std::cout<<"X()"<<std::endl;}
+
+ ~X()
+ {std::cout<<"~X()"<<std::endl;}
+};
+
+voidfn()
+{
+ Xx;
+
+ for(inti=0;;++i)
+ {
+ std::cout<<"fn(): "<<i<<std::endl;
+ // transfer execution control back to main()
+ctx.suspend();
+ }
+}
+
+intmain(intargc,char*argv[])
+{
+ ctx=boost::contexts::context(
+ fn,
+ boost::contexts::default_stacksize(),
+ boost::contexts::no_stack_unwind,
+ boost::contexts::return_to_caller);
+ ctx.start();
+ for(inti=0;i<5;++i)
+ {
+ // transfer execution control to fn()
+ctx.resume();
+ }
+
+ std::cout<<"ctx is complete: "<<std::boolalpha<<ctx.is_complete()<<"\n";
+ std::cout<<"call ctx.unwind_stack()"<<std::endl;
+
+ // unwind the stack
+// X::~X() will be called
+ctx.unwind_stack();// unwind the stack
+
+ std::cout<<"ctx is complete: "<<std::boolalpha<<ctx.is_complete()<<"\n";
+ std::cout<<"Done"<<std::endl;
+
+ returnEXIT_SUCCESS;
+}
+
+output:
+ X()
+ fn():0
+ fn():1
+ fn():2
+ fn():3
+ fn():4
+ fn():5
+ ctxiscomplete:false
+ callctx.unwind_stack()
+ ~X()
+ ctxiscomplete:true
+ Done
+
+
+
+ You must not swallow forced_unwind exceptions!
+
+
+
+ Class context
+
+#include<boost/context/context.hpp>
+
+classcontext
+{
+public:
+ context();
+
+ template<typenameFn>
+ context(Fnfn,std::size_tsize,flag_unwind_tdo_unwind,flag_return_tdo_return);
+
+ template<typenameFn,typenameAllocator>
+ context(Fnfn,std::size_tsize,flag_unwind_tdo_unwind,flag_return_tdo_return,Allocatorconst&alloc);
+
+ template<typenameFn>
+ context(Fnfn,std::size_tsize,flag_unwind_tdo_unwind,context&nxt);
+
+ template<typenameFn,typenameAllocator>
+ context(Fnfn,std::size_tsize,flag_unwind_tdo_unwind,context&nxt,Allocatorconst&alloc);
+
+ template<typenameFn,typenameA0>
+ context(FnfnA0a0,std::size_tsize,flag_unwind_tdo_unwind,flag_return_tdo_return);
+ ...
+ template<typenameFn,typenameA0,...,typenameA9>
+ context(FnfnA0a0,...,A9,a9,std::size_tsize,flag_unwind_tdo_unwind,flag_return_tdo_return);
+
+ template<typenameFn,typenameAllocator,typenameA0>
+ context(FnfnA0a0,std::size_tsize,flag_unwind_tdo_unwind,flag_return_tdo_return,Allocatorconst&alloc);
+ ...
+ template<typenameFn,typenameAllocator,typenameA0,...,typenameA9>
+ context(FnfnA0a0,...,A9,a9,std::size_tsize,flag_unwind_tdo_unwind,flag_return_tdo_return,Allocatorconst&alloc);
+
+ template<typenameFn,typenameA0>
+ context(FnfnA0a0,std::size_tsize,flag_unwind_tdo_unwind,context&nxt);
+ ...
+ template<typenameFn,typenameA0,...,typenameA9>
+ context(FnfnA0a0,...,A9,a9,std::size_tsize,flag_unwind_tdo_unwind,context&nxt);
+
+ template<typenameFn,typenameAllocator,typenameA0>
+ context(FnfnA0a0,std::size_tsize,flag_unwind_tdo_unwind,context&nxt,Allocatorconst&alloc);
+ ...
+ template<typenameFn,typenameAllocator,typenameA0,...,typenameA9>
+ context(FnfnA0a0,...,A9,a9,std::size_tsize,flag_unwind_tdo_unwind,context&nxt,Allocatorconst&alloc);
+
+ template<typenameFn>
+ context(Fn&&fn,std::size_tsize,flag_unwind_tdo_unwind,flag_return_tdo_return);
+
+ template<typenameFn,typenameAllocator>
+ context(Fn&&fn,std::size_tsize,flag_unwind_tdo_unwind,flag_return_tdo_return,Allocatorconst&alloc);
+
+ template<typenameFn>
+ context(Fn&&fn,std::size_tsize,flag_unwind_tdo_unwind,context&nxt);
+
+ template<typenameFn,typenameAllocator>
+ context(Fn&&fn,std::size_tsize,flag_unwind_tdo_unwind,context&nxt,Allocatorconst&alloc);
+
+ ~context();
+
+ context(context&&other);
+
+ context&operator=(context&&other);
+
+ operatorunspecified-bool-type()const;
+
+ booloperator!()const;
+
+ voidswap(context&other);
+
+ boolis_complete()const;
+
+ boolis_running()const;
+
+ intptr_tstart();
+
+ intptr_tresume(intptr_tvp=0);
+
+ intptr_tsuspend(intptr_tvp=0);
+
+ voidunwind_stack();
+};
+
+voidswap(context&l,context&r);
+
+
+
+ context()
+
+
+
+
+ Effects:
+
+
+ Creates a context representing a not-a-context.
+
+
+
+
+ Throws:
+
+
+ Nothing.
+
+
+
+
+
+
+ template<typenameFn>context(Fnfn,std::size_tsize,flag_unwind_t
+ do_unwind,
+ flag_return_tdo_return)
+
+
+
+
+ Preconditions:
+
+
+ size > minimum_stacksize(),
+ size < maximum_stacksize()
+ when ! is_stack_unbound().
+
+
+
+
+ Effects:
+
+
+ Creates a context which will execute fn.
+ If do_unwind is stack_unwind the destructor of *this
+ unwinds the stack before destructing it. If do_return
+ is exit_application
+ the application exits with return code 0,
+ otherwise execution control is transfered back to the invoker (resume()
+ returns).
+
+
+
+
+ Throws:
+
+
+ std::invalid_argument if a precondition is not
+ satisfied.
+
+
+
+
+
+
+ template<typenameFn,typenameAllocator>
+ context(
+ Fnfn,std::size_tsize,flag_unwind_t
+ do_unwind,
+ flag_return_tdo_return,Allocatorconst&alloc)
+
+
+
+
+ Effects:
+
+
+ Creates a context which will execute fn.
+ If do_unwind is stack_unwind the destructor of *this
+ unwinds the stack before destructing it. If do_return
+ is exit_application
+ the application exits with return code 0,
+ otherwise execution control is transfered back to the invoker (resume()
+ returns).
+
+
+
+
+ Throws:
+
+
+ depends on alloc if
+ a precondition is not satisfied.
+
+
+
+
+
+
+ template<typenameFn>context(Fnfn,std::size_tsize,flag_unwind_t
+ do_unwind,
+ context&
+ nxt)
+
+
+
+
+ Preconditions:
+
+
+ size > minimum_stacksize(),
+ size < maximum_stacksize()
+ when ! is_stack_unbound(), nxt
+ is not a not-a-context.
+
+
+
+
+ Effects:
+
+
+ Creates a context which will execute fn.
+ If do_unwind is stack_unwind the destructor of *this
+ unwinds the stack before destructing it. execution control is transfered
+ to nxt if fn returns (nxt
+ is linked).
+
+
+
+
+ Throws:
+
+
+ std::invalid_argument if a precondition is not
+ satisfied.
+
+
+
+
+
+
+ template<typenameFn,typenameAllocator>
+ context(
+ Fnfn,std::size_tsize,flag_unwind_t
+ do_unwind,
+ context&
+ nxt,
+ Allocatorconst&
+ alloc)
+
+
+
+
+ Preconditions:
+
+
+ nxt is not a not-a-context.
+
+
+
+
+ Effects:
+
+
+ Creates a context which will execute fn.
+ If do_unwind is stack_unwind the destructor of *this
+ unwinds the stack before destructing it. execution control is transfered
+ to nxt if fn returns (nxt
+ is linked).
+
+
+
+
+ Throws:
+
+
+ depends on alloc if
+ a precondition is not satisfied.
+
+
+
+
+
+
+ template<typenameFn,typenameA0,...>context(Fnfn,A0a0,...,std::size_tsize,flag_unwind_tdo_unwind,flag_return_t
+ do_return)
+
+
+
+
+ Preconditions:
+
+
+ size > minimum_stacksize(),
+ size < maximum_stacksize()
+ when ! is_stack_unbound().
+
+
+
+
+ Effects:
+
+
+ Creates a context which will execute fn
+ (arguments A0,... are
+ bound to fn). If do_unwind is stack_unwind
+ the destructor of *this
+ unwinds the stack before destructing it. If do_return
+ is exit_application
+ the application exits with return code 0,
+ otherwise execution control is transfered back to the invoker (resume()
+ returns).
+
+
+
+
+ Throws:
+
+
+ std::invalid_argument if a precondition is not
+ satisfied.
+
+
+
+
+
+
+ template<typenameFn,typenameAllocator,typenameA0,...>context(Fnfn,A0a0,...,std::size_tsize,flag_unwind_tdo_unwind,flag_return_t
+ do_return,
+ Allocatorconst&alloc)
+
+
+
+
+ Preconditions:
+
+
+ size > minimum_stacksize(),
+ size < maximum_stacksize()
+ when ! is_stack_unbound().
+
+
+
+
+ Effects:
+
+
+ Creates a context which will execute fn
+ (arguments A0,... are
+ bound to fn). If do_unwind is stack_unwind
+ the destructor of *this
+ unwinds the stack before destructing it. If do_return
+ is exit_application
+ the application exits with return code 0,
+ otherwise execution control is transfered back to the invoker (resume()
+ returns).
+
+
+
+
+ Throws:
+
+
+ depends on alloc if
+ a precondition is not satisfied.
+
+
+
+
+
+
+ template<typenameFn,typenameA0,...>context(Fnfn,A0a0,...,std::size_tsize,flag_unwind_tdo_unwind,context&nxt)
+
+
+
+
+ Preconditions:
+
+
+ size > minimum_stacksize(),
+ size < maximum_stacksize()
+ when ! is_stack_unbound(), nxt
+ is not a not-a-context.
+
+
+
+
+ Effects:
+
+
+ Creates a context which will execute fn
+ (argumentsA0,... are
+ bound to fn). If do_unwind is stack_unwind
+ the destructor of *this
+ unwinds the stack before destructing it. execution control is transfered
+ to nxt if fn returns (nxt
+ is linked).
+
+
+
+
+ Throws:
+
+
+ std::invalid_argument if a precondition is not
+ satisfied.
+
+
+
+
+
+
+ template<typenameFn,typenameAllocator,typenameA0,...>context(Fnfn,A0a0,...,std::size_tsize,flag_unwind_tdo_unwind,context&nxt,Allocatorconst&alloc)
+
+
+
+
+ Preconditions:
+
+
+ nxt is not a not-a-context.
+
+
+
+
+ Effects:
+
+
+ Creates a context which will execute fn
+ (argumentsA0,... are
+ bound to fn). If do_unwind is stack_unwind
+ the destructor of *this
+ unwinds the stack before destructing it. execution control is transfered
+ to nxt if fn returns (nxt
+ is linked).
+
+
+
+
+ Throws:
+
+
+ depends on alloc if
+ a precondition is not satisfied.
+
+
+
+
+
+
+ ~context()
+
+
+
+
+ Effects:
+
+
+ Destroys the instance and deallocates the stack. Unwinds the stack
+ if it was specified in the constructor of *this.
+
+
+
+
+ Throws:
+
+
+ Nothing.
+
+
+
+
+
+
+ context(context&&
+ other)
+
+
+
+
+ Effects:
+
+
+ Moves the internal data of other
+ to *this.
+ other becomes not-a-context.
+
+
+
+
+ Throws:
+
+
+ Nothing.
+
+
+
+
+
+
+ context&
+ operator=(
+ context&&
+ other)
+
+
+
+
+ Effects:
+
+
+ Destroys the internal data of *this and moves the internal data of
+ other to *this.
+ other becomes not-a-context.
+
+
+
+
+ Throws:
+
+
+ Nothing.
+
+
+
+
+
+
+ operatorunspecified-bool-type()const
+
+
+
+
+ Returns:
+
+
+ If *this
+ refers to not-a-context, the function returns
+ false. Otherwise true.
+
+
+
+
+ Throws:
+
+
+ Nothing.
+
+
+
+
+
+
+ booloperator!()const
+
+
+
+
+ Returns:
+
+
+ If *this
+ refers not to not-a-context, the function returns
+ true. Otherwise false.
+
+
+
+
+ Throws:
+
+
+ Nothing.
+
+
+
+
+
+
+ voidswap(context&other)
+
+
+
+
+ Effects:
+
+
+ Swaps the internal data from *this with the values of other.
+
+
+
+
+ Throws:
+
+
+ Nothing.
+
+
+
+
+
+
+ boolis_complete()const
+
+
+
+
+ Preconditions:
+
+
+ *this
+ is not a not-a-context.
+
+
+
+
+ Effects:
+
+
+ Returns true if context-function
+ of *this
+ has returned.
+
+
+
+
+ Throws:
+
+
+ Nothing.
+
+
+
+
+
+
+ boolis_running()const
+
+
+
+
+ Preconditions:
+
+
+ *this
+ is not a not-a-context.
+
+
+
+
+ Effects:
+
+
+ Returns true if *this
+ is the currently active context.
+
+
+
+
+ Throws:
+
+
+ Nothing.
+
+
+
+
+
+
+ intptr_tstart()
+
+
+
+
+ Preconditions:
+
+
+ *this
+ is not a not-a-context, !
+ is_complete()
+ and !is_running().
+
+
+
+
+ Effects:
+
+
+ start()
+ enters the context-functionfn
+ with which the context was constructed. Upon return, the context was
+ either left via suspend() or the context-function
+ returned.
+
+
+
+
+ Returns:
+
+
+ A intptr_t passed to suspend() is returned if the context was left
+ with suspend().
+
+
+
+
+ Throws:
+
+
+ Nothing.
+
+
+
+
+
+
+ intptr_tresume(intptr_tvp=0)
+
+
+
+
+ Preconditions:
+
+
+ *this
+ is not a not-a-context, !
+ is_complete()
+ and !is_running(), start() was called before.
+
+
+
+
+ Effects:
+
+
+ None, if *this was not suspended by a previous call to suspend(). Otherwise,
+ execution control is transferred back to the caller of suspend(), thereby
+ resuming *this. The argument, vp, will be returned by the previous
+ call to suspend().
+
+
+
+
+ Throws:
+
+
+ Nothing.
+
+
+
+
+
+
+ intptr_tsuspend(intptr_tvp=0)
+
+
+
+
+ Preconditions:
+
+
+ *this
+ is not a not-a-context, !
+ is_complete()
+ and is_running().
+
+
+
+
+ Effects:
+
+
+ Transfers execution control to the caller of the most recent call to
+ start() or resume(). The argument, vp, will be returned by start()
+ or resume().
+
+
+
+
+ Throws:
+
+
+ forced_unwind if context::unwind_stack()
+ was called.
+
+
+
+
+
+
+ voidunwind_stack()
+
+
+
+
+ Preconditions:
+
+
+ *this
+ is not a not-a-context, !
+ is_complete()
+ and !is_running().
+
+
+
+
+ Effects:
+
+
+ Destroys all objects allocated on the stack, owned by *this, in the
+ reverse order of their allocation.
+
+
+
+
+ Throws:
+
+
+ Nothing.
+
+
+
+
+ Postconditions:
+
+
+ *this
+ is not a not-a-context and is_complete().
+
+
+
+
+
+
+ Non-member
+ function swap()
+
+
+voidswap(context&l,context&r);
+
+
+
+
+ Effects:
+
+
+ As if 'l.swap( r)'.
+
+
+
+
+
+
+
+ Stack allocation
+
+ A context
+ requires a stack which will be allocated/deallocated by a StackAllocator.
+ Boost.Context uses stack_allocator
+ by default but a customized stack
+ allocator can be passed to the context
+ constructor instead. If a context is constructed it invokes allocate()
+ function and by its destruction the stack gets released by deallocate().
+
+
+
+ StackAllocator
+ concept
+
+
+ A StackAllocator must satisfy the StackAllocator
+ concept requirements shown in the following table, in which a is an object of a StackAllocator
+ type, p is a void*, and
+ s is a std::size_t:
+
+
+
+
+
+
+
+ expression
+
+
+
+
+ return type
+
+
+
+
+ notes
+
+
+
+
+
+
+
+
+ a.allocate(
+ s)
+
+
+
+
+ void*
+
+
+
+
+ returns a pointer to s
+ bytes allocated from the stack
+
+
+
+
+
+
+ a.deallocate(
+ p,
+ s)
+
+
+
+
+ void
+
+
+
+
+ deallocates s bytes
+ of memory beginning at p,
+ a pointer previously returned by a.allocate()
+
+
+
+
+
+
+
+
+ The implementation of allocate() might include logic to protect against
+ exceeding the context's available stack size rather than leaving it as undefined
+ behaviour.
+
+
+
+
+ Calling deallocate()
+ with a pointer not returned by allocate() results in undefined behaviour.
+
+
+
+
+ The stack is not required to be aligned; alignment takes place inside boost_fcontext_make().
+
+
+
+ Class stack_allocator
+
+ Boost.Context provides a StackAllocator
+ stack_allocator which models
+ the StackAllocator concept concept. It appends a guard-page
+ 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.
+
+
+
+ Helper functions
+
+ Boost.Context provides easy access to the
+ stack related limits defined by the environment.
+
+
+std::size_tdefault_stacksize();
+
+std::size_tminimum_stacksize();
+
+std::size_tmaximum_stacksize();
+
+boolis_stack_unbound();
+
+std::size_tpagesize();
+
+std::size_tpage_count(std::size_tstacksize);
+
+
+
+ std::size_t
+ default_stacksize()
+
+
+
+
+ Returns:
+
+
+ Returns a default stack size, which may be platform specific. The present
+ implementation returns a value of 256 kB.
+
+
+
+
+
+
+ std::size_t
+ minimum_stacksize()
+
+
+
+
+ Returns:
+
+
+ Returns the minimum size in bytes of stack defined by the environment.
+
+
+
+
+ Throws:
+
+
+ Nothing.
+
+
+
+
+
+
+ std::size_t
+ maximum_stacksize()
+
+
+
+
+ Preconditions:
+
+
+ is_stack_unbound()
+ returns false.
+
+
+
+
+ Returns:
+
+
+ Returns the maximum size in bytes of stack defined by the environment.
+
+
+
+
+ Throws:
+
+
+ Nothing.
+
+
+
+
+
+
+ boolis_stack_unbound()
+
+
+
+
+ Returns:
+
+
+ Returns true if the environment
+ defines no limit for the size of a stack.
+
+
+
+
+ Throws:
+
+
+ Nothing.
+
+
+
+
+
+
+ std::size_t
+ pagesize()
+
+
+
+
+ Returns:
+
+
+ Returns how many bytes the operating system allocates for one page.
+
+
+
+
+ Throws:
+
+
+ Nothing.
+
+
+
+
+
+
+ std::size_t
+ page_count(
+ std::size_tstacksize)
+
+
+
+
+ Returns:
+
+
+ Returns how many pages have to be allocated for a stack of stacksize bytes.
+
+
+
+
+ Throws:
+
+
+ Nothing.
+
+
+
+
+
+
+
+ Low level API (boost_fcontext_t)
+
+ Boost.Context uses and provides a low level
+ API which implements the execution control transfer mechanism. The boost_fcontext_t
+ API provides a similar interface like ucontext_t.
+ boost_fcontext_t and its functions are located in the global namespace and
+ the functions are declared as extern "C".
+
+
+#include<cstdio>
+#include<cstdlib>
+#include<cstring>
+#include<vector>
+
+#include<boost/cstdint.hpp>
+#include<boost/context/fcontext.hpp>
+
+boost_fcontext_tfcm,fc1,fc2;
+
+voidf1(intptr_tp)
+{
+ (void)p;
+ fprintf(stderr,"f1() started\n");
+ fprintf(stderr,"f1: call boost_fcontext_jump( & fc1, & fc2, 0)\n");
+ // jump to context fc2
+boost_fcontext_jump(&fc1,&fc2,0);
+ // returned from context fc2 (function f2())
+// return from function f1()
+fprintf(stderr,"f1() returns\n");
+}
+
+voidf2(intptr_tp)
+{
+ (void)p;
+ fprintf(stderr,"f2() started\n");
+ fprintf(stderr,"f2: call boost_fcontext_jump( & fc2, & fc1, 0)\n");
+ // jump to context fc1
+boost_fcontext_jump(&fc2,&fc1,0);
+ // never reached
+fprintf(stderr,"f2() returns\n");
+}
+
+intmain(intargc,char*argv[])
+{
+ // creat two protected stacks
+boost::contexts::protected_stackb1(262144);
+ boost::contexts::protected_stackb2(262144);
+
+ // let context fc1 use stack b1
+fc1.fc_stack.ss_base=b1.address();
+ fc1.fc_stack.ss_limit=
+ static_cast<char*>(fc1.fc_stack.ss_base)-b1.size();
+ // link context fcm;
+// fcm will be invoked after fc1 terminated
+fc1.fc_link=&fcm;
+ // context fc1 used f1() as context function
+boost_fcontext_make(&fc1,f1,0);
+
+ // let context fc2 use stack b2
+fc2.fc_stack.ss_base=b2.address();
+ fc2.fc_stack.ss_limit=
+ static_cast<char*>(fc2.fc_stack.ss_base)-b2.size();
+ // context fc2 used f2() as context function
+boost_fcontext_make(&fc2,f2,0);
+
+ // start context f1; entering f1()
+fprintf(stderr,"main: call boost_fcontext_start( & fcm, & fc1)\n");
+ boost_fcontext_start(&fcm,&fc1);
+
+ fprintf(stderr,"main() returns\n");
+ returnEXIT_SUCCESS;
+}
+
+output:
+ main:callboost_fcontext_start(&fcm,&fc1)
+ f1()started
+ f1:callboost_fcontext_jump(&fc1,&fc2,0)
+ f2()started
+ f2:callboost_fcontext_jump(&fc2,&fc1,0)
+ f1()returns
+ main()returns
+
+
+ Function boost_fcontext_start() enters the context-function
+ f1()
+ by starting context fc1 (context fcm saves the registers of main()). For jumping between context's fc1 and fc2
+ boost_fcontext_jump()
+ is called. Because context fcm is chained to fc1, main() is entered (returning from boost_fcontext_start())
+ after context fc1 becomes complete (return from f1()).
+
+
+
+ The low level API is the part to port to new platforms.
+
+
+
+ Struct boost_fcontext_t
+ and related functions
+typedefstructboost_fcontext_stackboost_fcontext_stack_t;
+structboost_fcontext_stack
+{
+ void*ss_base;
+ void*ss_limit;
+};
+
+typedefstructboost_fcontextboost_fcontext_t;
+structboost_fcontext
+{
+ <platformspecific>
+
+ boost_fcontext_stack_tfc_stack;
+ boost_fcontext_t*fc_link;
+};
+
+intptr_tboost_fcontext_start(boost_fcontext_t*ofc,boost_fcontext_tconst*nfc);
+intptr_tboost_fcontext_jump(boost_fcontext_t*ofc,boost_fcontext_tconst*nfc,intptr_tvp);
+voidboost_fcontext_make(boost_fcontext_t*fc,void(*fn)(void*),intptr_tp);
+
+
+
+ ss_base
+
+
+
+
+ Member:
+
+
+ Pointer to the top of the stack.
+
+
+
+
+
+
+ ss_limit
+
+
+
+
+ Member:
+
+
+ Pointer to the bottom of the stack.
+
+
+
+
+
+
+ fc_stack
+
+
+
+
+ Member:
+
+
+ Tracks the memory for the context's stack.
+
+
+
+
+
+
+ fc_link
+
+
+
+
+ Member:
+
+
+ The address of the next context link in a chain, if any.
+
+
+
+
+
+
+ intptr_tboost_fcontext_start(boost_fcontext_t
+ *ofc,boost_fcontext_t
+ *nfc)
+
+
+
+
+ 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. This function
+ must be called when first entering *nfc's execution context.
+
+
+
+
+ Returns:
+
+
+ The result of calling boost_fcontext_jump().
+
+
+
+
+
+
+ intptr_tboost_fcontext_jump(boost_fcontext_t
+ *ofc,boost_fcontext_t
+ *nfc,intptr_tp)
+
+
+
+
+ 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 boost_fcontext_start()
+ or boost_fcontext_jump() in the same thread.
+
+
+
+
+ Returns:
+
+
+ The third pointer argument passed to the most recent call to boost_fcontext_jump(),
+ if any.
+
+
+
+
+
+
+ voidboost_fcontext_make(boost_fcontext_t
+ *fc,void(*fn)(intptr_t),intptr_tp)
+
+
+
+
+ Precondition:
+
+
+ A stack is applied to *fc before boost_fcontext_make() is called. If a successor context
+ should be executed after *fc finishes the address of the successor
+ context must be stored ir fc->fc_link.
+ The application exits otherwise.
+
+
+
+
+ Effects:
+
+
+ Modifies *fc
+ in order to execute fn
+ with argument p when
+ the context is activated next.
+
+
+
+
+
+
+
+ Examples
+
+ Enumerator
+
+ Class power computes the
+ power of number for the exponents
+ in the range from 1 to number (constructor arguments).
+
+
+classpower
+{
+public:
+ power(intnumber,intexponent);
+
+ boolget(int&data);
+};
+
+
+ Each call to power::get() returns
+ a boolean indicating a vaild result. The reference argument data contains the computed result.
+
+
+intmain()
+{
+ {
+ powerpw(2,8);
+ inti;
+ // pw.get() returns the computed result
+// for each value from 1 to 8
+// return value of pw.get() indicates that
+// a valid result
+while(pw.get(i)){
+ std::cout<<i<<" ";
+ }
+ }
+
+ std::cout<<"\nDone"<<std::endl;
+
+ returnEXIT_SUCCESS;
+}
+
+output:
+ 248163264128256
+ Done
+
+
+ In main()
+ an instance of power is constructed,
+ computing the power of 2 for exponents in the range from 1 to 8 by each call
+ to power::get().
+
+
+ It is intended to compute the numbers in a loop and transfer each result
+ to the caller without disrupting the loop.
+
+
+ Boost.Context can be used to implement an
+ equivalent to C#'s yield
+ facility (C# provides the keywords yield
+ return and yield
+ break). For this purpose helper-template
+ enumerator is defined containing
+ the private member ctx_ executing
+ enumerate()
+ as context-function.
+
+
+template<typenameT>
+classenumerator{
+private:
+ boost::contexts::contextctx_;
+ boolcomplete_;
+ booldo_unwind_;
+ boolstarted_;
+
+ staticvoidrun(enumerator*self)
+ {self->enumerate();}
+
+protected:
+ virtualvoidenumerate()=0;
+
+ voidyield_return(Tconst&v)
+ {
+ intptr_tvp=reinterpret_cast<intptr_t>(&v);
+ ctx_.suspend(vp);
+ }
+
+ voidyield_break()
+ {
+ complete_=true;
+ ctx_.suspend();
+ }
+
+public:
+ enumerator(booldo_unwind=true):
+ ctx_(
+ &enumerator::run,this,
+ boost::contexts::default_stacksize(),
+ boost::contexts::no_stack_unwind,
+ boost::contexts::return_to_caller),
+ complete_(false),
+ do_unwind_(do_unwind),
+ started_(false)
+ {}
+
+ ~enumerator()
+ {
+ if(do_unwind_&&!ctx_.is_complete())
+ ctx_.unwind_stack();
+ }
+
+ boolget(T&result)
+ {
+ intptr_tvp=0;
+ if(!started_)
+ {
+ started_=true;
+ vp=ctx_.start();
+ }
+ else
+ {
+ vp=ctx_.resume();
+ }
+ if(vp)result=*reinterpret_cast<T*>(vp);
+ return!(complete_||ctx_.is_complete());
+ }
+};
+
+
+ Member-function get()
+ starts / resumes contextctx_
+ bycalling
+ context::start()/
+ context::resume()so
+ thatenumerate()` is (re-)entered.
+ Functions context::start() / context::resume()
+ return the computed values.
+
+
+ yield_return()
+ (equivalent to C# yieldreturn) jumps to get() transfering the execution control and computed
+ value back. With yield_break() (equivalent to C# yield
+ break) the control is unconditionally
+ returned to get()
+ to signal the end of iteration.
+
+
+ Class power derives from
+ enumerate<
+ int>
+ and its implementation of enumerate() computes the power in a local loop. Each
+ computed result is transfered to get() by calling yield_return().
+
+
+classpower:publicenumerator<int>
+{
+private:
+ voidenumerate()
+ {
+ intcounter=0;
+ intresult=1;
+ // local scoped while loop
+while(counter++<exponent_)
+ {
+ // compute the value
+result=result*number_;
+
+ // return the value by jumping to calling context
+// == jump to while-loop in main()
+yield_return(result);
+ }
+ }
+
+ intnumber_;
+ intexponent_;
+
+public:
+ power(intnumber,intexponent):
+ number_(number),exponent_(exponent)
+ {}
+};
+
+
+
+ Continuation
+
+ Adapted from the
+ Java continuation documentation example.
+
+
+ Class continuation provides
+ the ability for a functor to suspend itself. The functor is required to accept
+ a const reference to its own continuation
+ instance.
+
+
+classcontinuation
+{
+private:
+ boost::contexts::contextctx_;
+ boost::function<void(continuation&)>fn_;
+ boolstarted_;
+
+ voidtrampoline_()
+ {fn_(*this);}
+
+public:
+ continuation(boost::function<void(continuation&)>const&fn):
+ ctx_(
+ &continuation::trampoline_,this,
+ boost::contexts::default_stacksize(),
+ boost::contexts::no_stack_unwind,
+ boost::contexts::return_to_caller),
+ false),
+ fn_(fn),started_(false)
+ {}
+
+ voidresume()
+ {
+ if(!started_)
+ {
+ started_=true;
+ ctx_.start();
+ }
+ elsectx_.resume();
+ }
+
+ voidsuspend()
+ {ctx_.suspend();}
+
+ boolis_complete()const
+ {returnctx_.is_complete();}
+};
+
+
+ The functions first()
+ and second()
+ are so-called coroutines running 'quasi parallel'. Please note that the variables
+ in the for-loops have local scope.
+
+
+voidfirst(continuation&c)
+{
+ std::cout<<"started first! ";
+ for(inti=0;i<10;++i)
+ {
+ c.suspend();// jump back to main()
+std::cout<<"a"<<i;// output
+}
+}
+
+voidsecond(continuation&c)
+{
+ std::cout<<"started second! ";
+ for(inti=0;i<10;++i)
+ {
+ c.suspend();// jump back to main()
+std::cout<<"b"<<i;// output
+}
+}
+
+intmain(intargc,char*argv[])
+{
+ {
+ continuationc1(boost::bind(first,_1));
+ continuationc2(boost::bind(second,_1));
+ // loop until coroutines c1 and c2 are complete
+while(!c1.is_complete()&&!c2.is_complete()){
+ c1.resume();// call coroutine c1 -> first()
+std::cout<<" ";
+ c2.resume();// call coroutine c2 -> second()
+std::cout<<" ";
+ }
+ }
+
+ std::cout<<"\nDone"<<std::endl;
+
+ returnEXIT_SUCCESS;
+}
+
+output:
+ startedfirst!startedsecond!a0b0a1b1a2b2a3b3a4b4a5b5a6b6a7b7a8b8a9b9
+ Done
+
+
+
+ ASIO
+
+ Adapted from Thinking
+ Asynchronously in C++ using boost.asio to implement a simple echo
+ server.
+
+
+ Chris Kohlhoff uses stackless coroutines based on preprocessor-based switch
+ statements to implement an echo server using boost.asio. Class continuation provides a simple coroutine
+ implemenatation using boost.context.
+
+
+classcontinuation
+{
+private:
+ boost::contexts::contextctx_;
+ boost::function<void(continuation&)>fn_;
+ boolstarted_;
+
+ voidtrampoline_()
+ {fn_(*this);}
+
+public:
+ continuation(boost::function<void(continuation&)>const&fn):
+ ctx_(
+ &continuation::trampoline_,this,
+ boost::contexts::default_stacksize(),
+ boost::contexts::no_stack_unwind,boost::contexts::return_to_caller),
+ fn_(fn),started_(false)
+ {}
+
+ voidresume()
+ {
+ if(!started_)
+ {
+ started_=true;
+ ctx_.start();
+ }
+ elsectx_.resume();
+ }
+
+ voidsuspend()
+ {ctx_.suspend();}
+
+ boolis_complete()const
+ {returnctx_.is_complete();}
+};
+
+
+ The operator server::operator()
+ is called by the asio reactor and jumps to coroutine (which is entering or
+ resuming in function server::do_()).
+ In function sever::do_() the
+ control flow is all in one place and easy to follow; *this is queued for
+ accepting a connection before control flow is passed back to the reactor
+ via suspend().
+ If server::do_() is
+ reentered, a local buffer is created and an asynch. read operation is queued
+ into the reactor. The control flow is then returned to the reactor for io-multiplexing
+ again. After some bytes were received server::do_()
+ is reentred again starting a asynch. write operation.
+
+
+ The advantage over the ordinary bind() version is that the state of execution
+ is preserved so that variables used by the read/write operations may have
+ local scope. Stackfull coroutines (as boost.context provides) enable application-protocol
+ stacks which can be more easily implemented. For instance, if only part of
+ a message is received (some bytes are outstanding before message can be parsed
+ or serviced), then the current state can be easily suspended/preserved (even
+ from nested functions of the protocol stack) until the remaining bytes are
+ received.
+
+
+classserver:publicboost::enable_shared_from_this<server>,
+ privatecontinuation
+{
+private:
+ boost::asio::ip::tcp::acceptoracceptor_;
+ boost::system::error_codeec_;
+ std::size_tn_;
+
+ voiddo_()
+ {
+ for(;;)
+ {
+ boost::asio::ip::tcp::socketsocket(acceptor_.get_io_service());
+ // accept connection request and enque *this for reading data from conenction
+acceptor_.async_accept(socket,boost::bind(&server::operator(),this->shared_from_this(),_1,0));
+ // suspend() jumps back to operator() which returns to asio reactor
+suspend();
+
+ while(!ec_)
+ {
+ // buffer has local scope!
+boost::array<char,1024>buffer;
+
+ // enqueue *this to read some bytes from the connection into buffer
+socket.async_read_some(
+ boost::asio::buffer(buffer),
+ boost::bind(&server::operator(),this->shared_from_this(),_1,_2));
+ // suspend() jumps back to operator(); asio reactor
+suspend();
+
+ // finish and return
+if(ec_)break;
+
+ // enqueue *this to write data from buffer to connection
+boost::asio::async_write(
+ socket,
+ boost::asio::buffer(buffer,n_),
+ boost::bind(&server::operator(),this->shared_from_this(),_1,_2));
+ // suspend() jumps back to operator(); asio reactor
+suspend();
+ }
+ }
+ }
+
+ server(boost::asio::io_service&io_service,shortport):
+ continuation(boost::bind(&server::do_,this)),
+ acceptor_(io_service,boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(),port)),
+ ec_(),n_(0)
+ {}
+
+public:
+ typedefboost::shared_ptr<server>ptr_t;
+
+ staticptr_tcreate(boost::asio::io_service&io_service,shortport)
+ {returnptr_t(newserver(io_service,port));}
+
+ voidoperator()(boost::system::error_codeec,size_tn)
+ {
+ ec_=ec;
+ n_=n;
+ // jump to coroutine (do_())
+resume();
+ }
+};
+
+intmain(intargc,char*argv[])
+{
+ try
+ {
+ if(argc!=2)
+ {
+ std::cerr<<"Usage: echo_server <port>\n";
+ return1;
+ }
+ {
+ boost::asio::io_serviceio_service;
+ io_service.post(
+ boost::bind(
+ &server::operator(),
+ server::create(io_service,boost::lexical_cast<short>(argv[1])),
+ boost::system::error_code(),0));
+ io_service.run();
+ }
+ std::cout<<"Done"<<std::endl;
+
+ returnEXIT_SUCCESS;
+ }
+ catch(std::exceptionconst&e)
+ {std::cerr<<"Exception: "<<e.what()<<std::endl;}
+
+ returnEXIT_FAILURE;
+}
+
+
+
+
+ Performance
+
+ Performance of Boost.Context was measured
+ on the platforms shown in the following table. Performance measurements were
+ taken using rdtsc, with overhead
+ corrections, on x86 platforms. In each case, stack protection was active, cache
+ warm-up was accounted for, and the one running thread was pinned to a single
+ CPU. The code was compiled using the build options, 'variantrelease
+ cxxflags-DBOOST_DISABLE_ASSERTS'.
+
+
+ The numbers in the table are the number of cycles per iteration, based upon
+ an average computed over 1000 iterations.
+
+