mirror of
https://github.com/boostorg/context.git
synced 2026-01-22 17:12:17 +00:00
317 lines
11 KiB
Plaintext
317 lines
11 KiB
Plaintext
[/
|
|
Copyright Oliver Kowalke 2014.
|
|
Distributed under the Boost Software License, Version 1.0.
|
|
(See accompanying file LICENSE_1_0.txt or copy at
|
|
http://www.boost.org/LICENSE_1_0.txt
|
|
]
|
|
|
|
[section:econtext Class execution_context]
|
|
|
|
[important __econtext__ requires C++14.]
|
|
|
|
Class __econtext__ encapsulates __fcontext__ and related functions (
|
|
__jump_fcontext__ and __make_fcontext__) as well as stack management.
|
|
__econtext__ permits access to the current, active context via
|
|
`execution_context::current()`.
|
|
|
|
/*
|
|
* grammar:
|
|
* P ---> E '\0'
|
|
* E ---> T {('+'|'-') T}
|
|
* T ---> S {('*'|'/') S}
|
|
* S ---> digit | '(' E ')'
|
|
*/
|
|
class Parser{
|
|
// implementation omitted; see examples directory
|
|
};
|
|
|
|
int main() {
|
|
std::istringstream is("1+1");
|
|
char c;
|
|
bool done=false;
|
|
std::exception_ptr except;
|
|
|
|
// create handle to main execution context
|
|
auto main_ctx( boost::context::execution_context::current() );
|
|
|
|
// execute parser in new execution context
|
|
boost::context::execution_context parser_ctx(
|
|
std::allocator_arg,
|
|
boost::context::fixedsize_stack(),
|
|
[&main_ctx,&is,&c,&done,&except](){
|
|
// create parser with callback function
|
|
Parser p( is,
|
|
[&main_ctx,&c](char ch){
|
|
c=ch;
|
|
// resume main execution context
|
|
main_ctx();
|
|
});
|
|
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
|
|
parser_ctx();
|
|
if ( except) {
|
|
std::rethrow_exception( except);
|
|
}
|
|
while( ! done) {
|
|
printf("Parsed: %c\n",c);
|
|
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 __econtext__ the control flow can be inverted, e.g. the user-code
|
|
pulls parsed symbols from the parser - instead to get pushed from the parser
|
|
(via callback).
|
|
|
|
The interface of __econtext__ does not transfer data. This is not required
|
|
because usually sharing data's address (pointer/reference of lvalues) is sufficient.
|
|
|
|
If the code executed by __econtext__ emits an exception, the application is terminated.
|
|
['std::exception_ptr] can be used to transfer exceptions between different execution
|
|
contexts.
|
|
|
|
Sometimes it is necessary to unwind the stack of an unfinished context to
|
|
destroy local stack variables so they can release allocated resources (RAII
|
|
pattern). The user is responsible for this task.
|
|
|
|
[heading allocating control structures on top of stack]
|
|
Allocating control structures on top of the stack requires to allocated the
|
|
__stack_context__ and create the control structure with placement new before
|
|
__econtext__ is created.
|
|
[note The user is responsible for destructing the control structure at the top
|
|
of the stack.]
|
|
|
|
// stack-allocator used for (de-)allocating stack
|
|
fixedsize_stack salloc( 4048);
|
|
// allocate stack space
|
|
stack_context sctx( salloc.allocate() );
|
|
// reserve space for control structure on top of the stack
|
|
void * sp = static_cast< char * >( sctx.sp) - sizeof( my_control_structure);
|
|
std::size_t size = sctx.size - sizeof( my_control_structure);
|
|
// placement new creates control structure on reserved space
|
|
my_control_structure * cs = new ( sp) my_control_structure( sp, size, sctx, salloc);
|
|
...
|
|
// destructing the control structure
|
|
cs->~my_control_structure();
|
|
...
|
|
struct my_control_structure {
|
|
// execution context
|
|
execution_context ectx;
|
|
|
|
template< typename StackAllocator >
|
|
my_control_structure( void * sp, std::size_t size, stack_context sctx, StackAllocator salloc) :
|
|
// create execution context
|
|
ectx( std::allocator_arg, preallocated( sp, size, sctx), salloc, entry_func) {
|
|
}
|
|
...
|
|
};
|
|
|
|
[heading exception handling]
|
|
If the function executed inside a __econtext__ emits ans exception, the
|
|
application is terminated by calling ['std::terminate(). ['std::exception_ptr]
|
|
can be used to transfer exceptions between different execution contexts.
|
|
|
|
[heading parameter passing]
|
|
Input and output parameters are transferred via a lambda capture list and
|
|
references/pointers.
|
|
|
|
class X {
|
|
private:
|
|
int * inp_;
|
|
std::string outp_;
|
|
std::exception_ptr excptr_;
|
|
boost::context::execution_context caller_;
|
|
boost::context::execution_context callee_;
|
|
|
|
public:
|
|
X() :
|
|
inp_( nullptr),
|
|
outp_(),
|
|
excptr_(),
|
|
caller_( boost::context::execution_context::current() ),
|
|
callee_( [=] () {
|
|
try {
|
|
int i = * inp_;
|
|
outp_ = boost::lexical_cast< std::string >( i);
|
|
caller_();
|
|
} catch (...) {
|
|
excptr_=std::current_exception();
|
|
}
|
|
})
|
|
{}
|
|
|
|
std::string operator()( int i) {
|
|
inp_ = & i;
|
|
callee_();
|
|
if(excptr_){
|
|
std::rethrow_exception(excptr_);
|
|
}
|
|
return outp_;
|
|
}
|
|
};
|
|
|
|
int main() {
|
|
X x;
|
|
std::cout << x( 7) << std::endl;
|
|
std::cout << "done" << std::endl;
|
|
}
|
|
|
|
|
|
[heading Class `execution_context`]
|
|
|
|
class execution_context {
|
|
public:
|
|
static execution_context current() noexcept;
|
|
|
|
template< typename 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);
|
|
|
|
execution_context( execution_context const& other) noexcept;
|
|
execution_context( execution_context && other) noexcept;
|
|
|
|
execution_context & operator=( execution_context const& other) noexcept;
|
|
execution_context & operator=( execution_context && other) noexcept;
|
|
|
|
explicit operator bool() const noexcept;
|
|
bool operator!() const noexcept;
|
|
|
|
void operator()() noexcept;
|
|
};
|
|
|
|
[heading `static execution_context current()`]
|
|
[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)`]
|
|
[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().]]
|
|
]
|
|
|
|
[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`.]]
|
|
]
|
|
|
|
[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.]]
|
|
]
|
|
|
|
[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)`]
|
|
[variablelist
|
|
[[Effects:] [Moves underlying capture record to `*this`.]]
|
|
[[Throws:] [Nothing.]]
|
|
]
|
|
|
|
[heading `execution_context & operator=( execution_context const& other)`]
|
|
[variablelist
|
|
[[Effects:] [Copies the state of `other` to `*this`, state (capture record) is shared.]]
|
|
[[Throws:] [Nothing.]]
|
|
]
|
|
|
|
[heading `execution_context & operator=( execution_context && other)`]
|
|
[variablelist
|
|
[[Effects:] [Moves the state of `other` to `*this` using move semantics.]]
|
|
[[Throws:] [Nothing.]]
|
|
]
|
|
|
|
[heading `explicit operator bool() const noexcept`]
|
|
[variablelist
|
|
[[Returns:] [`true` if `*this` points to a capture record.]]
|
|
[[Throws:] [Nothing.]]
|
|
]
|
|
|
|
[heading `bool operator!() const noexcept`]
|
|
[variablelist
|
|
[[Returns:] [`true` if `*this` does not point to a capture record.]]
|
|
[[Throws:] [Nothing.]]
|
|
]
|
|
|
|
[heading `void operator()() noexcept`]
|
|
[variablelist
|
|
[[Effects:] [Stores internally the current context data (stack pointer,
|
|
instruction pointer, and CPU registers) to the current active context and
|
|
restores the context data from `*this`, which implies jumping to `*this`'s
|
|
execution context.]]
|
|
[[Note:] [The behaviour is undefined if `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:] [Reference to `*this`.]]
|
|
[[Throws:] [Nothing.]]
|
|
]
|
|
|
|
|
|
[heading Struct `preallocated`]
|
|
|
|
struct preallocated {
|
|
void * sp;
|
|
std::size_t size;
|
|
stack_context sctx;
|
|
|
|
preallocated( void * sp, std:size_t size, stack_allocator sctx) noexcept;
|
|
};
|
|
|
|
[heading `preallocated( void * sp, std:size_t size, stack_allocator sctx)`]
|
|
[variablelist
|
|
[[Effects:] [Creates an object of preallocated.]]
|
|
]
|
|
|
|
[section:winfibers 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.
|
|
[note 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).]
|
|
|
|
[endsect]
|
|
|
|
|
|
[endsect]
|