initial import

This commit is contained in:
Oliver Kowalke 2015-01-23 23:15:23 +01:00
parent 9b56338411
commit 81b2255ddf
69 changed files with 6812 additions and 0 deletions

11
README.md Normal file
View File

@ -0,0 +1,11 @@
boost.coroutine
===============
boost.coroutine provides templates for generalized subroutines which allow multiple entry points for
suspending and resuming execution at certain locations. It preserves the local state of execution and
allows re-entering subroutines more than once (useful if state must be kept across function calls).
Coroutines can be viewed as a language-level construct providing a special kind of control flow.
In contrast to threads, which are pre-emptive, coroutines switches are cooperative (programmer controls
when a switch will happen). The kernel is not involved in the coroutine switches.

34
build/Jamfile.v2 Normal file
View File

@ -0,0 +1,34 @@
# 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)
import feature ;
import modules ;
import toolset ;
feature.feature valgrind : on : optional propagated composite ;
feature.compose <valgrind>on : <define>BOOST_USE_VALGRIND ;
project boost/coroutine2
: requirements
<library>/boost/context//boost_context
<toolset>gcc,<segmented-stacks>on:<cxxflags>-fsplit-stack
<toolset>gcc,<segmented-stacks>on:<cxxflags>-DBOOST_USE_SEGMENTED_STACKS
<toolset>clang,<segmented-stacks>on:<cxxflags>-fsplit-stack
<toolset>clang,<segmented-stacks>on:<cxxflags>-DBOOST_USE_SEGMENTED_STACKS
<link>shared:<define>BOOST_COROUTINES2_DYN_LINK=1
<define>BOOST_COROUTINES2_SOURCE
<threading>multi
: usage-requirements
<link>shared:<define>BOOST_COROUTINES2_DYN_LINK=1
: source-location ../src
;
lib boost_coroutine2
:
: <link>shared:<library>../../context/build//boost_context
;
boost-install boost_coroutine2 ;

33
doc/Jamfile.v2 Normal file
View File

@ -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)
project coroutine/doc ;
import boostbook ;
import quickbook ;
import modules ;
path-constant here : . ;
boostbook coro
:
coro.qbk
:
# Path for links to Boost:
<xsl:param>boost.root=../../../..
# HTML options first:
# How far down we chunk nested sections, basically all of them:
<xsl:param>chunk.section.depth=3
# Don't put the first section on the same page as the TOC:
<xsl:param>chunk.first.sections=1
# How far down sections get TOC's
<xsl:param>toc.section.depth=10
# Max depth in each TOC:
<xsl:param>toc.max.depth=3
# How far down we go with TOC's
<xsl:param>generate.section.toc.level=10
# Absolute path for images:
<format>pdf:<xsl:param>img.src.path=$(here)/html/
;

17
doc/acknowledgements.qbk Normal file
View File

@ -0,0 +1,17 @@
[/
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:acknowledgements Acknowledgments]
I'd like to thank Alex Hagen-Zanker, Christopher Kormanyos, Conrad Poelman,
Eugene Yakubovich, Giovanni Piero Deretta, Hartmut Kaiser, Jeffrey Lee Hellrung,
[*Nat Goodspeed], Robert Stewart, Vicente J. Botet Escriba and Yuriy Krasnoschek.
Especially Eugene Yakubovich, Giovanni Piero Deretta and Vicente J. Botet
Escriba contributed many good ideas during the review.
[endsect]

13
doc/architectures.qbk Normal file
View File

@ -0,0 +1,13 @@
[/
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:architectures Architectures]
__boost_coroutine__ depends on __boost_context__ which supports these
[@boost:/libs/context/doc/html/context/architectures.html architectures].
[endsect]

708
doc/asymmetric.qbk Normal file
View File

@ -0,0 +1,708 @@
[/
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:asymmetric Asymmetric coroutine]
Two asymmetric coroutine types - __push_coro__ and __pull_coro__ - provide a
unidirectional transfer of data.
[heading __pull_coro__]
__pull_coro__ transfers data from another execution context (== pulled-from).
The template parameter defines the transferred parameter type.
The constructor of __pull_coro__ takes a function (__coro_fn__) accepting a
reference to an __push_coro__ as argument. Instantiating an __pull_coro__ passes
the control of execution to __coro_fn__ and a complementary __push_coro__ is
synthesized by the library and passed as reference to __coro_fn__.
This kind of coroutine provides __pull_coro_op__. This method only switches
context; it transfers no data.
__pull_coro__ provides input iterators (__pull_coro_it__) and __begin__/__end__
are overloaded. The increment-operation switches the context and transfers data.
typedef boost::coroutines2::asymmetric_coroutine<int> coro_t;
coro_t::pull_type source(
[&](coro_t::push_type& sink){
int first=1,second=1;
sink(first);
sink(second);
for(int i=0;i<8;++i){
int third=first+second;
first=second;
second=third;
sink(third);
}
});
for(auto i:source)
std::cout << i << " ";
output:
1 1 2 3 5 8 13 21 34 55
In this example an __pull_coro__ is created in the main execution context taking
a lambda function (== __coro_fn__) which calculates Fibonacci numbers in a
simple ['for]-loop.
The __coro_fn__ is executed in a newly created execution context which is
managed by the instance of __pull_coro__.
An __push_coro__ is automatically generated by the library and passed as
reference to the lambda function. Each time the lambda function calls
__push_coro_op__ with another Fibonacci number, __push_coro__ transfers it back
to the main execution context. The local state of __coro_fn__ is preserved and
will be restored upon transferring execution control back to __coro_fn__
to calculate the next Fibonacci number.
Because __pull_coro__ provides input iterators and __begin__/__end__ are
overloaded, a ['range-based for]-loop can be used to iterate over the generated
Fibonacci numbers.
[heading __push_coro__]
__push_coro__ transfers data to the other execution context (== pushed-to).
The template parameter defines the transferred parameter type.
The constructor of __push_coro__ takes a function (__coro_fn__) accepting a
reference to an __pull_coro__ as argument. In contrast to __pull_coro__,
instantiating an __push_coro__ does not pass the control of execution to
__coro_fn__ - instead the first call of __push_coro_op__ synthesizes a
complementary __pull_coro__ and passes it as reference to __coro_fn__.
The __push_coro__ interface does not contain a ['get()]-function: you can not retrieve
values from another execution context with this kind of coroutine.
__push_coro__ provides output iterators (__push_coro_it__) and
__begin__/__end__ are overloaded. The increment-operation switches the context
and transfers data.
typedef boost::coroutines2::asymmetric_coroutine<std::string> coro_t;
struct FinalEOL{
~FinalEOL(){
std::cout << std::endl;
}
};
const int num=5, width=15;
coro_t::push_type writer(
[&](coro_t::pull_type& in){
// finish the last line when we leave by whatever means
FinalEOL eol;
// pull values from upstream, lay them out 'num' to a line
for (;;){
for(int i=0;i<num;++i){
// when we exhaust the input, stop
if(!in) return;
std::cout << std::setw(width) << in.get();
// now that we've handled this item, advance to next
in();
}
// after 'num' items, line break
std::cout << std::endl;
}
});
std::vector<std::string> words{
"peas", "porridge", "hot", "peas",
"porridge", "cold", "peas", "porridge",
"in", "the", "pot", "nine",
"days", "old" };
std::copy(begin(words),end(words),begin(writer));
output:
peas porridge hot peas porridge
cold peas porridge in the
pot nine days old
In this example an __push_coro__ is created in the main execution context
accepting a lambda function (== __coro_fn__) which requests strings and lays out
'num' of them on each line.
This demonstrates the inversion of control permitted by coroutines. Without
coroutines, a utility function to perform the same job would necessarily
accept each new value as a function parameter, returning after processing that
single value. That function would depend on a static state variable. A
__coro_fn__, however, can request each new value as if by calling a function
-- even though its caller also passes values as if by calling a function.
The __coro_fn__ is executed in a newly created execution context which is
managed by the instance of __push_coro__.
The main execution context passes the strings to the __coro_fn__ by calling
__push_coro_op__.
An __pull_coro__ instance is automatically generated by the library and passed as
reference to the lambda function. The __coro_fn__ accesses the strings passed
from the main execution context by calling __pull_coro_get__ and lays those
strings out on ['std::cout] according the parameters 'num' and 'width'.
The local state of __coro_fn__ is preserved and will be restored after
transferring execution control back to __coro_fn__.
Because __push_coro__ provides output iterators and __begin__/__end__ are
overloaded, the ['std::copy] algorithm can be used to iterate over the vector
containing the strings and pass them one by one to the coroutine.
[heading coroutine-function]
The __coro_fn__ returns ['void] and takes its counterpart-coroutine as
argument, so that using the coroutine passed as argument to __coro_fn__ is the
only way to transfer data and execution control back to the caller.
Both coroutine types take the same template argument.
For __pull_coro__ the __coro_fn__ is entered at __pull_coro__ construction.
For __push_coro__ the __coro_fn__ is not entered at __push_coro__ construction
but entered by the first invocation of __push_coro_op__.
After execution control is returned from __coro_fn__ the state of the
coroutine can be checked via __pull_coro_bool__ returning `true` if the
coroutine is still valid (__coro_fn__ has not terminated). Unless the first
template parameter is `void`, `true` also implies that a data value is
available.
[heading passing data from a pull-coroutine to main-context]
In order to transfer data from an __pull_coro__ to the main-context the framework
synthesizes an __push_coro__ associated with the __pull_coro__ instance in the
main-context. The synthesized __push_coro__ is passed as argument to __coro_fn__.
The __coro_fn__ must call this __push_coro_op__ in order to transfer each
data value back to the main-context.
In the main-context, the __pull_coro_bool__ determines whether the coroutine is
still valid and a data value is available or __coro_fn__ has terminated
(__pull_coro__ is invalid; no data value available). Access to the transferred
data value is given by __pull_coro_get__.
typedef boost::coroutines2::asymmetric_coroutine<int> coro_t;
coro_t::pull_type source( // constructor enters coroutine-function
[&](coro_t::push_type& sink){
sink(1); // push {1} back to main-context
sink(1); // push {1} back to main-context
sink(2); // push {2} back to main-context
sink(3); // push {3} back to main-context
sink(5); // push {5} back to main-context
sink(8); // push {8} back to main-context
});
while(source){ // test if pull-coroutine is valid
int ret=source.get(); // access data value
source(); // context-switch to coroutine-function
}
[heading passing data from main-context to a push-coroutine]
In order to transfer data to an __push_coro__ from the main-context the framework
synthesizes an __pull_coro__ associated with the __push_coro__ instance in the
main-context. The synthesized __pull_coro__ is passed as argument to __coro_fn__.
The main-context must call this __push_coro_op__ in order to transfer each data
value into the __coro_fn__.
Access to the transferred data value is given by __pull_coro_get__.
typedef boost::coroutines2::asymmetric_coroutine<int> coro_t;
coro_t::push_type sink( // constructor does NOT enter coroutine-function
[&](coro_t::pull_type& source){
for (int i:source) {
std::cout << i << " ";
}
});
std::vector<int> v{1,1,2,3,5,8,13,21,34,55};
for( int i:v){
sink(i); // push {i} to coroutine-function
}
[heading accessing parameters]
Parameters returned from or transferred to the __coro_fn__ can be accessed with
__pull_coro_get__.
Splitting-up the access of parameters from context switch function enables to
check if __pull_coro__ is valid after return from __pull_coro_op__, e.g.
__pull_coro__ has values and __coro_fn__ has not terminated.
typedef boost::coroutines2::asymmetric_coroutine<boost::tuple<int,int>> coro_t;
coro_t::push_type sink(
[&](coro_t::pull_type& source){
// access tuple {7,11}; x==7 y==1
int x,y;
boost::tie(x,y)=source.get();
});
sink(boost::make_tuple(7,11));
[heading exceptions]
An exception thrown inside an __pull_coro__'s __coro_fn__ before its first call
to __push_coro_op__ will be re-thrown by the __pull_coro__ constructor. After an
__pull_coro__'s __coro_fn__'s first call to __push_coro_op__, any subsequent
exception inside that __coro_fn__ will be re-thrown by __pull_coro_op__.
__pull_coro_get__ does not throw.
An exception thrown inside an __push_coro__'s __coro_fn__ will be re-thrown by
__push_coro_op__.
[important Code executed by __coro_fn__ must not prevent the propagation of the
__forced_unwind__ exception. Absorbing that exception will cause stack
unwinding to fail. Thus, any code that catches all exceptions must re-throw any
pending __forced_unwind__ exception.]
try {
// code that might throw
} catch(const boost::coroutines2::detail::forced_unwind&) {
throw;
} catch(...) {
// possibly not re-throw pending exception
}
[important Do not jump from inside a catch block and than re-throw the
exception in another execution context.]
[heading Stack unwinding]
Sometimes it is necessary to unwind the stack of an unfinished coroutine to
destroy local stack variables so they can release allocated resources (RAII
pattern). The `attributes` argument of the coroutine constructor
indicates whether the destructor should unwind the stack (stack is unwound by
default).
Stack unwinding assumes the following preconditions:
* The coroutine is not __not_a_coro__
* The coroutine is not complete
* The coroutine is not running
* The coroutine owns a stack
After unwinding, a __coro__ is complete.
struct X {
X(){
std::cout<<"X()"<<std::endl;
}
~X(){
std::cout<<"~X()"<<std::endl;
}
};
{
typedef boost::coroutines2::asymmetric_coroutine<void>::push_type coro_t;
coro_t::push_type sink(
[&](coro_t::pull_type& source){
X x;
for(int=0;;++i){
std::cout<<"fn(): "<<i<<std::endl;
// transfer execution control back to main()
source();
}
});
sink();
sink();
sink();
sink();
sink();
std::cout<<"sink is complete: "<<std::boolalpha<<!sink<<"\n";
}
output:
X()
fn(): 0
fn(): 1
fn(): 2
fn(): 3
fn(): 4
fn(): 5
sink is complete: false
~X()
[heading Range iterators]
__boost_coroutine__ provides output- and input-iterators using __boost_range__.
__pull_coro__ can be used via input-iterators using __begin__ and __end__.
typedef boost::coroutines2::asymmetric_coroutine< int > coro_t;
int number=2,exponent=8;
coro_t::pull_type source(
[&](coro_t::push_type & sink){
int counter=0,result=1;
while(counter++<exponent){
result=result*number;
sink(result);
}
});
for (auto i:source)
std::cout << i << " ";
output:
2 4 8 16 32 64 128 256
['asymmetric_coroutine<>::pull_type::iterator::operator++()] corresponds to
__pull_coro_op__; ['asymmetric_coroutine<>::pull_type::iterator::operator*()]
roughly corresponds to __pull_coro_get__. An iterator originally obtained from
__begin__ of an __pull_coro__ compares equal to an iterator obtained from
__end__ of that same __pull_coro__ instance when its __pull_coro_bool__ would
return `false`].
[note If `T` is a move-only type, then
['asymmetric_coroutine<T>::pull_type::iterator] may only be dereferenced once
before it is incremented again.]
Output-iterators can be created from __push_coro__.
typedef boost::coroutines2::asymmetric_coroutine<int> coro_t;
coro_t::push_type sink(
[&](coro_t::pull_type& source){
while(source){
std::cout << source.get() << " ";
source();
}
});
std::vector<int> v{1,1,2,3,5,8,13,21,34,55};
std::copy(begin(v),end(v),begin(sink));
['asymmetric_coroutine<>::push_type::iterator::operator*()] roughly
corresponds to __push_coro_op__. An iterator originally obtained from
__begin__ of an __push_coro__ compares equal to an iterator obtained from
__end__ of that same __push_coro__ instance when its __push_coro_bool__ would
return `false`.
[heading Exit a __coro_fn__]
__coro_fn__ is exited with a simple return statement jumping back to the calling
routine. The __pull_coro__, __push_coro__ becomes complete, e.g. __pull_coro_bool__,
__push_coro_bool__ will return `false`.
[important After returning from __coro_fn__ the __coro__ is complete (can not
resumed with __push_coro_op__, __pull_coro_op__).]
[section:pull_coro Class `asymmetric_coroutine<>::pull_type`]
#include <boost/coroutine2/asymmetric_coroutine.hpp>
template< typename R >
class asymmetric_coroutine<>::pull_type
{
public:
pull_type() noexcept;
template< typename Fn >
pull_type( Fn && fn, attributes const& attr = attributes() );
template< typename Fn, typename StackAllocator >
pull_type( Fn && fn, attributes const& attr, StackAllocator stack_alloc);
pull_type( pull_type const& other)=delete;
pull_type & operator=( pull_type const& other)=delete;
~pull_type();
pull_type( pull_type && other) noexcept;
pull_type & operator=( pull_type && other) noexcept;
operator unspecified-bool-type() const noexcept;
bool operator!() const noexcept;
void swap( pull_type & other) noexcept;
pull_type & operator()();
R get() const;
};
template< typename R >
void swap( pull_type< R > & l, pull_type< R > & r);
template< typename R >
range_iterator< pull_type< R > >::type begin( pull_type< R > &);
template< typename R >
range_iterator< pull_type< R > >::type end( pull_type< R > &);
[heading `pull_type()`]
[variablelist
[[Effects:] [Creates a coroutine representing __not_a_coro__.]]
[[Throws:] [Nothing.]]
]
[heading `template< typename Fn >
pull_type( Fn && fn, attributes const& attr)`]
[variablelist
[[Preconditions:] [`size` >= minimum_stacksize(), `size` <= maximum_stacksize()
when ! is_stack_unbounded().]]
[[Effects:] [Creates a coroutine which will execute `fn`, and enters it.
Argument `attr` determines stack clean-up and preserving floating-point
registers.]]
[[Throws:] [Exceptions thrown inside __coro_fn__.]]
]
[heading `template< typename Fn, typename StackAllocator >
pull_type( Fn && fn, attributes const& attr, StackAllocator const& stack_alloc)`]
[variablelist
[[Preconditions:] [`size` >= minimum_stacksize(), `size` <= maximum_stacksize()
when ! is_stack_unbounded().]]
[[Effects:] [Creates a coroutine which will execute `fn`. Argument `attr`
determines stack clean-up and preserving floating-point registers.
For allocating/deallocating the stack `stack_alloc` is used.]]
[[Throws:] [Exceptions thrown inside __coro_fn__.]]
]
[heading `~pull_type()`]
[variablelist
[[Effects:] [Destroys the context and deallocates the stack.]]
]
[heading `pull_type( pull_type && other)`]
[variablelist
[[Effects:] [Moves the internal data of `other` to `*this`.
`other` becomes __not_a_coro__.]]
[[Throws:] [Nothing.]]
]
[heading `pull_type & operator=( pull_type && other)`]
[variablelist
[[Effects:] [Destroys the internal data of `*this` and moves the
internal data of `other` to `*this`. `other` becomes __not_a_coro__.]]
[[Throws:] [Nothing.]]
]
[heading `operator unspecified-bool-type() const`]
[variablelist
[[Returns:] [If `*this` refers to __not_a_coro__ or the coroutine-function
has returned (completed), the function returns `false`. Otherwise `true`.]]
[[Throws:] [Nothing.]]
]
[heading `bool operator!() const`]
[variablelist
[[Returns:] [If `*this` refers to __not_a_coro__ or the coroutine-function
has returned (completed), the function returns `true`. Otherwise `false`.]]
[[Throws:] [Nothing.]]
]
[heading `pull_type<> & operator()()`]
[variablelist
[[Preconditions:] [`*this` is not a __not_a_coro__.]]
[[Effects:] [Execution control is transferred to __coro_fn__ (no parameter is
passed to the coroutine-function).]]
[[Throws:] [Exceptions thrown inside __coro_fn__.]]
]
[heading `R get()`]
R asymmetric_coroutine<R,StackAllocator>::pull_type::get();
R& asymmetric_coroutine<R&,StackAllocator>::pull_type::get();
void asymmetric_coroutine<void,StackAllocator>::pull_type::get()=delete;
[variablelist
[[Preconditions:] [`*this` is not a __not_a_coro__.]]
[[Returns:] [Returns data transferred from coroutine-function via
__push_coro_op__.]]
[[Throws:] [`invalid_result`]]
[[Note:] [If `R` is a move-only type, you may only call `get()` once before
the next __pull_coro_op__ call.]]
]
[heading `void swap( pull_type & other)`]
[variablelist
[[Effects:] [Swaps the internal data from `*this` with the values
of `other`.]]
[[Throws:] [Nothing.]]
]
[heading Non-member function `swap()`]
template< typename R >
void swap( pull_type< R > & l, pull_type< R > & r);
[variablelist
[[Effects:] [As if 'l.swap( r)'.]]
]
[heading Non-member function `begin( pull_type< R > &)`]
template< typename R >
range_iterator< pull_type< R > >::type begin( pull_type< R > &);
[variablelist
[[Returns:] [Returns a range-iterator (input-iterator).]]
]
[heading Non-member function `end( pull_type< R > &)`]
template< typename R >
range_iterator< pull_type< R > >::type end( pull_type< R > &);
[variablelist
[[Returns:] [Returns an end range-iterator (input-iterator).]]
[[Note:] [When first obtained from `begin( pull_type< R > &)`, or after some
number of increment operations, an iterator will compare equal to the iterator
returned by `end( pull_type< R > &)` when the corresponding __pull_coro_bool__
would return `false`.]]
]
[endsect]
[section:push_coro Class `asymmetric_coroutine<>::push_type`]
#include <boost/coroutine2/asymmetric_coroutine.hpp>
template< typename Arg >
class asymmetric_coroutine<>::push_type
{
public:
push_type() noexcept;
template< typename Fn >
push_type( Fn && fn, attributes const& attr = attributes() );
template< typename Fn, typename StackAllocator >
push_type( Fn && fn, attributes const& attr, StackAllocator stack_alloc);
push_type( push_type const& other)=delete;
push_type & operator=( push_type const& other)=delete;
~push_type();
push_type( push_type && other) noexcept;
push_type & operator=( push_type && other) noexcept;
operator unspecified-bool-type() const noexcept;
bool operator!() const noexcept;
void swap( push_type & other) noexcept;
push_type & operator()( Arg arg);
};
template< typename Arg >
void swap( push_type< Arg > & l, push_type< Arg > & r);
template< typename Arg >
range_iterator< push_type< Arg > >::type begin( push_type< Arg > &);
template< typename Arg >
range_iterator< push_type< Arg > >::type end( push_type< Arg > &);
[heading `push_type()`]
[variablelist
[[Effects:] [Creates a coroutine representing __not_a_coro__.]]
[[Throws:] [Nothing.]]
]
[heading `template< typename Fn >
push_type( Fn && fn, attributes const& attr)`]
[variablelist
[[Preconditions:] [`size` >= minimum_stacksize(), `size` <= maximum_stacksize()
when ! is_stack_unbounded().]]
[[Effects:] [Creates a coroutine which will execute `fn`. Argument `attr`
determines stack clean-up and preserving floating-point registers.]]
]
[heading `template< typename Fn, typename StackAllocator >
push_type( Fn && fn, attributes const& attr, StackAllocator const& stack_alloc)`]
[variablelist
[[Preconditions:] [`size` >= minimum_stacksize(), `size` <= maximum_stacksize()
when ! is_stack_unbounded().]]
[[Effects:] [Creates a coroutine which will execute `fn`. Argument `attr`
determines stack clean-up and preserving floating-point registers.
For allocating/deallocating the stack `stack_alloc` is used.]]
]
[heading `~push_type()`]
[variablelist
[[Effects:] [Destroys the context and deallocates the stack.]]
]
[heading `push_type( push_type && other)`]
[variablelist
[[Effects:] [Moves the internal data of `other` to `*this`.
`other` becomes __not_a_coro__.]]
[[Throws:] [Nothing.]]
]
[heading `push_type & operator=( push_type && other)`]
[variablelist
[[Effects:] [Destroys the internal data of `*this` and moves the
internal data of `other` to `*this`. `other` becomes __not_a_coro__.]]
[[Throws:] [Nothing.]]
]
[heading `operator unspecified-bool-type() const`]
[variablelist
[[Returns:] [If `*this` refers to __not_a_coro__ or the coroutine-function
has returned (completed), the function returns `false`. Otherwise `true`.]]
[[Throws:] [Nothing.]]
]
[heading `bool operator!() const`]
[variablelist
[[Returns:] [If `*this` refers to __not_a_coro__ or the coroutine-function
has returned (completed), the function returns `true`. Otherwise `false`.]]
[[Throws:] [Nothing.]]
]
[heading `push_type & operator()(Arg arg)`]
push_type& asymmetric_coroutine<Arg>::push_type::operator()(Arg);
push_type& asymmetric_coroutine<Arg&>::push_type::operator()(Arg&);
push_type& asymmetric_coroutine<void>::push_type::operator()();
[variablelist
[[Preconditions:] [operator unspecified-bool-type() returns `true` for `*this`.]]
[[Effects:] [Execution control is transferred to __coro_fn__ and the argument
`arg` is passed to the coroutine-function.]]
[[Throws:] [Exceptions thrown inside __coro_fn__.]]
]
[heading `void swap( push_type & other)`]
[variablelist
[[Effects:] [Swaps the internal data from `*this` with the values
of `other`.]]
[[Throws:] [Nothing.]]
]
[heading Non-member function `swap()`]
template< typename Arg >
void swap( push_type< Arg > & l, push_type< Arg > & r);
[variablelist
[[Effects:] [As if 'l.swap( r)'.]]
]
[heading Non-member function `begin( push_type< Arg > &)`]
template< typename Arg >
range_iterator< push_type< Arg > >::type begin( push_type< Arg > &);
[variablelist
[[Returns:] [Returns a range-iterator (output-iterator).]]
]
[heading Non-member function `end( push_type< Arg > &)`]
template< typename Arg >
range_iterator< push_type< Arg > >::type end( push_type< Arg > &);
[variablelist
[[Returns:] [Returns a end range-iterator (output-iterator).]]
[[Note:] [When first obtained from `begin( push_type< R > &)`, or after some
number of increment operations, an iterator will compare equal to the iterator
returned by `end( push_type< R > &)` when the corresponding __push_coro_bool__
would return `false`.]]
]
[endsect]
[endsect]

107
doc/attributes.qbk Normal file
View File

@ -0,0 +1,107 @@
[/
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:attributes Attributes]
Class `attributes` is used to specify parameters required to setup a
coroutine's context.
enum flag_unwind_t
{
stack_unwind,
no_stack_unwind
};
enum flag_fpu_t
{
fpu_preserved,
fpu_not_preserved
};
struct attributes
{
std::size_t size;
flag_unwind_t do_unwind;
flag_fpu_t preserve_fpu;
attributes() noexcept;
explicit attributes( std::size_t size_) noexcept;
explicit attributes( flag_unwind_t do_unwind_) noexcept;
explicit attributes( flag_fpu_t preserve_fpu_) noexcept;
explicit attributes( std::size_t size_, flag_unwind_t do_unwind_) noexcept;
explicit attributes( std::size_t size_, flag_fpu_t preserve_fpu_) noexcept;
explicit attributes( flag_unwind_t do_unwind_, flag_fpu_t preserve_fpu_) noexcept;
explicit attributes( std::size_t size_, flag_unwind_t do_unwind_, flag_fpu_t preserve_fpu_) noexcept;
};
[heading `attributes()`]
[variablelist
[[Effects:] [Default constructor using `boost::context::default_stacksize()`, does unwind
the stack after coroutine/generator is complete and preserves FPU registers.]]
[[Throws:] [Nothing.]]
]
[heading `attributes( std::size_t size)`]
[variablelist
[[Effects:] [Argument `size` defines stack size of the new coroutine.
Stack unwinding after termination and preserving FPU registers is set by
default.]]
[[Throws:] [Nothing.]]
]
[heading `attributes( flag_unwind_t do_unwind)`]
[variablelist
[[Effects:] [Argument `do_unwind` determines if stack will be unwound after
termination or not. The default stacksize is used for the new coroutine
and FPU registers are preserved.]]
[[Throws:] [Nothing.]]
]
[heading `attributes( flag_fpu_t preserve_fpu)`]
[variablelist
[[Effects:] [Argument `preserve_fpu` determines if FPU register have to be
preserved across context switches. The default stacksize is used for the
new coroutine and its stack will be unwound after termination.]]
[[Throws:] [Nothing.]]
]
[heading `attributes( std::size_t size, flag_unwind_t do_unwind)`]
[variablelist
[[Effects:] [Arguments `size` and `do_unwind` are given by the user.
FPU registers are preserved across each context switch.]]
[[Throws:] [Nothing.]]
]
[heading `attributes( std::size_t size, flag_fpu_t preserve_fpu)`]
[variablelist
[[Effects:] [Arguments `size` and `preserve_fpu` are given by the user.
The stack is automatically unwound after coroutine/generator terminates.]]
[[Throws:] [Nothing.]]
]
[heading `attributes( flag_unwind_t do_unwind, flag_fpu_t preserve_fpu)`]
[variablelist
[[Effects:] [Arguments `do_unwind` and `preserve_fpu` are given by the user.
The stack gets a default value of `boost::context::default_stacksize()`.]]
[[Throws:] [Nothing.]]
]
[heading `attributes( std::size_t size, flag_unwind_t do_unwind, flag_fpu_t preserve_fpu)`]
[variablelist
[[Effects:] [Arguments `size`, `do_unwind` and `preserve_fpu` are given by the
user.]]
[[Throws:] [Nothing.]]
]
[endsect]

85
doc/coro.qbk Normal file
View File

@ -0,0 +1,85 @@
[/
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
]
[library Coroutine2
[quickbook 1.5]
[authors [Kowalke, Oliver]]
[copyright 2014 Oliver Kowalke]
[purpose C++11 Library providing coroutine facility]
[id coroutine2]
[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_asio__ [*Boost.Asio]]
[def __boost_build__ [*Boost.Build]]
[def __boost_context__ [*Boost.Context]]
[def __boost_coroutine__ [*Boost.Coroutine2]]
[def __coro__ ['coroutine]]
[def __coro_fn__ ['coroutine-function]]
[def __coros__ ['coroutines]]
[def __ctx__ ['context]]
[def __not_a_coro__ ['not-a-coroutine]]
[def __segmented_stack__ ['segmented-stack]]
[def __signature__ ['Signature]]
[def __stack_allocator_concept__ ['stack-allocator concept]]
[def __stack_allocator__ ['stack-allocator]]
[def __stack_traits__ ['stack-traits]]
[def __stack__ ['stack]]
[def __tls__ ['thread-local-storage]]
[def __acoro__ ['asymmetric_coroutine<>]]
[def __attrs__ ['attributes]]
[def __begin__ ['std::begin()]]
[def __bind__ ['boost::bind()]]
[def __coro_allocator__ ['stack_allocator]]
[def __coro_ns__ ['boost::coroutines2]]
[def __econtext__ ['execution_context]]
[def __end__ ['std::end()]]
[def __fcontext__ ['boost::contexts::fcontext_t]]
[def __fetch__ ['inbuf::fetch()]]
[def __fixedsize__ ['fixedsize_stack]]
[def __forced_unwind__ ['detail::forced_unwind]]
[def __getline__ ['std::getline()]]
[def __handle_read__ ['session::handle_read()]]
[def __io_service__ ['boost::asio::io_sevice]]
[def __protected_allocator__ ['protected_fixedsize]]
[def __protected_fixedsize__ ['protected_fixedsize_stack]]
[def __pull_coro__ ['asymmetric_coroutine<>::pull_type]]
[def __pull_coro_bool__ ['asymmetric_coroutine<>::pull_type::operator bool]]
[def __pull_coro_get__ ['asymmetric_coroutine<>::pull_type::get()]]
[def __pull_coro_it__ ['asymmetric_coroutine<>::pull_type::iterator]]
[def __pull_coro_op__ ['asymmetric_coroutine<>::pull_type::operator()]]
[def __push_coro__ ['asymmetric_coroutine<>::push_type]]
[def __push_coro_bool__ ['asymmetric_coroutine<>::push_type::operator bool]]
[def __push_coro_it__ ['asymmetric_coroutine<>::push_type::iterator]]
[def __push_coro_op__ ['asymmetric_coroutine<>::push_type::operator()]]
[def __segmented_allocator__ ['segmented]]
[def __segmented__ ['segmented_stack]]
[def __server__ ['server]]
[def __session__ ['session]]
[def __stack_context__ ['stack_context]]
[def __standard_allocator__ ['fixedsize]]
[def __start__ ['session::start()]]
[def __terminate__ ['std::terminate()]]
[def __underflow__ ['stream_buf::underflow()]]
[def __yield_context__ ['boost::asio::yield_context]]
[include overview.qbk]
[include intro.qbk]
[include motivation.qbk]
[include coroutine.qbk]
[include stack.qbk]
[include performance.qbk]
[include architectures.qbk]
[include acknowledgements.qbk]

95
doc/coroutine.qbk Normal file
View File

@ -0,0 +1,95 @@
[/
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:coroutine Coroutine]
__boost_coroutine__ provides asymmetric coroutines.
Implementations that produce sequences of values typically use asymmetric
coroutines.
[footnote Moura, Ana Lucia De and Ierusalimschy, Roberto.
"Revisiting coroutines". ACM Trans. Program. Lang. Syst., Volume 31 Issue 2,
February 2014, Article No. 6]
[heading stackful]
Each instance of a coroutine has its own stack.
In contrast to stackless coroutines, stackful coroutines allow invoking the
suspend operation out of arbitrary sub-stackframes, enabling escape-and-reenter
recursive operations.
[heading move-only]
A coroutine is moveable-only.
If it were copyable, then its stack with all the objects allocated on it
would be copied too. That would force undefined behaviour if some of these
objects were RAII-classes (manage a resource via RAII pattern). When the first
of the coroutine copies terminates (unwinds its stack), the RAII class
destructors will release their managed resources. When the second copy
terminates, the same destructors will try to doubly-release the same resources,
leading to undefined behaviour.
[heading clean-up]
On coroutine destruction the associated stack will be unwound.
The constructor of coroutine allows you to pass a customized ['stack-allocator].
['stack-allocator] is free to deallocate the stack or cache it for future usage
(for coroutines created later).
[heading segmented stack]
__push_coro__ and __pull_coro__ support segmented stacks (growing on demand).
It is not always possible to accurately estimate the required stack size - in
most cases too much memory is allocated (waste of virtual address-space).
At construction a coroutine starts with a default (minimal) stack size. This
minimal stack size is the maximum of page size and the canonical size for signal
stack (macro SIGSTKSZ on POSIX).
At this time of writing only GCC (4.7)
[footnote [@http://gcc.gnu.org/wiki/SplitStacks Ian Lance Taylor, Split Stacks in GCC]]
is known to support segmented stacks. With version 1.54 __boost_coroutine__
provides support for segmented stacks.
The destructor releases the associated stack. The implementer is free to
deallocate the stack or to cache it for later usage.
[heading context switch]
A coroutine saves and restores registers according to the underlying ABI on
each context switch (using __boost_context__).
Some applications do not use floating-point registers and can disable preserving
FPU registers for performance reasons.
[note According to the calling convention the FPU registers are preserved by
default.]
On POSIX systems, the coroutine context switch does not preserve signal masks
for performance reasons.
A context switch is done via __push_coro_op__ and __pull_coro_op__.
[warning Calling __push_coro_op__ and __pull_coro_op__ from inside the [_same]
coroutine results in undefined behaviour.]
As an example, the code below will result in undefined behaviour:
boost::coroutines2::asymmetric_coroutine<void>::push_type coro(
[&](boost::coroutines2::asymmetric_coroutine<void>::pull_type& yield){
coro();
});
coro();
[include asymmetric.qbk]
[endsect]

BIN
doc/images/event_model.dia Normal file

Binary file not shown.

BIN
doc/images/event_model.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

BIN
doc/images/foo_bar.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

BIN
doc/images/foo_bar_seq.dia Normal file

Binary file not shown.

BIN
doc/images/foo_bar_seq.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

BIN
doc/images/same_fringe.dia Normal file

Binary file not shown.

BIN
doc/images/same_fringe.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

106
doc/intro.qbk Normal file
View File

@ -0,0 +1,106 @@
[/
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:intro Introduction]
[heading Definition]
In computer science routines are defined as a sequence of operations. The
execution of routines forms a parent-child relationship and the child terminates
always before the parent. Coroutines (the term was introduced by Melvin
Conway [footnote Conway, Melvin E.. "Design of a Separable Transition-Diagram Compiler".
Commun. ACM, Volume 6 Issue 7, July 1963, Article No. 7]),
are a generalization of routines (Donald Knuth [footnote Knuth, Donald Ervin (1997).
"Fundamental Algorithms. The Art of Computer Programming 1", (3rd ed.)].
The principal difference between coroutines and routines
is that a coroutine enables explicit suspend and resume of its progress via
additional operations by preserving execution state and thus provides an
[*enhanced control flow] (maintaining the execution context).
[heading How it works]
Functions foo() and bar() are supposed to alternate their execution (leave and
enter function body).
[$../../../../libs/coroutine2/doc/images/foo_bar.png [align center]]
If coroutines were called exactly like routines, the stack would grow with
every call and would never be popped. A jump into the middle of a coroutine
would not be possible, because the return address would be on top of
stack entries.
The solution is that each coroutine has its own stack and control-block
(__fcontext__ from __boost_context__).
Before the coroutine gets suspended, the non-volatile registers (including stack
and instruction/program pointer) of the currently active coroutine are stored in
the coroutine's control-block.
The registers of the newly activated coroutine must be restored from its
associated control-block before it is resumed.
The context switch requires no system privileges and provides cooperative
multitasking convenient to C++. Coroutines provide quasi parallelism.
When a program is supposed to do several things at the same time, coroutines
help to do this much more simply and elegantly than with only a single flow of
control.
The advantages can be seen particularly clearly with the use of a recursive
function, such as traversal of binary trees (see example 'same fringe').
[heading characteristics]
Characteristics [footnote Moura, Ana Lucia De and Ierusalimschy, Roberto.
"Revisiting coroutines". ACM Trans. Program. Lang. Syst., Volume 31 Issue 2,
February 2014, Article No. 6] of a coroutine are:
* values of local data persist between successive calls (context switches)
* execution is suspended as control leaves coroutine and is resumed at certain time later
* symmetric or asymmetric control-transfer mechanism; see below
* first-class object (can be passed as argument, returned by procedures,
stored in a data structure to be used later or freely manipulated by
the developer)
* stackful or stackless
Coroutines are useful in simulation, artificial intelligence, concurrent
programming, text processing and data manipulation, supporting
the implementation of components such as cooperative tasks (fibers), iterators,
generators, infinite lists, pipes etc.
[heading execution-transfer mechanism]
Two categories of coroutines exist: symmetric and asymmetric coroutines.
An asymmetric coroutine knows its invoker, using a special operation to
implicitly yield control specifically to its invoker. By contrast, all symmetric
coroutines are equivalent; one symmetric coroutine may pass control to any
other symmetric coroutine. Because of this, a symmetric coroutine ['must]
specify the coroutine to which it intends to yield control.
[$../../../../libs/coroutine2/doc/images/foo_bar_seq.png [align center]]
Both concepts are equivalent and a fully-general coroutine library can provide
either symmetric or asymmetric coroutines.
[heading stackfulness]
In contrast to a stackless coroutine a stackful coroutine can be suspended
from within a nested stackframe. Execution resumes at exactly the same point
in the code where it was suspended before.
With a stackless coroutine, only the top-level routine may be suspended. Any
routine called by that top-level routine may not itself suspend. This prohibits
providing suspend/resume operations in routines within a general-purpose library.
[heading first-class continuation]
A first-class continuation can be passed as an argument, returned by a
function and stored in a data structure to be used later.
In some implementations (for instance C# ['yield]) the continuation can
not be directly accessed or directly manipulated.
Without stackfulness and first-class semantics, some useful execution control
flows cannot be supported (for instance cooperative multitasking or
checkpointing).
[endsect]

567
doc/motivation.qbk Normal file
View File

@ -0,0 +1,567 @@
[/
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:motivation Motivation]
In order to support a broad range of execution control behaviour the coroutine
types of __acoro__ can be used to ['escape-and-reenter] loops, to
['escape-and-reenter] recursive computations and for ['cooperative] multitasking
helping to solve problems in a much simpler and more elegant way than with only
a single flow of control.
[heading event-driven model]
The event-driven model is a programming paradigm where the flow of a program is
determined by events. The events are generated by multiple independent sources
and an event-dispatcher, waiting on all external sources, triggers callback
functions (event-handlers) whenever one of those events is detected (event-loop).
The application is divided into event selection (detection) and event handling.
[$../../../../libs/coroutine2/doc/images/event_model.png [align center]]
The resulting applications are highly scalable, flexible, have high
responsiveness and the components are loosely coupled. This makes the event-driven
model suitable for user interface applications, rule-based productions systems
or applications dealing with asynchronous I/O (for instance network servers).
[heading event-based asynchronous paradigm]
A classic synchronous console program issues an I/O request (e.g. for user
input or filesystem data) and blocks until the request is complete.
In contrast, an asynchronous I/O function initiates the physical operation but
immediately returns to its caller, even though the operation is not yet
complete. A program written to leverage this functionality does not block: it
can proceed with other work (including other I/O requests in parallel) while
the original operation is still pending. When the operation completes, the
program is notified. Because asynchronous applications spend less overall time
waiting for operations, they can outperform synchronous programs.
Events are one of the paradigms for asynchronous execution, but
not all asynchronous systems use events.
Although asynchronous programming can be done using threads, they come with
their own costs:
* hard to program (traps for the unwary)
* memory requirements are high
* large overhead with creation and maintenance of thread state
* expensive context switching between threads
The event-based asynchronous model avoids those issues:
* simpler because of the single stream of instructions
* much less expensive context switches
The downside of this paradigm consists in a sub-optimal program
structure. An event-driven program is required to split its code into
multiple small callback functions, i.e. the code is organized in a sequence of
small steps that execute intermittently. An algorithm that would usually be expressed
as a hierarchy of functions and loops must be transformed into callbacks. The
complete state has to be stored into a data structure while the control flow
returns to the event-loop.
As a consequence, event-driven applications are often tedious and confusing to
write. Each callback introduces a new scope, error callback etc. The
sequential nature of the algorithm is split into multiple callstacks,
making the application hard to debug. Exception handlers are restricted to
local handlers: it is impossible to wrap a sequence of events into a single
try-catch block.
The use of local variables, while/for loops, recursions etc. together with the
event-loop is not possible. The code becomes less expressive.
In the past, code using asio's ['asynchronous operations] was convoluted by
callback functions.
class session
{
public:
session(boost::asio::io_service& io_service) :
socket_(io_service) // construct a TCP-socket from io_service
{}
tcp::socket& socket(){
return socket_;
}
void start(){
// initiate asynchronous read; handle_read() is callback-function
socket_.async_read_some(boost::asio::buffer(data_,max_length),
boost::bind(&session::handle_read,this,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
}
private:
void handle_read(const boost::system::error_code& error,
size_t bytes_transferred){
if (!error)
// initiate asynchronous write; handle_write() is callback-function
boost::asio::async_write(socket_,
boost::asio::buffer(data_,bytes_transferred),
boost::bind(&session::handle_write,this,
boost::asio::placeholders::error));
else
delete this;
}
void handle_write(const boost::system::error_code& error){
if (!error)
// initiate asynchronous read; handle_read() is callback-function
socket_.async_read_some(boost::asio::buffer(data_,max_length),
boost::bind(&session::handle_read,this,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
else
delete this;
}
boost::asio::ip::tcp::socket socket_;
enum { max_length=1024 };
char data_[max_length];
};
In this example, a simple echo server, the logic is split into three member
functions - local state (such as data buffer) is moved to member variables.
__boost_asio__ provides with its new ['asynchronous result] feature a new
framework combining event-driven model and coroutines, hiding the complexity
of event-driven programming and permitting the style of classic sequential code.
The application is not required to pass callback functions to asynchronous
operations and local state is kept as local variables. Therefore the code
is much easier to read and understand.
[footnote Christopher Kohlhoff,
[@ http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n3964.pdf
N3964 - Library Foundations for Asynchronous Operations, Revision 1]].
void session(boost::asio::io_service& io_service){
// construct TCP-socket from io_service
boost::asio::ip::tcp::socket socket(io_service);
try{
for(;;){
// local data-buffer
char data[max_length];
boost::system::error_code ec;
// read asynchronous data from socket
// execution context will be suspended until
// some bytes are read from socket
std::size_t length=socket.async_read_some(
boost::asio::buffer(data),
boost::asio::yield[ec]);
if (ec==boost::asio::error::eof)
break; //connection closed cleanly by peer
else if(ec)
throw boost::system::system_error(ec); //some other error
// write some bytes asynchronously
boost::asio::async_write(
socket,
boost::asio::buffer(data,length),
boost::asio::yield[ec]);
if (ec==boost::asio::error::eof)
break; //connection closed cleanly by peer
else if(ec)
throw boost::system::system_error(ec); //some other error
}
} catch(std::exception const& e){
std::cerr<<"Exception: "<<e.what()<<"\n";
}
}
In contrast to the previous example this one gives the impression of sequential
code and local data (['data]) while using asynchronous operations
(['async_read()], ['async_write()]). The algorithm is implemented in one
function and error handling is done by one try-catch block.
[heading recursive descent parsing]
Coroutines let you invert the flow of control so you can ask a recursive descent
parser for parsed symbols.
class Parser{
char next;
std::istream& is;
std::function<void(char)> cb;
char pull(){
return std::char_traits<char>::to_char_type(is.get());
}
void scan(){
do{
next=pull();
}
while(isspace(next));
}
public:
Parser(std::istream& is_,std::function<void(char)> cb_) :
next(), is(is_), cb(cb_)
{}
void run() {
scan();
E();
}
private:
void E(){
T();
while (next=='+'||next=='-'){
cb(next);
scan();
T();
}
}
void T(){
S();
while (next=='*'||next=='/'){
cb(next);
scan();
S();
}
}
void S(){
if (std::isdigit(next)){
cb(next);
scan();
}
else if(next=='('){
cb(next);
scan();
E();
if (next==')'){
cb(next);
scan();
}else{
throw parser_error();
}
}
else{
throw parser_error();
}
}
};
typedef boost::coroutines2::asymmetric_coroutine< char > coro_t;
int main() {
std::istringstream is("1+1");
// invert control flow
coro_t::pull_type seq(
boost::coroutines2::fixedsize_stack(),
[&is](coro_t::push_type & yield) {
// create parser with callback function
Parser p( is,
[&yield](char ch){
// resume user-code
yield(ch);
});
// start recursive parsing
p.run();
});
// user-code pulls parsed data from parser
// invert control flow
for(char c:seq){
printf("Parsed: %c\n",c);
}
}
This problem does not map at all well to communicating between independent
threads. It makes no sense for either side to proceed independently of the
other. You want them to pass control back and forth.
There's yet another advantage to using coroutines. This recursive descent parser
throws an exception when parsing fails. With a coroutine implementation, you
need only wrap the calling code in try/catch.
With communicating threads, you would have to arrange to catch the exception
and pass along the exception pointer on the same queue you're using to deliver
the other events. You would then have to rethrow the exception to unwind the
recursive document processing.
The coroutine solution maps very naturally to the problem space.
[heading 'same fringe' problem]
The advantages of suspending at an arbitrary call depth can be seen
particularly clearly with the use of a recursive function, such as traversal
of trees.
If traversing two different trees in the same deterministic order produces the
same list of leaf nodes, then both trees have the same fringe.
[$../../../../libs/coroutine2/doc/images/same_fringe.png [align center]]
Both trees in the picture have the same fringe even though the structure of the
trees is different.
The same fringe problem could be solved using coroutines by iterating over the
leaf nodes and comparing this sequence via ['std::equal()]. The range of data
values is generated by function ['traverse()] which recursively traverses the
tree and passes each node's data value to its __push_coro__.
__push_coro__ suspends the recursive computation and transfers the data value to
the main execution context.
__pull_coro_it__, created from __pull_coro__, steps over those data values and
delivers them to ['std::equal()] for comparison. Each increment of
__pull_coro_it__ resumes ['traverse()]. Upon return from
['iterator::operator++()], either a new data value is available, or tree
traversal is finished (iterator is invalidated).
In effect, the coroutine iterator presents a flattened view of the recursive
data structure.
struct node{
typedef std::shared_ptr<node> ptr_t;
// Each tree node has an optional left subtree,
// an optional right subtree and a value of its own.
// The value is considered to be between the left
// subtree and the right.
ptr_t left,right;
std::string value;
// construct leaf
node(const std::string& v):
left(),right(),value(v)
{}
// construct nonleaf
node(ptr_t l,const std::string& v,ptr_t r):
left(l),right(r),value(v)
{}
static ptr_t create(const std::string& v){
return ptr_t(new node(v));
}
static ptr_t create(ptr_t l,const std::string& v,ptr_t r){
return ptr_t(new node(l,v,r));
}
};
node::ptr_t create_left_tree_from(const std::string& root){
/* --------
root
/ \
b e
/ \
a c
-------- */
return node::create(
node::create(
node::create("a"),
"b",
node::create("c")),
root,
node::create("e"));
}
node::ptr_t create_right_tree_from(const std::string& root){
/* --------
root
/ \
a d
/ \
c e
-------- */
return node::create(
node::create("a"),
root,
node::create(
node::create("c"),
"d",
node::create("e")));
}
typedef boost::coroutines2::asymmetric_coroutine<std::string> coro_t;
// recursively walk the tree, delivering values in order
void traverse(node::ptr_t n,
coro_t::push_type& out){
if(n->left) traverse(n->left,out);
out(n->value);
if(n->right) traverse(n->right,out);
}
// evaluation
{
node::ptr_t left_d(create_left_tree_from("d"));
coro_t::pull_type left_d_reader([&](coro_t::push_type & out){
traverse(left_d,out);
});
node::ptr_t right_b(create_right_tree_from("b"));
coro_t::pull_type right_b_reader([&](coro_t::push_type & out){
traverse(right_b,out);
});
std::cout << "left tree from d == right tree from b? "
<< std::boolalpha
<< std::equal(begin(left_d_reader),
end(left_d_reader),
begin(right_b_reader))
<< std::endl;
}
{
node::ptr_t left_d(create_left_tree_from("d"));
coro_t::pull_type left_d_reader([&](coro_t::push_type & out){
traverse(left_d,out);
});
node::ptr_t right_x(create_right_tree_from("x"));
coro_t::pull_type right_x_reader([&](coro_t::push_type & out){
traverse(right_x,out);
});
std::cout << "left tree from d == right tree from x? "
<< std::boolalpha
<< std::equal(begin(left_d_reader),
end(left_d_reader),
begin(right_x_reader))
<< std::endl;
}
std::cout << "Done" << std::endl;
output:
left tree from d == right tree from b? true
left tree from d == right tree from x? false
Done
[heading chaining coroutines]
This code shows how coroutines could be chained.
typedef boost::coroutines2::asymmetric_coroutine<std::string> coro_t;
// deliver each line of input stream to sink as a separate string
void readlines(coro_t::push_type& sink,std::istream& in){
std::string line;
while(std::getline(in,line))
sink(line);
}
void tokenize(coro_t::push_type& sink, coro_t::pull_type& source){
// This tokenizer doesn't happen to be stateful: you could reasonably
// implement it with a single call to push each new token downstream. But
// I've worked with stateful tokenizers, in which the meaning of input
// characters depends in part on their position within the input line.
for(std::string line:source){
std::string::size_type pos=0;
while(pos<line.length()){
if(line[pos]=='"'){
std::string token;
++pos; // skip open quote
while(pos<line.length()&&line[pos]!='"')
token+=line[pos++];
++pos; // skip close quote
sink(token); // pass token downstream
} else if (std::isspace(line[pos])){
++pos; // outside quotes, ignore whitespace
} else if (std::isalpha(line[pos])){
std::string token;
while (pos < line.length() && std::isalpha(line[pos]))
token += line[pos++];
sink(token); // pass token downstream
} else { // punctuation
sink(std::string(1,line[pos++]));
}
}
}
}
void only_words(coro_t::push_type& sink,coro_t::pull_type& source){
for(std::string token:source){
if (!token.empty() && std::isalpha(token[0]))
sink(token);
}
}
void trace(coro_t::push_type& sink, coro_t::pull_type& source){
for(std::string token:source){
std::cout << "trace: '" << token << "'\n";
sink(token);
}
}
struct FinalEOL{
~FinalEOL(){
std::cout << std::endl;
}
};
void layout(coro_t::pull_type& source,int num,int width){
// Finish the last line when we leave by whatever means
FinalEOL eol;
// Pull values from upstream, lay them out 'num' to a line
for (;;){
for (int i = 0; i < num; ++i){
// when we exhaust the input, stop
if (!source) return;
std::cout << std::setw(width) << source.get();
// now that we've handled this item, advance to next
source();
}
// after 'num' items, line break
std::cout << std::endl;
}
}
// For example purposes, instead of having a separate text file in the
// local filesystem, construct an istringstream to read.
std::string data(
"This is the first line.\n"
"This, the second.\n"
"The third has \"a phrase\"!\n"
);
{
std::cout << "\nfilter:\n";
std::istringstream infile(data);
coro_t::pull_type reader(std::bind(readlines, _1, std::ref(infile)));
coro_t::pull_type tokenizer(std::bind(tokenize, _1, std::ref(reader)));
coro_t::pull_type filter(std::bind(only_words, _1, std::ref(tokenizer)));
coro_t::pull_type tracer(std::bind(trace, _1, std::ref(filter)));
for(std::string token:tracer){
// just iterate, we're already pulling through tracer
}
}
{
std::cout << "\nlayout() as coroutine::push_type:\n";
std::istringstream infile(data);
coro_t::pull_type reader(std::bind(readlines, _1, std::ref(infile)));
coro_t::pull_type tokenizer(std::bind(tokenize, _1, std::ref(reader)));
coro_t::pull_type filter(std::bind(only_words, _1, std::ref(tokenizer)));
coro_t::push_type writer(std::bind(layout, _1, 5, 15));
for(std::string token:filter){
writer(token);
}
}
{
std::cout << "\nfiltering output:\n";
std::istringstream infile(data);
coro_t::pull_type reader(std::bind(readlines,_1,std::ref(infile)));
coro_t::pull_type tokenizer(std::bind(tokenize,_1,std::ref(reader)));
coro_t::push_type writer(std::bind(layout,_1,5,15));
// Because of the symmetry of the API, we can use any of these
// chaining functions in a push_type coroutine chain as well.
coro_t::push_type filter(std::bind(only_words,std::ref(writer),_1));
for(std::string token:tokenizer){
filter(token);
}
}
[endsect]

36
doc/overview.qbk Normal file
View File

@ -0,0 +1,36 @@
[/
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:overview Overview]
__boost_coroutine__ provides templates for generalized subroutines which allow
suspending and resuming execution at certain locations.
It preserves the local state of execution and allows re-entering subroutines more
than once (useful if state must be kept across function calls).
Coroutines can be viewed as a language-level construct providing a special kind
of control flow.
In contrast to threads, which are pre-emptive, __coro__ switches are
cooperative (programmer controls when a switch will happen). The kernel is not
involved in the coroutine switches.
The implementation uses __boost_context__ for context switching.
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/coroutine2/all.hpp>
which includes all the other headers in turn.
All functions and classes are contained in the namespace __coro_ns__.
[note __boost_coroutine__ is C++11-only!]
[endsect]

40
doc/performance.qbk Normal file
View File

@ -0,0 +1,40 @@
[/
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:performance Performance]
Performance of __boost_coroutine__ was measured on the platforms shown in the
following table. Performance measurements were taken using `rdtsc` and
`boost::chrono::high_resolution_clock`, with overhead corrections, on x86
platforms. In each case, cache warm-up was accounted for, and the one
running thread was pinned to a single CPU.
[table Performance of asymmetric coroutines
[
[Platform]
[switch]
[construction (protected stack-allocator)]
[construction (preallocated stack-allocator)]
[construction (standard stack-allocator)]
]
[
[i386 (AMD Athlon 64 DualCore 4400+, Linux 32bit)]
[49 ns / 50 cycles]
[51 \u00b5s / 51407 cycles]
[14 \u00b5s / 15231 cycles]
[14 \u00b5s / 15216 cycles]
]
[
[x86_64 (Intel Core2 Q6700, Linux 64bit)]
[12 ns / 39 cycles]
[16 \u00b5s / 41802 cycles]
[6 \u00b5s / 10350 cycles]
[6 \u00b5s / 18817 cycles]
]
]
[endsect]

289
doc/stack.qbk Normal file
View File

@ -0,0 +1,289 @@
[/
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:stack Stack allocation]
The memory used by the stack is allocated/deallocated via a __stack_allocator__
which is required to model a __stack_allocator_concept__.
[heading __stack_allocator_concept__]
A __stack_allocator__ must satisfy the __stack_allocator_concept__ requirements
shown in the following table, in which `a` is an object of a
__stack_allocator__ type, `sctx` is a `stack_context`, and `size` is a `std::size_t`:
[table
[[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
__econtext__.]
[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).]
[section:protected_fixedsize Class ['protected_fixedsize]]
__boost_coroutine__ provides the class __protected_fixedsize__ 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__ 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 `guard page` is [*not] mapped to physical memory, only
virtual addresses are used.]
#include <boost/coroutine2/protected_fixedsize.hpp>
template< typename traitsT >
struct basic_protected_fixedsize
{
typedef traitT traits_type;
basic_protected_fixesize(std::size_t size = traits_type::default_size());
stack_context allocate();
void deallocate( stack_context &);
}
typedef basic_protected_fixedsize< stack_traits > protected_fixedsize
[heading `stack_context allocate()`]
[variablelist
[[Preconditions:] [`traits_type::minimum:size() <= size` and
`! traits_type::is_unbounded() && ( traits_type::maximum:size() >= size)`.]]
[[Effects:] [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.]]
]
[heading `void deallocate( stack_context & sctx)`]
[variablelist
[[Preconditions:] [`sctx.sp` is valid, `traits_type::minimum:size() <= sctx.size` and
`! traits_type::is_unbounded() && ( traits_type::maximum:size() >= sctx.size)`.]]
[[Effects:] [Deallocates the stack space.]]
]
[endsect]
[section:fixedsize Class ['fixedsize_stack]]
__boost_coroutine__ provides the class __fixedsize__ which models
the __stack_allocator_concept__.
In contrast to __protected_fixedsize__ it does not append a guard page at the
end of each stack. The memory is simply managed by `std::malloc()` and
`std::free()`.
#include <boost/coroutine2/fixedsize_stack.hpp>
template< typename traitsT >
struct basic_fixedsize_stack
{
typedef traitT traits_type;
basic_fixesize_stack(std::size_t size = traits_type::default_size());
stack_context allocate();
void deallocate( stack_context &);
}
typedef basic_fixedsize_stack< stack_traits > fixedsize_stack;
[heading `stack_context allocate()`]
[variablelist
[[Preconditions:] [`traits_type::minimum:size() <= size` and
`! traits_type::is_unbounded() && ( traits_type::maximum:size() >= size)`.]]
[[Effects:] [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.]]
]
[heading `void deallocate( stack_context & sctx)`]
[variablelist
[[Preconditions:] [`sctx.sp` is valid, `traits_type::minimum:size() <= sctx.size` and
`! traits_type::is_unbounded() && ( traits_type::maximum:size() >= sctx.size)`.]]
[[Effects:] [Deallocates the stack space.]]
]
[endsect]
[section:segmented Class ['segmented_stack]]
__boost_coroutine__ supports usage of a __segmented__, 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__ models the __stack_allocator_concept__.
In contrast to __protected_fixedsize__ and __fixedsize__ it creates a
stack which grows on demand.
[note Segmented stacks are currently only supported by [*gcc] from version
[*4.7] [*clang] from version [*3.4] onwards. In order to use a
__segmented_stack__ __boost_coroutine__ 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].]
#include <boost/coroutine2/segmented_stack.hpp>
template< typename traitsT >
struct basic_segmented_stack
{
typedef traitT traits_type;
basic_segmented_stack(std::size_t size = traits_type::default_size());
stack_context allocate();
void deallocate( stack_context &);
}
typedef basic_segmented_stack< stack_traits > segmented_stack;
[heading `stack_context allocate()`]
[variablelist
[[Preconditions:] [`traits_type::minimum:size() <= size` and
`! traits_type::is_unbounded() && ( traits_type::maximum:size() >= size)`.]]
[[Effects:] [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.]]
]
[heading `void deallocate( stack_context & sctx)`]
[variablelist
[[Preconditions:] [`sctx.sp` is valid, `traits_type::minimum:size() <= sctx.size` and
`! traits_type::is_unbounded() && ( traits_type::maximum:size() >= sctx.size)`.]]
[[Effects:] [Deallocates the stack space.]]
]
[endsect]
[section:stack_traits Class ['stack_traits]]
['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.
struct stack_traits
{
static bool is_unbounded() noexcept;
static std::size_t page_size() noexcept;
static std::size_t default_size() noexcept;
static std::size_t minimum_size() noexcept;
static std::size_t maximum_size() noexcept;
}
[heading `static bool is_unbounded()`]
[variablelist
[[Returns:] [Returns `true` if the environment defines no limit for the size of
a stack.]]
[[Throws:] [Nothing.]]
]
[heading `static std::size_t page_size()`]
[variablelist
[[Returns:] [Returns the page size in bytes.]]
[[Throws:] [Nothing.]]
]
[heading `static std::size_t default_size()`]
[variablelist
[[Returns:] [Returns a default stack size, which may be platform specific.
If the stack is unbounded then the present implementation returns the maximum of
`64 kB` and `minimum_size()`.]]
[[Throws:] [Nothing.]]
]
[heading `static std::size_t minimum_size()`]
[variablelist
[[Returns:] [Returns the minimum size in bytes of stack defined by the
environment (Win32 4kB/Win64 8kB, defined by rlimit on POSIX).]]
[[Throws:] [Nothing.]]
]
[heading `static std::size_t maximum_size()`]
[variablelist
[[Preconditions:] [`is_unbounded()` returns `false`.]]
[[Returns:] [Returns the maximum size in bytes of stack defined by the
environment.]]
[[Throws:] [Nothing.]]
]
[endsect]
[section:stack_context Class ['stack_context]]
__boost_coroutine__ provides the class __stack_context__ which will contain
the stack pointer and the size of the stack.
In case of a __segmented__, __stack_context__ contains some extra control
structures.
struct stack_context
{
void * sp;
std::size_t size;
// might contain additional control structures
// for segmented stacks
}
[heading `void * sp`]
[variablelist
[[Value:] [Pointer to the beginning of the stack.]]
]
[heading `std::size_t size`]
[variablelist
[[Value:] [Actual size of the stack.]]
]
[endsect]
[endsect]

49
example/Jamfile.v2 Normal file
View File

@ -0,0 +1,49 @@
# 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)
# For more information, see http://www.boost.org/
import common ;
import feature ;
import indirect ;
import modules ;
import os ;
import toolset ;
project boost/coroutine2/example
: requirements
<library>/boost/context//boost_context
<toolset>gcc,<segmented-stacks>on:<cxxflags>-fsplit-stack
<toolset>gcc,<segmented-stacks>on:<cxxflags>-DBOOST_USE_SEGMENTED_STACKS
<toolset>clang,<segmented-stacks>on:<cxxflags>-fsplit-stack
<toolset>clang,<segmented-stacks>on:<cxxflags>-DBOOST_USE_SEGMENTED_STACKS
<link>static
<threading>multi
;
exe simple
: simple.cpp
;
exe fibonacci
: fibonacci.cpp
;
exe same_fringe
: same_fringe.cpp
;
exe layout
: layout.cpp
;
exe parser
: parser.cpp
;
exe segmented
: segmented.cpp
;

34
example/fibonacci.cpp Normal file
View File

@ -0,0 +1,34 @@
// 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)
#include <cstdlib>
#include <iostream>
#include <boost/coroutine2/all.hpp>
int main()
{
boost::coroutines2::coroutine< int >::pull_type source(
[&]( boost::coroutines2::coroutine< int >::push_type & sink) {
int first = 1, second = 1;
sink( first);
sink( second);
for ( int i = 0; i < 8; ++i)
{
int third = first + second;
first = second;
second = third;
sink( third);
}
});
for ( auto i : source)
std::cout << i << " ";
std::cout << "\nDone" << std::endl;
return EXIT_SUCCESS;
}

53
example/layout.cpp Normal file
View File

@ -0,0 +1,53 @@
// Copyright Nat Goodspeed 2013.
// 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)
#include <iostream>
#include <iomanip>
#include <vector>
#include <string>
#include <utility>
#include <boost/coroutine2/all.hpp>
struct FinalEOL{
~FinalEOL(){
std::cout << std::endl;
}
};
int main(int argc,char* argv[]){
using std::begin;
using std::end;
std::vector<std::string> words{
"peas", "porridge", "hot", "peas",
"porridge", "cold", "peas", "porridge",
"in", "the", "pot", "nine",
"days", "old" };
int num=5,width=15;
boost::coroutines2::coroutine<std::string>::push_type writer(
[&](boost::coroutines2::coroutine<std::string>::pull_type& in){
// finish the last line when we leave by whatever means
FinalEOL eol;
// pull values from upstream, lay them out 'num' to a line
for (;;){
for(int i=0;i<num;++i){
// when we exhaust the input, stop
if(!in) return;
std::cout << std::setw(width) << in.get();
// now that we've handled this item, advance to next
in();
}
// after 'num' items, line break
std::cout << std::endl;
}
});
std::copy(begin(words),end(words),begin(writer));
return 0;
}

109
example/parser.cpp Normal file
View File

@ -0,0 +1,109 @@
// 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)
#include <cstdio>
#include <functional>
#include <iostream>
#include <sstream>
#include <thread>
#include <boost/coroutine2/all.hpp>
/*
* grammar:
* P ---> E '\0'
* E ---> T {('+'|'-') T}
* T ---> S {('*'|'/') S}
* S ---> digit | '(' E ')'
*/
class Parser{
char next;
std::istream& is;
std::function<void(char)> cb;
char pull(){
return std::char_traits<char>::to_char_type(is.get());
}
void scan(){
do{
next=pull();
}
while(isspace(next));
}
public:
Parser(std::istream& is_,std::function<void(char)> cb_) :
next(), is(is_), cb(cb_)
{}
void run() {
scan();
E();
}
private:
void E(){
T();
while (next=='+'||next=='-'){
cb(next);
scan();
T();
}
}
void T(){
S();
while (next=='*'||next=='/'){
cb(next);
scan();
S();
}
}
void S(){
if (std::isdigit(next)){
cb(next);
scan();
}
else if(next=='('){
cb(next);
scan();
E();
if (next==')'){
cb(next);
scan();
}else{
exit(2);
}
}
else{
exit(3);
}
}
};
typedef boost::coroutines2::asymmetric_coroutine< char > coro_t;
int main() {
std::istringstream is("1+1");
// invert control flow
coro_t::pull_type seq(
boost::coroutines2::fixedsize_stack(),
[&is]( coro_t::push_type & yield) {
Parser p( is,
[&yield](char ch){
yield(ch);
});
p.run();
});
// user-code pulls parsed data from parser
for(char c:seq){
printf("Parsed: %c\n",c);
}
}

176
example/same_fringe.cpp Normal file
View File

@ -0,0 +1,176 @@
// Copyright Nat Goodspeed 2013.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSEstd::placeholders::_1_0.txt or copy at
// http://www.boost.org/LICENSEstd::placeholders::_1_0.txt)
#include <cstddef>
#include <cstdlib>
#include <iostream>
#include <iterator>
#include <string>
#include <utility>
#include <boost/coroutine2/all.hpp>
struct node
{
typedef std::shared_ptr< node > ptr_t;
// Each tree node has an optional left subtree, an optional right subtree
// and a value of its own. The value is considered to be between the left
// subtree and the right.
ptr_t left, right;
std::string value;
// construct leaf
node(const std::string& v):
left(),right(),value(v)
{}
// construct nonleaf
node(ptr_t l, const std::string& v, ptr_t r):
left(l),right(r),value(v)
{}
static ptr_t create(const std::string& v)
{
return ptr_t(new node(v));
}
static ptr_t create(ptr_t l, const std::string& v, ptr_t r)
{
return ptr_t(new node(l, v, r));
}
};
node::ptr_t create_left_tree_from(const std::string& root)
{
/* --------
root
/ \
b e
/ \
a c
-------- */
return node::create(
node::create(
node::create("a"),
"b",
node::create("c")),
root,
node::create("e"));
}
node::ptr_t create_right_tree_from(const std::string& root)
{
/* --------
root
/ \
a d
/ \
c e
-------- */
return node::create(
node::create("a"),
root,
node::create(
node::create("c"),
"d",
node::create("e")));
}
// recursively walk the tree, delivering values in order
void traverse(node::ptr_t n, boost::coroutines2::coroutine<std::string>::push_type& out)
{
if (n->left)
traverse(n->left,out);
out(n->value);
if (n->right)
traverse(n->right,out);
}
int main()
{
{
node::ptr_t left_d(create_left_tree_from("d"));
boost::coroutines2::coroutine<std::string>::pull_type left_d_reader(
[&]( boost::coroutines2::coroutine<std::string>::push_type & out) {
traverse(left_d,out);
});
std::cout << "left tree from d:\n";
std::copy(begin(left_d_reader),
end(left_d_reader),
std::ostream_iterator<std::string>(std::cout, " "));
std::cout << std::endl;
node::ptr_t right_b(create_right_tree_from("b"));
boost::coroutines2::coroutine<std::string>::pull_type right_b_reader(
[&]( boost::coroutines2::coroutine<std::string>::push_type & out) {
traverse(right_b,out);
});
std::cout << "right tree from b:\n";
std::copy(begin(right_b_reader),
end(right_b_reader),
std::ostream_iterator<std::string>(std::cout, " "));
std::cout << std::endl;
node::ptr_t right_x(create_right_tree_from("x"));
boost::coroutines2::coroutine<std::string>::pull_type right_x_reader(
[&]( boost::coroutines2::coroutine<std::string>::push_type & out) {
traverse(right_x,out);
});
std::cout << "right tree from x:\n";
std::copy(begin(right_x_reader),
end(right_x_reader),
std::ostream_iterator<std::string>(std::cout, " "));
std::cout << std::endl;
}
{
node::ptr_t left_d(create_left_tree_from("d"));
boost::coroutines2::coroutine<std::string>::pull_type left_d_reader(
[&]( boost::coroutines2::coroutine<std::string>::push_type & out) {
traverse(left_d,out);
});
node::ptr_t right_b(create_right_tree_from("b"));
boost::coroutines2::coroutine<std::string>::pull_type right_b_reader(
[&]( boost::coroutines2::coroutine<std::string>::push_type & out) {
traverse(right_b,out);
});
std::cout << "left tree from d == right tree from b? "
<< std::boolalpha
<< std::equal(begin(left_d_reader),
end(left_d_reader),
begin(right_b_reader))
<< std::endl;
}
{
node::ptr_t left_d(create_left_tree_from("d"));
boost::coroutines2::coroutine<std::string>::pull_type left_d_reader(
[&]( boost::coroutines2::coroutine<std::string>::push_type & out) {
traverse(left_d,out);
});
node::ptr_t right_x(create_right_tree_from("x"));
boost::coroutines2::coroutine<std::string>::pull_type right_x_reader(
[&]( boost::coroutines2::coroutine<std::string>::push_type & out) {
traverse(right_x,out);
});
std::cout << "left tree from d == right tree from x? "
<< std::boolalpha
<< std::equal(begin(left_d_reader),
end(left_d_reader),
begin(right_x_reader))
<< std::endl;
}
std::cout << "Done" << std::endl;
return EXIT_SUCCESS;
}

65
example/segmented.cpp Normal file
View File

@ -0,0 +1,65 @@
// 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)
#include <cstdlib>
#include <iostream>
#include <boost/config.hpp>
#include <boost/coroutine2/all.hpp>
#ifdef BOOST_MSVC //MS VisualStudio
__declspec(noinline) void access( char *buf);
#else // GCC
void access( char *buf) __attribute__ ((noinline));
#endif
void access( char *buf)
{
buf[0] = '\0';
}
void bar( int i)
{
char buf[4 * 1024];
if ( i > 0)
{
access( buf);
std::cout << i << ". iteration" << std::endl;
bar( i - 1);
}
}
int main() {
int count = 384;
#if defined(BOOST_USE_SEGMENTED_STACKS)
std::cout << "using segmented_stack stacks: allocates " << count << " * 4kB == " << 4 * count << "kB on stack, ";
std::cout << "initial stack size = " << boost::context::segmented_stack::traits_type::default_size() / 1024 << "kB" << std::endl;
std::cout << "application should not fail" << std::endl;
#else
std::cout << "using standard stacks: allocates " << count << " * 4kB == " << 4 * count << "kB on stack, ";
std::cout << "initial stack size = " << boost::context::fixedsize_stack::traits_type::default_size() / 1024 << "kB" << std::endl;
std::cout << "application might fail" << std::endl;
#endif
boost::coroutines2::coroutine< void >::push_type sink(
#if defined(BOOST_USE_SEGMENTED_STACKS)
boost::coroutines2::segmented_stack(),
#else
boost::coroutines2::fixedsize_stack(),
#endif
[&]( boost::coroutines2::coroutine< void >::pull_type & source) {
bar( count);
source();
});
sink();
std::cout << "main: done" << std::endl;
return 0;
}

24
example/simple.cpp Normal file
View File

@ -0,0 +1,24 @@
// 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)
#include <cstdlib>
#include <iostream>
#include <boost/coroutine2/all.hpp>
int main()
{
int i = 0;
boost::coroutines2::coroutine< void >::push_type sink(
[&]( boost::coroutines2::coroutine< void >::pull_type & source) {
std::cout << "inside coroutine-fn" << std::endl;
});
sink();
std::cout << "\nDone" << std::endl;
return EXIT_SUCCESS;
}

97
example/tree.h Normal file
View File

@ -0,0 +1,97 @@
// 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)
#ifndef TREE_H
#define TREE_H
#include <cstddef>
#include <string>
#include <boost/assert.hpp>
#include <boost/config.hpp>
#include <boost/intrusive_ptr.hpp>
#if defined(_MSC_VER)
# pragma warning(push)
# pragma warning(disable:4355)
#endif
struct branch;
struct leaf;
struct visitor
{
virtual ~visitor() {};
virtual void visit( branch & b) = 0;
virtual void visit( leaf & l) = 0;
};
struct node
{
typedef boost::intrusive_ptr< node > ptr_t;
std::size_t use_count;
node() :
use_count( 0)
{}
virtual ~node() {}
virtual void accept( visitor & v) = 0;
friend inline void intrusive_ptr_add_ref( node * p)
{ ++p->use_count; }
friend inline void intrusive_ptr_release( node * p)
{ if ( 0 == --p->use_count) delete p; }
};
struct branch : public node
{
node::ptr_t left;
node::ptr_t right;
static ptr_t create( node::ptr_t left_, node::ptr_t right_)
{ return ptr_t( new branch( left_, right_) ); }
branch( node::ptr_t left_, node::ptr_t right_) :
left( left_), right( right_)
{}
void accept( visitor & v)
{ v.visit( * this); }
};
struct leaf : public node
{
std::string value;
static ptr_t create( std::string const& value_)
{ return ptr_t( new leaf( value_) ); }
leaf( std::string const& value_) :
value( value_)
{}
void accept( visitor & v)
{ v.visit( * this); }
};
inline
bool operator==( leaf const& l, leaf const& r)
{ return l.value == r.value; }
inline
bool operator!=( leaf const& l, leaf const& r)
{ return l.value != r.value; }
#if defined(_MSC_VER)
# pragma warning(pop)
#endif
#endif // TREE_H

View File

@ -0,0 +1,15 @@
// 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)
#ifndef BOOST_COROUTINES2_ALL_H
#define BOOST_COROUTINES2_ALL_H
#include <boost/coroutine2/coroutine.hpp>
#include <boost/coroutine2/fixedsize_stack.hpp>
#include <boost/coroutine2/protected_fixedsize_stack.hpp>
#include <boost/coroutine2/segmented_stack.hpp>
#endif // BOOST_COROUTINES2_ALL_H

View File

@ -0,0 +1,39 @@
// 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)
#ifndef BOOST_COROUTINES2_COROUTINE_H
#define BOOST_COROUTINES2_COROUTINE_H
#include <exception>
#include <boost/assert.hpp>
#include <boost/config.hpp>
#include <boost/coroutine2/detail/coroutine.hpp>
#ifdef BOOST_HAS_ABI_HEADERS
# include BOOST_ABI_PREFIX
#endif
namespace boost {
namespace coroutines2 {
template< typename T >
struct asymmetric_coroutine {
typedef detail::pull_coroutine< T > pull_type;
typedef detail::push_coroutine< T > push_type;
};
template< typename T >
using coroutine = asymmetric_coroutine< T >;
}}
#ifdef BOOST_HAS_ABI_HEADERS
# include BOOST_ABI_SUFFIX
#endif
#endif // BOOST_COROUTINES2_COROUTINE_H

View File

@ -0,0 +1,49 @@
// 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)
#ifndef BOOST_COROUTINES2_DETAIL_CONFIG_H
#define BOOST_COROUTINES2_DETAIL_CONFIG_H
#include <boost/config.hpp>
#include <boost/detail/workaround.hpp>
#ifdef BOOST_COROUTINES2_DECL
# undef BOOST_COROUTINES2_DECL
#endif
#if (defined(BOOST_ALL_DYN_LINK) || defined(BOOST_COROUTINES2_DYN_LINK) ) && ! defined(BOOST_COROUTINES2_STATIC_LINK)
# if defined(BOOST_COROUTINES2_SOURCE)
# define BOOST_COROUTINES2_DECL BOOST_SYMBOL_EXPORT
# define BOOST_COROUTINES2_BUILD_DLL
# else
# define BOOST_COROUTINES2_DECL BOOST_SYMBOL_IMPORT
# endif
#endif
#if ! defined(BOOST_COROUTINES2_DECL)
# define BOOST_COROUTINES2_DECL
#endif
#if ! defined(BOOST_COROUTINES2_SOURCE) && ! defined(BOOST_ALL_NO_LIB) && ! defined(BOOST_COROUTINES2_NO_LIB)
# define BOOST_LIB_NAME boost_coroutine
# if defined(BOOST_ALL_DYN_LINK) || defined(BOOST_COROUTINES2_DYN_LINK)
# define BOOST_DYN_LINK
# endif
# include <boost/config/auto_link.hpp>
#endif
#if defined(BOOST_USE_SEGMENTED_STACKS)
# if ! ( (defined(__GNUC__) && __GNUC__ > 3 && __GNUC_MINOR__ > 6) || \
(defined(__clang__) && __clang_major__ > 2 && __clang_minor__ > 3) )
# error "compiler does not support segmented_stack stacks"
# endif
# define BOOST_COROUTINES2_SEGMENTS 10
#endif
#define BOOST_COROUTINES2_UNIDIRECT
#define BOOST_COROUTINES2_SYMMETRIC
#endif // BOOST_COROUTINES2_DETAIL_CONFIG_H

View File

@ -0,0 +1,44 @@
// 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)
#ifndef BOOST_COROUTINES2_DETAIL_COROUTINE_HPP
#define BOOST_COROUTINES2_DETAIL_COROUTINE_HPP
#include <boost/config.hpp>
#ifdef BOOST_HAS_ABI_HEADERS
# include BOOST_ABI_PREFIX
#endif
namespace boost {
namespace coroutines2 {
namespace detail {
template< typename T >
class pull_coroutine;
template< typename T >
class push_coroutine;
}}}
#include <boost/coroutine2/detail/pull_coroutine.hpp>
#include <boost/coroutine2/detail/push_coroutine.hpp>
#include <boost/coroutine2/detail/pull_control_block.hpp>
#include <boost/coroutine2/detail/push_control_block.hpp>
#include <boost/coroutine2/detail/pull_coroutine.ipp>
#include <boost/coroutine2/detail/push_coroutine.ipp>
#include <boost/coroutine2/detail/pull_control_block.ipp>
#include <boost/coroutine2/detail/push_control_block.ipp>
#ifdef BOOST_HAS_ABI_HEADERS
# include BOOST_ABI_SUFFIX
#endif
#endif // BOOST_COROUTINES2_DETAIL_COROUTINE_HPP

View File

@ -0,0 +1,33 @@
// 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)
#ifndef BOOST_COROUTINES2_DETAIL_FORCED_UNWIND_HPP
#define BOOST_COROUTINES2_DETAIL_FORCED_UNWIND_HPP
#include <exception>
#include <boost/assert.hpp>
#include <boost/config.hpp>
#include <boost/context/execution_context.hpp>
#ifdef BOOST_HAS_ABI_HEADERS
# include BOOST_ABI_PREFIX
#endif
namespace boost {
namespace coroutines2 {
namespace detail {
struct forced_unwind {};
}}}
#ifdef BOOST_HAS_ABI_HEADERS
# include BOOST_ABI_SUFFIX
#endif
#endif // BOOST_COROUTINES2_DETAIL_FORCED_UNWIND_HPP

View File

@ -0,0 +1,102 @@
// 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)
#ifndef BOOST_COROUTINES2_DETAIL_PULL_CONTROL_BLOCK_HPP
#define BOOST_COROUTINES2_DETAIL_PULL_CONTROL_BLOCK_HPP
#include <exception>
#include <boost/config.hpp>
#include <boost/context/execution_context.hpp>
#include <boost/coroutine2/detail/rref.hpp>
#ifdef BOOST_HAS_ABI_HEADERS
# include BOOST_ABI_PREFIX
#endif
namespace boost {
namespace coroutines2 {
namespace detail {
template< typename T >
struct pull_coroutine< T >::control_block {
typename push_coroutine< T >::control_block * other;
boost::context::execution_context caller;
boost::context::execution_context callee;
bool preserve_fpu;
int state;
std::exception_ptr except;
template< typename StackAllocator, typename Fn >
control_block( context::preallocated, StackAllocator, rref< Fn >, bool);
explicit control_block( typename push_coroutine< T >::control_block *);
~control_block();
control_block( control_block &) = delete;
control_block & operator=( control_block &) = delete;
void jump_to();
bool valid() const noexcept;
};
template< typename T >
struct pull_coroutine< T & >::control_block {
typename push_coroutine< T & >::control_block * other;
boost::context::execution_context caller;
boost::context::execution_context callee;
bool preserve_fpu;
int state;
std::exception_ptr except;
template< typename StackAllocator, typename Fn >
control_block( context::preallocated, StackAllocator, rref< Fn >, bool);
explicit control_block( typename push_coroutine< T & >::control_block *);
~control_block();
control_block( control_block &) = delete;
control_block & operator=( control_block &) = delete;
void jump_to();
bool valid() const noexcept;
};
struct pull_coroutine< void >::control_block {
typename push_coroutine< void >::control_block * other;
boost::context::execution_context caller;
boost::context::execution_context callee;
bool preserve_fpu;
int state;
std::exception_ptr except;
template< typename StackAllocator, typename Fn >
control_block( context::preallocated, StackAllocator, rref< Fn >, bool);
explicit control_block( typename push_coroutine< void >::control_block *);
~control_block();
control_block( control_block &) = delete;
control_block & operator=( control_block &) = delete;
void jump_to();
bool valid() const noexcept;
};
}}}
#ifdef BOOST_HAS_ABI_HEADERS
# include BOOST_ABI_SUFFIX
#endif
#endif // BOOST_COROUTINES2_DETAIL_PULL_CONTROL_BLOCK_HPP

View File

@ -0,0 +1,259 @@
// 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)
#ifndef BOOST_COROUTINES2_DETAIL_PULL_CONTROL_BLOCK_IPP
#define BOOST_COROUTINES2_DETAIL_PULL_CONTROL_BLOCK_IPP
#include <exception>
#include <boost/assert.hpp>
#include <boost/config.hpp>
#include <boost/context/execution_context.hpp>
#include <boost/coroutine2/detail/config.hpp>
#include <boost/coroutine2/detail/forced_unwind.hpp>
#include <boost/coroutine2/detail/rref.hpp>
#include <boost/coroutine2/detail/state.hpp>
#ifdef BOOST_HAS_ABI_HEADERS
# include BOOST_ABI_PREFIX
#endif
namespace boost {
namespace coroutines2 {
namespace detail {
// pull_coroutine< T >
template< typename T >
template< typename StackAllocator, typename Fn >
pull_coroutine< T >::control_block::control_block( context::preallocated palloc, StackAllocator salloc,
rref< Fn > rr, bool preserve_fpu_) :
other( nullptr),
caller( boost::context::execution_context::current() ),
callee( palloc, salloc,
[=] () mutable {
try {
// create synthesized push_coroutine< T >
typename push_coroutine< T >::control_block synthesized_cb( this);
push_coroutine< T > synthesized( & synthesized_cb);
other = & synthesized_cb;
// call coroutine-fn with synthesized push_coroutine as argument
rr( synthesized);
} catch ( forced_unwind const&) {
// do nothing for unwinding exception
} catch (...) {
// store other exceptions in exception-pointer
except = std::current_exception();
}
// set termination flags
state |= static_cast< int >( state_t::complete);
// jump back to caller
caller.jump_to( preserve_fpu);
BOOST_ASSERT_MSG( false, "pull_coroutine is complete");
}),
preserve_fpu( preserve_fpu_),
state( static_cast< int >( state_t::unwind) ),
except() {
callee.jump_to( preserve_fpu);
}
template< typename T >
pull_coroutine< T >::control_block::control_block( typename push_coroutine< T >::control_block * cb) :
other( cb),
caller( other->callee),
callee( other->caller),
preserve_fpu( other->preserve_fpu),
state( 0),
except() {
}
template< typename T >
pull_coroutine< T >::control_block::~control_block() {
if ( 0 == ( state & static_cast< int >( state_t::complete ) ) &&
0 != ( state & static_cast< int >( state_t::unwind) ) ) {
// set early-exit flag
state |= static_cast< int >( state_t::early_exit);
callee.jump_to( preserve_fpu);
}
}
template< typename T >
void
pull_coroutine< T >::control_block::jump_to() {
callee.jump_to( preserve_fpu);
if ( except) {
std::rethrow_exception( except);
}
// test early-exit-flag
if ( 0 != ( ( other->state) & static_cast< int >( state_t::early_exit) ) ) {
throw forced_unwind();
}
}
template< typename T >
bool
pull_coroutine< T >::control_block::valid() const noexcept {
return nullptr != other && nullptr != other->t && 0 == ( state & static_cast< int >( state_t::complete) );
}
// pull_coroutine< T & >
template< typename T >
template< typename StackAllocator, typename Fn >
pull_coroutine< T & >::control_block::control_block( context::preallocated palloc, StackAllocator salloc,
rref< Fn > rr, bool preserve_fpu_) :
other( nullptr),
caller( boost::context::execution_context::current() ),
callee( palloc, salloc,
[=] () mutable {
try {
// create synthesized push_coroutine< T >
typename push_coroutine< T & >::control_block synthesized_cb( this);
push_coroutine< T & > synthesized( & synthesized_cb);
other = & synthesized_cb;
// call coroutine-fn with synthesized push_coroutine as argument
rr( synthesized);
} catch ( forced_unwind const&) {
// do nothing for unwinding exception
} catch (...) {
// store other exceptions in exception-pointer
except = std::current_exception();
}
// set termination flags
state |= static_cast< int >( state_t::complete);
// jump back to caller
caller.jump_to( preserve_fpu);
BOOST_ASSERT_MSG( false, "pull_coroutine is complete");
}),
preserve_fpu( preserve_fpu_),
state( static_cast< int >( state_t::unwind) ),
except() {
callee.jump_to( preserve_fpu);
}
template< typename T >
pull_coroutine< T & >::control_block::control_block( typename push_coroutine< T & >::control_block * cb) :
other( cb),
caller( other->callee),
callee( other->caller),
preserve_fpu( other->preserve_fpu),
state( 0),
except() {
}
template< typename T >
pull_coroutine< T & >::control_block::~control_block() {
if ( 0 == ( state & static_cast< int >( state_t::complete ) ) &&
0 != ( state & static_cast< int >( state_t::unwind) ) ) {
// set early-exit flag
state |= static_cast< int >( state_t::early_exit);
callee.jump_to( preserve_fpu);
}
}
template< typename T >
void
pull_coroutine< T & >::control_block::jump_to() {
callee.jump_to( preserve_fpu);
if ( except) {
std::rethrow_exception( except);
}
// test early-exit-flag
if ( 0 != ( ( other->state) & static_cast< int >( state_t::early_exit) ) ) {
throw forced_unwind();
}
}
template< typename T >
bool
pull_coroutine< T & >::control_block::valid() const noexcept {
return nullptr != other && nullptr != other->t && 0 == ( state & static_cast< int >( state_t::complete) );
}
// pull_coroutine< void >
template< typename StackAllocator, typename Fn >
pull_coroutine< void >::control_block::control_block( context::preallocated palloc, StackAllocator salloc,
rref< Fn > rr, bool preserve_fpu_) :
other( nullptr),
caller( boost::context::execution_context::current() ),
callee( palloc, salloc,
[=] () mutable {
try {
// create synthesized push_coroutine< T >
typename push_coroutine< void >::control_block synthesized_cb( this);
push_coroutine< void > synthesized( & synthesized_cb);
other = & synthesized_cb;
// call coroutine-fn with synthesized push_coroutine as argument
rr( synthesized);
} catch ( forced_unwind const&) {
// do nothing for unwinding exception
} catch (...) {
// store other exceptions in exception-pointer
except = std::current_exception();
}
// set termination flags
state |= static_cast< int >( state_t::complete);
// jump back to caller
caller.jump_to( preserve_fpu);
BOOST_ASSERT_MSG( false, "pull_coroutine is complete");
}),
preserve_fpu( preserve_fpu_),
state( static_cast< int >( state_t::unwind) ),
except() {
callee.jump_to( preserve_fpu);
}
inline
pull_coroutine< void >::control_block::control_block( typename push_coroutine< void >::control_block * cb) :
other( cb),
caller( other->callee),
callee( other->caller),
preserve_fpu( other->preserve_fpu),
state( 0),
except() {
}
inline
pull_coroutine< void >::control_block::~control_block() {
if ( 0 == ( state & static_cast< int >( state_t::complete ) ) &&
0 != ( state & static_cast< int >( state_t::unwind) ) ) {
// set early-exit flag
state |= static_cast< int >( state_t::early_exit);
callee.jump_to( preserve_fpu);
}
}
inline
void
pull_coroutine< void >::control_block::jump_to() {
callee.jump_to( preserve_fpu);
if ( except) {
std::rethrow_exception( except);
}
// test early-exit-flag
if ( 0 != ( ( other->state) & static_cast< int >( state_t::early_exit) ) ) {
throw forced_unwind();
}
}
inline
bool
pull_coroutine< void >::control_block::valid() const noexcept {
return nullptr != other && 0 == ( state & static_cast< int >( state_t::complete) );
}
}}}
#ifdef BOOST_HAS_ABI_HEADERS
# include BOOST_ABI_SUFFIX
#endif
#endif // BOOST_COROUTINES2_DETAIL_PULL_CONTROL_BLOCK_IPP

View File

@ -0,0 +1,320 @@
// 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)
#ifndef BOOST_COROUTINES2_DETAIL_PULL_COROUTINE_HPP
#define BOOST_COROUTINES2_DETAIL_PULL_COROUTINE_HPP
#include <iterator>
#include <type_traits>
#include <boost/assert.hpp>
#include <boost/config.hpp>
#include <boost/context/execution_context.hpp>
#include <boost/coroutine2/detail/config.hpp>
#ifdef BOOST_HAS_ABI_HEADERS
# include BOOST_ABI_PREFIX
#endif
namespace boost {
namespace coroutines2 {
namespace detail {
template< typename T >
class pull_coroutine {
private:
template< typename X >
friend class push_coroutine;
struct control_block;
control_block * cb_;
explicit pull_coroutine( control_block *);
bool has_result_() const;
public:
template< typename Fn >
explicit pull_coroutine( Fn &&, bool = false);
template< typename StackAllocator, typename Fn >
explicit pull_coroutine( StackAllocator, Fn &&, bool = false);
~pull_coroutine();
pull_coroutine( pull_coroutine const&) = delete;
pull_coroutine & operator=( pull_coroutine const&) = delete;
pull_coroutine( pull_coroutine &&);
pull_coroutine & operator=( pull_coroutine && other) {
if ( this != & other) {
cb_ = other.cb_;
other.cb_ = nullptr;
}
return * this;
}
pull_coroutine & operator()();
explicit operator bool() const noexcept;
bool operator!() const noexcept;
T get() const noexcept;
class iterator : public std::iterator< std::input_iterator_tag, typename std::remove_reference< T >::type > {
private:
pull_coroutine< T > * c_;
void fetch_() {
BOOST_ASSERT( nullptr != c_);
if ( ! ( * c_) ) {
c_ = nullptr;
return;
}
}
void increment_() {
BOOST_ASSERT( nullptr != c_);
BOOST_ASSERT( * c_);
( * c_)();
fetch_();
}
public:
typedef typename iterator::pointer pointer_t;
typedef typename iterator::reference reference_t;
iterator() :
c_( nullptr) {
}
explicit iterator( pull_coroutine< T > * c) :
c_( c) {
fetch_();
}
iterator( iterator const& other) :
c_( other.c_) {
}
iterator & operator=( iterator const& other) {
if ( this == & other) return * this;
c_ = other.c_;
return * this;
}
bool operator==( iterator const& other) const {
return other.c_ == c_;
}
bool operator!=( iterator const& other) const {
return other.c_ != c_;
}
iterator & operator++() {
increment_();
return * this;
}
iterator operator++( int) {
iterator tmp( * this);
++*this;
return tmp;
}
reference_t operator*() const {
return * c_->cb_->other->t;
}
pointer_t operator->() const {
return c_->cb_->other->t;
}
};
friend class iterator;
};
template< typename T >
class pull_coroutine< T & > {
private:
template< typename X >
friend class push_coroutine;
struct control_block;
control_block * cb_;
explicit pull_coroutine( control_block *);
bool has_result_() const;
public:
template< typename Fn >
explicit pull_coroutine( Fn &&, bool = false);
template< typename StackAllocator, typename Fn >
explicit pull_coroutine( StackAllocator, Fn &&, bool = false);
~pull_coroutine();
pull_coroutine( pull_coroutine const&) = delete;
pull_coroutine & operator=( pull_coroutine const&) = delete;
pull_coroutine( pull_coroutine &&);
pull_coroutine & operator=( pull_coroutine && other) {
if ( this != & other) {
cb_ = other.cb_;
other.cb_ = nullptr;
}
return * this;
}
pull_coroutine & operator()();
explicit operator bool() const noexcept;
bool operator!() const noexcept;
T & get() const noexcept;
class iterator : public std::iterator< std::input_iterator_tag, typename std::remove_reference< T >::type > {
private:
pull_coroutine< T & > * c_;
void fetch_() {
BOOST_ASSERT( nullptr != c_);
if ( ! ( * c_) ) {
c_ = nullptr;
return;
}
}
void increment_() {
BOOST_ASSERT( nullptr != c_);
BOOST_ASSERT( * c_);
( * c_)();
fetch_();
}
public:
typedef typename iterator::pointer pointer_t;
typedef typename iterator::reference reference_t;
iterator() :
c_( nullptr) {
}
explicit iterator( pull_coroutine< T & > * c) :
c_( c) {
fetch_();
}
iterator( iterator const& other) :
c_( other.c_) {
}
iterator & operator=( iterator const& other) {
if ( this == & other) return * this;
c_ = other.c_;
return * this;
}
bool operator==( iterator const& other) const {
return other.c_ == c_;
}
bool operator!=( iterator const& other) const {
return other.c_ != c_;
}
iterator & operator++() {
increment_();
return * this;
}
iterator operator++( int) {
iterator tmp( * this);
++*this;
return tmp;
}
reference_t operator*() const {
return * c_->cb_->other->t;
}
pointer_t operator->() const {
return c_->cb_->other->t;
}
};
friend class iterator;
};
template<>
class pull_coroutine< void > {
private:
template< typename X >
friend class push_coroutine;
struct control_block;
control_block * cb_;
explicit pull_coroutine( control_block *);
public:
template< typename Fn >
explicit pull_coroutine( Fn &&, bool = false);
template< typename StackAllocator, typename Fn >
explicit pull_coroutine( StackAllocator, Fn &&, bool = false);
~pull_coroutine();
pull_coroutine( pull_coroutine const&) = delete;
pull_coroutine & operator=( pull_coroutine const&) = delete;
pull_coroutine( pull_coroutine &&);
pull_coroutine & operator=( pull_coroutine && other) {
if ( this != & other) {
cb_ = other.cb_;
other.cb_ = nullptr;
}
return * this;
}
pull_coroutine & operator()();
explicit operator bool() const noexcept;
bool operator!() const noexcept;
};
template< typename T >
typename pull_coroutine< T >::iterator
begin( pull_coroutine< T > & c) {
return typename pull_coroutine< T >::iterator( & c);
}
template< typename T >
typename pull_coroutine< T >::iterator
end( pull_coroutine< T > &) {
return typename pull_coroutine< T >::iterator();
}
}}}
#ifdef BOOST_HAS_ABI_HEADERS
# include BOOST_ABI_SUFFIX
#endif
#endif // BOOST_COROUTINES2_DETAIL_PULL_COROUTINE_HPP

View File

@ -0,0 +1,226 @@
// 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)
#ifndef BOOST_COROUTINES2_DETAIL_PULL_COROUTINE_IPP
#define BOOST_COROUTINES2_DETAIL_PULL_COROUTINE_IPP
#include <algorithm>
#include <utility>
#include <boost/assert.hpp>
#include <boost/config.hpp>
#include <boost/context/execution_context.hpp>
#include <boost/context/stack_context.hpp>
#include <boost/coroutine2/detail/config.hpp>
#include <boost/coroutine2/detail/rref.hpp>
#include <boost/coroutine2/fixedsize_stack.hpp>
#ifdef BOOST_HAS_ABI_HEADERS
# include BOOST_ABI_PREFIX
#endif
namespace boost {
namespace coroutines2 {
namespace detail {
// pull_coroutine< T >
template< typename T >
pull_coroutine< T >::pull_coroutine( control_block * cb) :
cb_( cb) {
}
template< typename T >
bool
pull_coroutine< T >::has_result_() const {
return nullptr != cb_->other->t;
}
template< typename T >
template< typename Fn >
pull_coroutine< T >::pull_coroutine( Fn && fn, bool preserve_fpu) :
pull_coroutine( fixedsize_stack(), std::forward< Fn >( fn), preserve_fpu) {
}
template< typename T >
template< typename StackAllocator, typename Fn >
pull_coroutine< T >::pull_coroutine( StackAllocator salloc, Fn && fn, bool preserve_fpu) :
cb_( nullptr) {
context::stack_context sctx( salloc.allocate() );
void * sp = static_cast< char * >( sctx.sp) - sizeof( control_block);
std::size_t size = sctx.size - sizeof( control_block);
cb_ = new ( sp) control_block( context::preallocated( sp, size, sctx),
salloc, rref< Fn >( std::forward< Fn >( fn) ), preserve_fpu);
}
template< typename T >
pull_coroutine< T >::~pull_coroutine() {
if ( nullptr != cb_) {
cb_->~control_block();
}
}
template< typename T >
pull_coroutine< T >::pull_coroutine( pull_coroutine && other) :
cb_( nullptr) {
std::swap( cb_, other.cb_);
}
template< typename T >
pull_coroutine< T > &
pull_coroutine< T >::operator()() {
cb_->jump_to();
return * this;
}
template< typename T >
pull_coroutine< T >::operator bool() const noexcept {
return nullptr != cb_ && cb_->valid();
}
template< typename T >
bool
pull_coroutine< T >::operator!() const noexcept {
return nullptr == cb_ || ! cb_->valid();
}
template< typename T >
T
pull_coroutine< T >::get() const noexcept {
return std::move( * cb_->other->t);
}
// pull_coroutine< T & >
template< typename T >
pull_coroutine< T & >::pull_coroutine( control_block * cb) :
cb_( cb) {
}
template< typename T >
bool
pull_coroutine< T & >::has_result_() const {
return nullptr != cb_->other->t;
}
template< typename T >
template< typename Fn >
pull_coroutine< T & >::pull_coroutine( Fn && fn, bool preserve_fpu) :
pull_coroutine( fixedsize_stack(), std::forward< Fn >( fn), preserve_fpu) {
}
template< typename T >
template< typename StackAllocator, typename Fn >
pull_coroutine< T & >::pull_coroutine( StackAllocator salloc, Fn && fn, bool preserve_fpu) :
cb_( nullptr) {
context::stack_context sctx( salloc.allocate() );
void * sp = static_cast< char * >( sctx.sp) - sizeof( control_block);
std::size_t size = sctx.size - sizeof( control_block);
cb_ = new ( sp) control_block( context::preallocated( sp, size, sctx),
salloc, rref< Fn >( std::forward< Fn >( fn) ), preserve_fpu);
}
template< typename T >
pull_coroutine< T & >::~pull_coroutine() {
if ( nullptr != cb_) {
cb_->~control_block();
}
}
template< typename T >
pull_coroutine< T & >::pull_coroutine( pull_coroutine && other) :
cb_( nullptr) {
std::swap( cb_, other.cb_);
}
template< typename T >
pull_coroutine< T & > &
pull_coroutine< T & >::operator()() {
cb_->jump_to();
return * this;
}
template< typename T >
pull_coroutine< T & >::operator bool() const noexcept {
return nullptr != cb_ && cb_->valid();
}
template< typename T >
bool
pull_coroutine< T & >::operator!() const noexcept {
return nullptr == cb_ || ! cb_->valid();
}
template< typename T >
T &
pull_coroutine< T & >::get() const noexcept {
return * cb_->other->t;
}
// pull_coroutine< void >
inline
pull_coroutine< void >::pull_coroutine( control_block * cb) :
cb_( cb) {
}
template< typename Fn >
pull_coroutine< void >::pull_coroutine( Fn && fn, bool preserve_fpu) :
pull_coroutine( fixedsize_stack(), std::forward< Fn >( fn), preserve_fpu) {
}
template< typename StackAllocator, typename Fn >
pull_coroutine< void >::pull_coroutine( StackAllocator salloc, Fn && fn, bool preserve_fpu) :
cb_( nullptr) {
context::stack_context sctx( salloc.allocate() );
void * sp = static_cast< char * >( sctx.sp) - sizeof( control_block);
std::size_t size = sctx.size - sizeof( control_block);
cb_ = new ( sp) control_block( context::preallocated( sp, size, sctx),
salloc, rref< Fn >( std::forward< Fn >( fn) ), preserve_fpu);
}
inline
pull_coroutine< void >::~pull_coroutine() {
if ( nullptr != cb_) {
cb_->~control_block();
}
}
inline
pull_coroutine< void >::pull_coroutine( pull_coroutine && other) :
cb_( nullptr) {
std::swap( cb_, other.cb_);
}
inline
pull_coroutine< void > &
pull_coroutine< void >::operator()() {
cb_->jump_to();
return * this;
}
inline
pull_coroutine< void >::operator bool() const noexcept {
return nullptr != cb_ && cb_->valid();
}
inline
bool
pull_coroutine< void >::operator!() const noexcept {
return nullptr == cb_ || ! cb_->valid();
}
}}}
#ifdef BOOST_HAS_ABI_HEADERS
# include BOOST_ABI_SUFFIX
#endif
#endif // BOOST_COROUTINES2_DETAIL_PULL_COROUTINE_IPP

View File

@ -0,0 +1,106 @@
// 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)
#ifndef BOOST_COROUTINES2_DETAIL_PUSH_CONTROL_BLOCK_HPP
#define BOOST_COROUTINES2_DETAIL_PUSH_CONTROL_BLOCK_HPP
#include <exception>
#include <boost/config.hpp>
#include <boost/context/execution_context.hpp>
#include <boost/coroutine2/detail/rref.hpp>
#ifdef BOOST_HAS_ABI_HEADERS
# include BOOST_ABI_PREFIX
#endif
namespace boost {
namespace coroutines2 {
namespace detail {
template< typename T >
struct push_coroutine< T >::control_block {
typename pull_coroutine< T >::control_block * other;
boost::context::execution_context caller;
boost::context::execution_context callee;
bool preserve_fpu;
int state;
std::exception_ptr except;
T * t;
template< typename StackAllocator, typename Fn >
control_block( context::preallocated, StackAllocator, rref< Fn >, bool);
explicit control_block( typename pull_coroutine< T >::control_block *);
~control_block();
control_block( control_block &) = delete;
control_block & operator=( control_block &) = delete;
void jump_to( T const&);
void jump_to( T &&);
bool valid() const noexcept;
};
template< typename T >
struct push_coroutine< T & >::control_block {
typename pull_coroutine< T & >::control_block * other;
boost::context::execution_context caller;
boost::context::execution_context callee;
bool preserve_fpu;
int state;
std::exception_ptr except;
T * t;
template< typename StackAllocator, typename Fn >
control_block( context::preallocated, StackAllocator, rref< Fn >, bool);
explicit control_block( typename pull_coroutine< T & >::control_block *);
~control_block();
control_block( control_block &) = delete;
control_block & operator=( control_block &) = delete;
void jump_to( T &);
bool valid() const noexcept;
};
struct push_coroutine< void >::control_block {
typename pull_coroutine< void >::control_block * other;
boost::context::execution_context caller;
boost::context::execution_context callee;
bool preserve_fpu;
int state;
std::exception_ptr except;
template< typename StackAllocator, typename Fn >
control_block( context::preallocated, StackAllocator, rref< Fn >, bool);
explicit control_block( typename pull_coroutine< void >::control_block *);
~control_block();
control_block( control_block &) = delete;
control_block & operator=( control_block &) = delete;
void jump_to();
bool valid() const noexcept;
};
}}}
#ifdef BOOST_HAS_ABI_HEADERS
# include BOOST_ABI_SUFFIX
#endif
#endif // BOOST_COROUTINES2_DETAIL_PUSH_CONTROL_BLOCK_HPP

View File

@ -0,0 +1,285 @@
// 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)
#ifndef BOOST_COROUTINES2_DETAIL_PUSH_CONTROL_BLOCK_IPP
#define BOOST_COROUTINES2_DETAIL_PUSH_CONTROL_BLOCK_IPP
#include <algorithm>
#include <exception>
#include <boost/assert.hpp>
#include <boost/config.hpp>
#include <boost/context/execution_context.hpp>
#include <boost/coroutine2/detail/config.hpp>
#include <boost/coroutine2/detail/forced_unwind.hpp>
#include <boost/coroutine2/detail/rref.hpp>
#include <boost/coroutine2/detail/state.hpp>
#ifdef BOOST_HAS_ABI_HEADERS
# include BOOST_ABI_PREFIX
#endif
namespace boost {
namespace coroutines2 {
namespace detail {
// push_coroutine< T >
template< typename T >
template< typename StackAllocator, typename Fn >
push_coroutine< T >::control_block::control_block( context::preallocated palloc, StackAllocator salloc,
rref< Fn > rr, bool preserve_fpu_) :
other( nullptr),
caller( boost::context::execution_context::current() ),
callee( palloc, salloc,
[=] () mutable {
try {
// create synthesized pull_coroutine< T >
typename pull_coroutine< T >::control_block synthesized_cb( this);
pull_coroutine< T > synthesized( & synthesized_cb);
other = & synthesized_cb;
// call coroutine-fn with synthesized pull_coroutine as argument
rr( synthesized);
} catch ( forced_unwind const&) {
// do nothing for unwinding exception
} catch (...) {
// store other exceptions in exception-pointer
except = std::current_exception();
}
// set termination flags
state |= static_cast< int >( state_t::complete);
// jump back to caller
caller.jump_to( preserve_fpu_);
BOOST_ASSERT_MSG( false, "push_coroutine is complete");
}),
preserve_fpu( preserve_fpu_),
state( static_cast< int >( state_t::unwind) ),
except(),
t( nullptr) {
}
template< typename T >
push_coroutine< T >::control_block::control_block( typename pull_coroutine< T >::control_block * cb) :
other( cb),
caller( other->callee),
callee( other->caller),
preserve_fpu( other->preserve_fpu),
state( 0),
except(),
t( nullptr) {
}
template< typename T >
push_coroutine< T >::control_block::~control_block() {
if ( 0 == ( state & static_cast< int >( state_t::complete ) ) &&
0 != ( state & static_cast< int >( state_t::unwind) ) ) {
// set early-exit flag
state |= static_cast< int >( state_t::early_exit);
callee.jump_to( preserve_fpu);
}
}
template< typename T >
void
push_coroutine< T >::control_block::jump_to( T const& t_) {
// store data on this stack
// pass an pointer (address of tmp) to other context
T tmp( t_);
t = & tmp;
callee.jump_to( preserve_fpu);
t = nullptr;
if ( except) {
std::rethrow_exception( except);
}
// test early-exit-flag
if ( 0 != ( ( other->state) & static_cast< int >( state_t::early_exit) ) ) {
throw forced_unwind();
}
}
template< typename T >
void
push_coroutine< T >::control_block::jump_to( T && t_) {
// store data on this stack
// pass an pointer (address of tmp) to other context
T tmp( std::move( t_) );
t = & tmp;
callee.jump_to( preserve_fpu);
t = nullptr;
if ( except) {
std::rethrow_exception( except);
}
// test early-exit-flag
if ( 0 != ( ( other->state) & static_cast< int >( state_t::early_exit) ) ) {
throw forced_unwind();
}
}
template< typename T >
bool
push_coroutine< T >::control_block::valid() const noexcept {
return 0 == ( state & static_cast< int >( state_t::complete) );
}
// push_coroutine< T & >
template< typename T >
template< typename StackAllocator, typename Fn >
push_coroutine< T & >::control_block::control_block( context::preallocated palloc, StackAllocator salloc,
rref< Fn > rr, bool preserve_fpu_) :
other( nullptr),
caller( boost::context::execution_context::current() ),
callee( palloc, salloc,
[=] () mutable {
try {
// create synthesized pull_coroutine< T >
typename pull_coroutine< T & >::control_block synthesized_cb( this);
pull_coroutine< T & > synthesized( & synthesized_cb);
other = & synthesized_cb;
// call coroutine-fn with synthesized pull_coroutine as argument
rr( synthesized);
} catch ( forced_unwind const&) {
// do nothing for unwinding exception
} catch (...) {
// store other exceptions in exception-pointer
except = std::current_exception();
}
// set termination flags
state |= static_cast< int >( state_t::complete);
// jump back to caller
caller.jump_to( preserve_fpu_);
BOOST_ASSERT_MSG( false, "push_coroutine is complete");
}),
preserve_fpu( preserve_fpu_),
state( static_cast< int >( state_t::unwind) ),
except(),
t( nullptr) {
}
template< typename T >
push_coroutine< T & >::control_block::control_block( typename pull_coroutine< T & >::control_block * cb) :
other( cb),
caller( other->callee),
callee( other->caller),
preserve_fpu( other->preserve_fpu),
state( 0),
except(),
t( nullptr) {
}
template< typename T >
push_coroutine< T & >::control_block::~control_block() {
if ( 0 == ( state & static_cast< int >( state_t::complete ) ) &&
0 != ( state & static_cast< int >( state_t::unwind) ) ) {
// set early-exit flag
state |= static_cast< int >( state_t::early_exit);
callee.jump_to( preserve_fpu);
}
}
template< typename T >
void
push_coroutine< T & >::control_block::jump_to( T & t_) {
t = & t_;
callee.jump_to( preserve_fpu);
t = nullptr;
if ( except) {
std::rethrow_exception( except);
}
// test early-exit-flag
if ( 0 != ( ( other->state) & static_cast< int >( state_t::early_exit) ) ) {
throw forced_unwind();
}
}
template< typename T >
bool
push_coroutine< T & >::control_block::valid() const noexcept {
return 0 == ( state & static_cast< int >( state_t::complete) );
}
// push_coroutine< void >
template< typename StackAllocator, typename Fn >
push_coroutine< void >::control_block::control_block( context::preallocated palloc, StackAllocator salloc, rref< Fn > rr, bool preserve_fpu_) :
other( nullptr),
caller( boost::context::execution_context::current() ),
callee( palloc, salloc,
[=] () mutable {
try {
// create synthesized pull_coroutine< T >
typename pull_coroutine< void >::control_block synthesized_cb( this);
pull_coroutine< void > synthesized( & synthesized_cb);
other = & synthesized_cb;
// call coroutine-fn with synthesized pull_coroutine as argument
rr( synthesized);
} catch ( forced_unwind const&) {
// do nothing for unwinding exception
} catch (...) {
// store other exceptions in exception-pointer
except = std::current_exception();
}
// set termination flags
state |= static_cast< int >( state_t::complete);
// jump back to caller
caller.jump_to( preserve_fpu_);
BOOST_ASSERT_MSG( false, "push_coroutine is complete");
}),
preserve_fpu( preserve_fpu_),
state( static_cast< int >( state_t::unwind) ),
except() {
}
inline
push_coroutine< void >::control_block::control_block( typename pull_coroutine< void >::control_block * cb) :
other( cb),
caller( other->callee),
callee( other->caller),
preserve_fpu( other->preserve_fpu),
state( 0),
except() {
}
inline
push_coroutine< void >::control_block::~control_block() {
if ( 0 == ( state & static_cast< int >( state_t::complete ) ) &&
0 != ( state & static_cast< int >( state_t::unwind) ) ) {
// set early-exit flag
state |= static_cast< int >( state_t::early_exit);
callee.jump_to( preserve_fpu);
}
}
inline
void
push_coroutine< void >::control_block::jump_to() {
callee.jump_to( preserve_fpu);
if ( except) {
std::rethrow_exception( except);
}
// test early-exit-flag
if ( 0 != ( ( other->state) & static_cast< int >( state_t::early_exit) ) ) {
throw forced_unwind();
}
}
inline
bool
push_coroutine< void >::control_block::valid() const noexcept {
return 0 == ( state & static_cast< int >( state_t::complete) );
}
}}}
#ifdef BOOST_HAS_ABI_HEADERS
# include BOOST_ABI_SUFFIX
#endif
#endif // BOOST_COROUTINES2_DETAIL_PUSH_CONTROL_BLOCK_IPP

View File

@ -0,0 +1,242 @@
// 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)
#ifndef BOOST_COROUTINES2_DETAIL_PUSH_COROUTINE_HPP
#define BOOST_COROUTINES2_DETAIL_PUSH_COROUTINE_HPP
#include <iterator>
#include <type_traits>
#include <boost/assert.hpp>
#include <boost/config.hpp>
#include <boost/context/execution_context.hpp>
#include <boost/coroutine2/detail/config.hpp>
#ifdef BOOST_HAS_ABI_HEADERS
# include BOOST_ABI_PREFIX
#endif
namespace boost {
namespace coroutines2 {
namespace detail {
template< typename T >
class push_coroutine {
private:
template< typename X >
friend class pull_coroutine;
struct control_block;
control_block * cb_;
explicit push_coroutine( control_block *);
public:
template< typename Fn >
explicit push_coroutine( Fn &&, bool = false);
template< typename StackAllocator, typename Fn >
explicit push_coroutine( StackAllocator, Fn &&, bool = false);
~push_coroutine();
push_coroutine( push_coroutine const&) = delete;
push_coroutine & operator=( push_coroutine const&) = delete;
push_coroutine( push_coroutine &&);
push_coroutine & operator=( push_coroutine && other) {
if ( this != & other) {
cb_ = other.cb_;
other.cb_ = nullptr;
}
return * this;
}
push_coroutine & operator()( T const&);
push_coroutine & operator()( T &&);
explicit operator bool() const noexcept;
bool operator!() const noexcept;
class iterator : public std::iterator< std::output_iterator_tag, void, void, void, void > {
private:
push_coroutine< T > * c_;
public:
iterator() :
c_( nullptr) {
}
explicit iterator( push_coroutine< T > * c) :
c_( c) {
}
iterator & operator=( T t) {
BOOST_ASSERT( c_);
if ( ! ( * c_)( t) ) c_ = 0;
return * this;
}
bool operator==( iterator const& other) const {
return other.c_ == c_;
}
bool operator!=( iterator const& other) const {
return other.c_ != c_;
}
iterator & operator*() {
return * this;
}
iterator & operator++() {
return * this;
}
};
};
template< typename T >
class push_coroutine< T & > {
private:
template< typename X >
friend class pull_coroutine;
struct control_block;
control_block * cb_;
explicit push_coroutine( control_block *);
public:
template< typename Fn >
explicit push_coroutine( Fn &&, bool = false);
template< typename StackAllocator, typename Fn >
explicit push_coroutine( StackAllocator, Fn &&, bool = false);
~push_coroutine();
push_coroutine( push_coroutine const&) = delete;
push_coroutine & operator=( push_coroutine const&) = delete;
push_coroutine( push_coroutine &&);
push_coroutine & operator=( push_coroutine && other) {
if ( this != & other) {
cb_ = other.cb_;
other.cb_ = nullptr;
}
return * this;
}
push_coroutine & operator()( T &);
explicit operator bool() const noexcept;
bool operator!() const noexcept;
class iterator : public std::iterator< std::output_iterator_tag, void, void, void, void > {
private:
push_coroutine< T & > * c_;
public:
iterator() :
c_( nullptr) {
}
explicit iterator( push_coroutine< T & > * c) :
c_( c) {
}
iterator & operator=( T & t) {
BOOST_ASSERT( c_);
if ( ! ( * c_)( t) ) c_ = 0;
return * this;
}
bool operator==( iterator const& other) const {
return other.c_ == c_;
}
bool operator!=( iterator const& other) const {
return other.c_ != c_;
}
iterator & operator*() {
return * this;
}
iterator & operator++() {
return * this;
}
};
};
template<>
class push_coroutine< void > {
private:
template< typename X >
friend class pull_coroutine;
struct control_block;
control_block * cb_;
explicit push_coroutine( control_block *);
public:
template< typename Fn >
explicit push_coroutine( Fn &&, bool = false);
template< typename StackAllocator, typename Fn >
explicit push_coroutine( StackAllocator, Fn &&, bool = false);
~push_coroutine();
push_coroutine( push_coroutine const&) = delete;
push_coroutine & operator=( push_coroutine const&) = delete;
push_coroutine( push_coroutine &&);
push_coroutine & operator=( push_coroutine && other) {
if ( this != & other) {
cb_ = other.cb_;
other.cb_ = nullptr;
}
return * this;
}
push_coroutine & operator()();
explicit operator bool() const noexcept;
bool operator!() const noexcept;
};
template< typename T >
typename push_coroutine< T >::iterator
begin( push_coroutine< T > & c) {
return typename push_coroutine< T >::iterator( & c);
}
template< typename T >
typename push_coroutine< T >::iterator
end( push_coroutine< T > &) {
return typename push_coroutine< T >::iterator();
}
}}}
#ifdef BOOST_HAS_ABI_HEADERS
# include BOOST_ABI_SUFFIX
#endif
#endif // BOOST_COROUTINES2_DETAIL_PUSH_COROUTINE_HPP

View File

@ -0,0 +1,208 @@
// 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)
#ifndef BOOST_COROUTINES2_DETAIL_PUSH_COROUTINE_IPP
#define BOOST_COROUTINES2_DETAIL_PUSH_COROUTINE_IPP
#include <utility>
#include <boost/assert.hpp>
#include <boost/config.hpp>
#include <boost/context/execution_context.hpp>
#include <boost/context/stack_context.hpp>
#include <boost/coroutine2/detail/config.hpp>
#include <boost/coroutine2/detail/rref.hpp>
#include <boost/coroutine2/fixedsize_stack.hpp>
#ifdef BOOST_HAS_ABI_HEADERS
# include BOOST_ABI_PREFIX
#endif
namespace boost {
namespace coroutines2 {
namespace detail {
// push_coroutine< T >
template< typename T >
push_coroutine< T >::push_coroutine( control_block * cb) :
cb_( cb) {
}
template< typename T >
template< typename Fn >
push_coroutine< T >::push_coroutine( Fn && fn, bool preserve_fpu) :
push_coroutine( fixedsize_stack(), std::forward< Fn >( fn), preserve_fpu) {
}
template< typename T >
template< typename StackAllocator, typename Fn >
push_coroutine< T >::push_coroutine( StackAllocator salloc, Fn && fn, bool preserve_fpu) :
cb_( nullptr) {
context::stack_context sctx( salloc.allocate() );
void * sp = static_cast< char * >( sctx.sp) - sizeof( control_block);
std::size_t size = sctx.size - sizeof( control_block);
cb_= new ( sp) control_block( context::preallocated( sp, size, sctx),
salloc, rref< Fn >( std::forward< Fn >( fn) ), preserve_fpu);
}
template< typename T >
push_coroutine< T >::~push_coroutine() {
if ( nullptr != cb_) {
cb_->~control_block();
}
}
template< typename T >
push_coroutine< T >::push_coroutine( push_coroutine && other) :
cb_( nullptr) {
std::swap( cb_, other.cb_);
}
template< typename T >
push_coroutine< T > &
push_coroutine< T >::operator()( T const& t) {
cb_->jump_to( t);
return * this;
}
template< typename T >
push_coroutine< T > &
push_coroutine< T >::operator()( T && t) {
cb_->jump_to( std::forward< T >( t) );
return * this;
}
template< typename T >
push_coroutine< T >::operator bool() const noexcept {
return nullptr != cb_ && cb_->valid();
}
template< typename T >
bool
push_coroutine< T >::operator!() const noexcept {
return nullptr == cb_ || ! cb_->valid();
}
// push_coroutine< T & >
template< typename T >
push_coroutine< T & >::push_coroutine( control_block * cb) :
cb_( cb) {
}
template< typename T >
template< typename Fn >
push_coroutine< T & >::push_coroutine( Fn && fn, bool preserve_fpu) :
push_coroutine( fixedsize_stack(), std::forward< Fn >( fn), preserve_fpu) {
}
template< typename T >
template< typename StackAllocator, typename Fn >
push_coroutine< T & >::push_coroutine( StackAllocator salloc, Fn && fn, bool preserve_fpu) :
cb_( nullptr) {
context::stack_context sctx( salloc.allocate() );
void * sp = static_cast< char * >( sctx.sp) - sizeof( control_block);
std::size_t size = sctx.size - sizeof( control_block);
cb_ = new ( sp) control_block( context::preallocated( sp, size, sctx),
salloc, rref< Fn >( std::forward< Fn >( fn) ), preserve_fpu);
}
template< typename T >
push_coroutine< T & >::~push_coroutine() {
if ( nullptr != cb_) {
cb_->~control_block();
}
}
template< typename T >
push_coroutine< T & >::push_coroutine( push_coroutine && other) :
cb_( nullptr) {
std::swap( cb_, other.cb_);
}
template< typename T >
push_coroutine< T & > &
push_coroutine< T & >::operator()( T & t) {
cb_->jump_to( t);
return * this;
}
template< typename T >
push_coroutine< T & >::operator bool() const noexcept {
return nullptr != cb_ && cb_->valid();
}
template< typename T >
bool
push_coroutine< T & >::operator!() const noexcept {
return nullptr == cb_ || ! cb_->valid();
}
// push_coroutine< void >
inline
push_coroutine< void >::push_coroutine( control_block * cb) :
cb_( cb) {
}
template< typename Fn >
push_coroutine< void >::push_coroutine( Fn && fn, bool preserve_fpu) :
push_coroutine( fixedsize_stack(), std::forward< Fn >( fn), preserve_fpu) {
}
template< typename StackAllocator, typename Fn >
push_coroutine< void >::push_coroutine( StackAllocator salloc, Fn && fn, bool preserve_fpu) :
cb_( nullptr) {
context::stack_context sctx( salloc.allocate() );
void * sp = static_cast< char * >( sctx.sp) - sizeof( control_block);
std::size_t size = sctx.size - sizeof( control_block);
cb_ = new ( sp) control_block( context::preallocated( sp, size, sctx),
salloc, rref< Fn >( std::forward< Fn >( fn) ), preserve_fpu);
}
inline
push_coroutine< void >::~push_coroutine() {
if ( nullptr != cb_) {
cb_->~control_block();
}
}
inline
push_coroutine< void >::push_coroutine( push_coroutine && other) :
cb_( nullptr) {
std::swap( cb_, other.cb_);
}
inline
push_coroutine< void > &
push_coroutine< void >::operator()() {
cb_->jump_to();
return * this;
}
inline
push_coroutine< void >::operator bool() const noexcept {
return nullptr != cb_ && cb_->valid();
}
inline
bool
push_coroutine< void >::operator!() const noexcept {
return nullptr == cb_ || ! cb_->valid();
}
}}}
#ifdef BOOST_HAS_ABI_HEADERS
# include BOOST_ABI_SUFFIX
#endif
#endif // BOOST_COROUTINES2_DETAIL_PUSH_COROUTINE_IPP

View File

@ -0,0 +1,57 @@
// Copyright Oliver Kowalke 2013.
// 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)
#ifndef BOOST_COROUTINES2_DETAIL_RREF_H
#define BOOST_COROUTINES2_DETAIL_RREF_H
#include <boost/config.hpp>
#include <boost/coroutine2/detail/config.hpp>
#ifdef BOOST_HAS_ABI_HEADERS
# include BOOST_ABI_PREFIX
#endif
namespace boost {
namespace coroutines2 {
namespace detail {
// helper class for capture move-only objects
// generalized lambda captures are supported by C++14
template< typename Fn >
class rref {
public:
rref( Fn && fn) :
fn_( std::forward< Fn >( fn) ) {
}
rref( rref & other) :
fn_( std::forward< Fn >( other.fn_) ) {
}
rref( rref && other) :
fn_( std::forward< Fn >( other.fn_) ) {
}
rref( rref const& other) = delete;
rref & operator=( rref const& other) = delete;
template< typename S >
void operator()( S & s) {
return fn_( s);
}
private:
Fn fn_;
};
}}}
#ifdef BOOST_HAS_ABI_HEADERS
# include BOOST_ABI_SUFFIX
#endif
#endif // BOOST_COROUTINES2_DETAIL_RREF_H

View File

@ -0,0 +1,37 @@
// 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)
#ifndef BOOST_COROUTINES2_DETAIL_ASYMMETRIC_COROUTINE_HPP
#define BOOST_COROUTINES2_DETAIL_ASYMMETRIC_COROUTINE_HPP
#include <exception>
#include <boost/assert.hpp>
#include <boost/config.hpp>
#include <boost/coroutine2/detail/config.hpp>
#ifdef BOOST_HAS_ABI_HEADERS
# include BOOST_ABI_PREFIX
#endif
namespace boost {
namespace coroutines2 {
namespace detail {
enum class state_t : unsigned int {
complete = 1 << 1,
unwind = 1 << 2,
early_exit = 1 << 3
};
}}}
#ifdef BOOST_HAS_ABI_HEADERS
# include BOOST_ABI_SUFFIX
#endif
#endif // BOOST_COROUTINES2_DETAIL_ASYMMETRIC_COROUTINE_HPP

View File

@ -0,0 +1,33 @@
// 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)
#ifndef BOOST_COROUTINES2_FIXEDSIZE_H
#define BOOST_COROUTINES2_FIXEDSIZE_H
#include <exception>
#include <boost/assert.hpp>
#include <boost/config.hpp>
#include <boost/context/fixedsize_stack.hpp>
#include <boost/coroutine2/detail/coroutine.hpp>
#ifdef BOOST_HAS_ABI_HEADERS
# include BOOST_ABI_PREFIX
#endif
namespace boost {
namespace coroutines2 {
typedef boost::context::fixedsize_stack fixedsize_stack;
}}
#ifdef BOOST_HAS_ABI_HEADERS
# include BOOST_ABI_SUFFIX
#endif
#endif // BOOST_COROUTINES2_FIXEDSIZE_H

View File

@ -0,0 +1,33 @@
// 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)
#ifndef BOOST_COROUTINES2_PROTECTED_FIXEDSIZE_H
#define BOOST_COROUTINES2_PROTECTED_FIXEDSIZE_H
#include <exception>
#include <boost/assert.hpp>
#include <boost/config.hpp>
#include <boost/context/protected_fixedsize_stack.hpp>
#include <boost/coroutine2/detail/coroutine.hpp>
#ifdef BOOST_HAS_ABI_HEADERS
# include BOOST_ABI_PREFIX
#endif
namespace boost {
namespace coroutines2 {
typedef boost::context::protected_fixedsize_stack protected_fixedsize_stack;
}}
#ifdef BOOST_HAS_ABI_HEADERS
# include BOOST_ABI_SUFFIX
#endif
#endif // BOOST_COROUTINES2_PROTECTED_FIXEDSIZE_H

View File

@ -0,0 +1,37 @@
// 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)
#ifndef BOOST_COROUTINES2_SEGMENTED_H
#define BOOST_COROUTINES2_SEGMENTED_H
#include <exception>
#include <boost/assert.hpp>
#include <boost/config.hpp>
#include <boost/context/segmented_stack.hpp>
#include <boost/coroutine2/detail/coroutine.hpp>
#ifdef BOOST_HAS_ABI_HEADERS
# include BOOST_ABI_PREFIX
#endif
namespace boost {
namespace coroutines2 {
#if defined(BOOST_USE_SEGMENTED_STACKS)
# if ! defined(BOOST_WINDOWS)
typedef boost::context::segmented_stack segmented_stack;
# endif
#endif
}}
#ifdef BOOST_HAS_ABI_HEADERS
# include BOOST_ABI_SUFFIX
#endif
#endif // BOOST_COROUTINES2_SEGMENTED_H

14
index.html Normal file
View File

@ -0,0 +1,14 @@
<html>
<head>
<meta http-equiv="refresh" content="0; URL=doc/html/index.html">
</head>
<body>
Automatic redirection failed, please go to
<a href="doc/html/index.html">doc/html/index.html</a>
<hr>
<p>&copy; Copyright Beman Dawes, 2001</p>
<p> Distributed under the Boost Software
License, Version 1.0. (See accompanying file <a href="../../LICENSE_1_0.txt">LICENSE_1_0.txt</a> or copy at <a href="http://www.boost.org/LICENSE_1_0.txt">
www.boost.org/LICENSE_1_0.txt</a>)</p>
</body>
</html>

14
meta/libraries.json Normal file
View File

@ -0,0 +1,14 @@
{
"key": "coroutine",
"name": "Coroutine",
"authors": [
"Oliver Kowalke"
],
"description": "Coroutine library.",
"category": [
"Concurrent"
],
"maintainers": [
"Oliver Kowalke <oliver.kowalke -at- gmail.com>"
]
}

77
performance/Jamfile.v2 Normal file
View File

@ -0,0 +1,77 @@
# 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)
# For more information, see http://www.boost.org/
import common ;
import feature ;
import indirect ;
import modules ;
import os ;
import toolset ;
project boost/coroutine2/performance
: requirements
<library>/boost/chrono//boost_chrono
<library>/boost/context//boost_context
<library>/boost/program_options//boost_program_options
<toolset>gcc,<segmented-stacks>on:<cxxflags>-fsplit-stack
<toolset>gcc,<segmented-stacks>on:<cxxflags>-DBOOST_USE_SEGMENTED_STACKS
<toolset>clang,<segmented-stacks>on:<cxxflags>-fsplit-stack
<toolset>clang,<segmented-stacks>on:<cxxflags>-DBOOST_USE_SEGMENTED_STACKS
<link>static
<threading>multi
<cxxflags>-DBOOST_DISABLE_ASSERTS
<optimization>speed
<variant>release
;
alias sources
: bind_processor_aix.cpp
: <target-os>aix
;
alias sources
: bind_processor_freebsd.cpp
: <target-os>freebsd
;
alias sources
: bind_processor_hpux.cpp
: <target-os>hpux
;
alias sources
: bind_processor_linux.cpp
: <target-os>linux
;
alias sources
: bind_processor_solaris.cpp
: <target-os>solaris
;
alias sources
: bind_processor_windows.cpp
: <target-os>windows
;
explicit sources ;
exe performance_create_protected
: sources
performance_create_protected.cpp
;
exe performance_create_standard
: sources
performance_create_standard.cpp
;
exe performance_switch
: sources
performance_switch.cpp
;

View File

@ -0,0 +1,12 @@
// 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)
#ifndef BIND_TO_PROCESSOR_H
#define BIND_TO_PROCESSOR_H
void bind_to_processor( unsigned int n);
#endif // BIND_TO_PROCESSOR_H

View File

@ -0,0 +1,25 @@
// 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)
#include "bind_processor.hpp"
extern "C"
{
#include <sys/processor.h>
#include <sys/thread.h>
}
#include <stdexcept>
#include <boost/config/abi_prefix.hpp>
void bind_to_processor( unsigned int n)
{
if ( ::bindprocessor( BINDTHREAD, ::thread_yield(), static_cast< cpu_t >( n) ) == -1)
throw std::runtime_error("::bindprocessor() failed");
}
#include <boost/config/abi_suffix.hpp>

View File

@ -0,0 +1,29 @@
// 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)
#include "bind_processor.hpp"
extern "C"
{
#include <sys/param.h>
#include <sys/cpuset.h>
}
#include <stdexcept>
#include <boost/config/abi_prefix.hpp>
void bind_to_processor( unsigned int n)
{
cpuset_t cpuset;
CPU_ZERO( & cpuset);
CPU_SET( n, & cpuset);
if ( ::cpuset_setaffinity( CPU_LEVEL_WHICH, CPU_WHICH_TID, -1, sizeof( cpuset), & cpuset) == -1)
throw std::runtime_error("::cpuset_setaffinity() failed");
}
#include <boost/config/abi_suffix.hpp>

View File

@ -0,0 +1,31 @@
// 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)
#include "bind_processor.hpp"
extern "C"
{
#include <sys/pthread.h>
}
#include <stdexcept>
#include <boost/config/abi_prefix.hpp>
void bind_to_processor( unsigned int n)
{
::pthread_spu_t spu;
int errno_(
::pthread_processor_bind_np(
PTHREAD_BIND_FORCED_NP,
& spu,
static_cast< pthread_spu_t >( n),
PTHREAD_SELFTID_NP) );
if ( errno_ != 0)
throw std::runtime_error("::pthread_processor_bind_np() failed");
}
#include <boost/config/abi_suffix.hpp>

View File

@ -0,0 +1,30 @@
// 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)
#include "bind_processor.hpp"
extern "C"
{
#include <pthread.h>
#include <sched.h>
}
#include <stdexcept>
#include <boost/config/abi_prefix.hpp>
void bind_to_processor( unsigned int n)
{
cpu_set_t cpuset;
CPU_ZERO( & cpuset);
CPU_SET( n, & cpuset);
int errno_( ::pthread_setaffinity_np( ::pthread_self(), sizeof( cpuset), & cpuset) );
if ( errno_ != 0)
throw std::runtime_error("::pthread_setaffinity_np() failed");
}
#include <boost/config/abi_suffix.hpp>

View File

@ -0,0 +1,26 @@
// 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)
#include "bind_processor.hpp"
extern "C"
{
#include <sys/types.h>
#include <sys/processor.h>
#include <sys/procset.h>
}
#include <stdexcept>
#include <boost/config/abi_prefix.hpp>
void bind_to_processor( unsigned int n)
{
if ( ::processor_bind( P_LWPID, P_MYID, static_cast< processorid_t >( n), 0) == -1)
throw std::runtime_error("::processor_bind() failed");
}
#include <boost/config/abi_suffix.hpp>

View File

@ -0,0 +1,24 @@
// 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)
#include "bind_processor.hpp"
extern "C"
{
#include <windows.h>
}
#include <stdexcept>
#include <boost/config/abi_prefix.hpp>
void bind_to_processor( unsigned int n)
{
if ( ::SetThreadAffinityMask( ::GetCurrentThread(), ( DWORD_PTR)1 << n) == 0)
throw std::runtime_error("::SetThreadAffinityMask() failed");
}
#include <boost/config/abi_suffix.hpp>

45
performance/clock.hpp Normal file
View File

@ -0,0 +1,45 @@
// 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)
//
#ifndef CLOCK_H
#define CLOCK_H
#include <algorithm>
#include <cstddef>
#include <numeric>
#include <vector>
#include <boost/assert.hpp>
#include <boost/chrono.hpp>
#include <boost/cstdint.hpp>
typedef boost::chrono::high_resolution_clock clock_type;
typedef clock_type::duration duration_type;
typedef clock_type::time_point time_point_type;
struct clock_overhead
{
boost::uint64_t operator()()
{
time_point_type start( clock_type::now() );
return ( clock_type::now() - start).count();
}
};
inline
duration_type overhead_clock()
{
std::size_t iterations( 10);
std::vector< boost::uint64_t > overhead( iterations, 0);
for ( std::size_t i = 0; i < iterations; ++i)
std::generate(
overhead.begin(), overhead.end(),
clock_overhead() );
BOOST_ASSERT( overhead.begin() != overhead.end() );
return duration_type( std::accumulate( overhead.begin(), overhead.end(), 0) / iterations);
}
#endif // CLOCK_H

26
performance/cycle.hpp Normal file
View File

@ -0,0 +1,26 @@
// 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)
#ifndef CYCLE_H
#define CYCLE_H
// x86_64
// test x86_64 before i386 because icc might
// define __i686__ for x86_64 too
#if defined(__x86_64__) || defined(__x86_64) \
|| defined(__amd64__) || defined(__amd64) \
|| defined(_M_X64) || defined(_M_AMD64)
# include "cycle_x86-64.hpp"
// i386
#elif defined(i386) || defined(__i386__) || defined(__i386) \
|| defined(__i486__) || defined(__i586__) || defined(__i686__) \
|| defined(__X86__) || defined(_X86_) || defined(__THW_INTEL__) \
|| defined(__I86__) || defined(__INTEL__) || defined(__IA32__) \
|| defined(_M_IX86) || defined(_I86_)
# include "cycle_i386.hpp"
#endif
#endif // CYCLE_H

View File

@ -0,0 +1,83 @@
// 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)
#ifndef CYCLE_I386_H
#define CYCLE_I386_H
#include <algorithm>
#include <numeric>
#include <cstddef>
#include <vector>
#include <boost/assert.hpp>
#include <boost/bind.hpp>
#include <boost/cstdint.hpp>
#define BOOST_CONTEXT_CYCLE
typedef boost::uint64_t cycle_type;
#if _MSC_VER
inline
cycle_type cycles()
{
cycle_type c;
__asm {
cpuid
rdtsc
mov dword ptr [c + 0], eax
mov dword ptr [c + 4], edx
}
return c;
}
#elif defined(__GNUC__) || \
defined(__INTEL_COMPILER) || defined(__ICC) || defined(_ECC) || defined(__ICL)
inline
cycle_type cycles()
{
boost::uint32_t lo, hi;
__asm__ __volatile__ (
"xorl %%eax, %%eax\n"
"cpuid\n"
::: "%eax", "%ebx", "%ecx", "%edx"
);
__asm__ __volatile__ ("rdtsc" : "=a" (lo), "=d" (hi) );
__asm__ __volatile__ (
"xorl %%eax, %%eax\n"
"cpuid\n"
::: "%eax", "%ebx", "%ecx", "%edx"
);
return ( cycle_type)hi << 32 | lo;
}
#else
# error "this compiler is not supported"
#endif
struct cycle_overhead
{
cycle_type operator()()
{
cycle_type start( cycles() );
return cycles() - start;
}
};
inline
cycle_type overhead_cycle()
{
std::size_t iterations( 10);
std::vector< cycle_type > overhead( iterations, 0);
for ( std::size_t i( 0); i < iterations; ++i)
std::generate(
overhead.begin(), overhead.end(),
cycle_overhead() );
BOOST_ASSERT( overhead.begin() != overhead.end() );
return std::accumulate( overhead.begin(), overhead.end(), 0) / iterations;
}
#endif // CYCLE_I386_H

View File

@ -0,0 +1,79 @@
// 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)
#ifndef CYCLE_X86_64_H
#define CYCLE_X86_64_H
#include <algorithm>
#include <numeric>
#include <cstddef>
#include <vector>
#include <boost/assert.hpp>
#include <boost/bind.hpp>
#include <boost/cstdint.hpp>
#define BOOST_CONTEXT_CYCLE
typedef boost::uint64_t cycle_type;
#if _MSC_VER >= 1400
# include <intrin.h>
# pragma intrinsic(__rdtsc)
inline
cycle_type cycles()
{ return __rdtsc(); }
#elif defined(__INTEL_COMPILER) || defined(__ICC) || defined(_ECC) || defined(__ICL)
inline
cycle_type cycles()
{ return __rdtsc(); }
#elif defined(__GNUC__) || defined(__SUNPRO_C)
inline
cycle_type cycles()
{
boost::uint32_t lo, hi;
__asm__ __volatile__ (
"xorl %%eax, %%eax\n"
"cpuid\n"
::: "%rax", "%rbx", "%rcx", "%rdx"
);
__asm__ __volatile__ ("rdtsc" : "=a" (lo), "=d" (hi) );
__asm__ __volatile__ (
"xorl %%eax, %%eax\n"
"cpuid\n"
::: "%rax", "%rbx", "%rcx", "%rdx"
);
return ( cycle_type)hi << 32 | lo;
}
#else
# error "this compiler is not supported"
#endif
struct cycle_overhead
{
cycle_type operator()()
{
cycle_type start( cycles() );
return cycles() - start;
}
};
inline
cycle_type overhead_cycle()
{
std::size_t iterations( 10);
std::vector< cycle_type > overhead( iterations, 0);
for ( std::size_t i( 0); i < iterations; ++i)
std::generate(
overhead.begin(), overhead.end(),
cycle_overhead() );
BOOST_ASSERT( overhead.begin() != overhead.end() );
return std::accumulate( overhead.begin(), overhead.end(), 0) / iterations;
}
#endif // CYCLE_X86_64_H

View File

@ -0,0 +1,107 @@
// 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)
#include <cstdlib>
#include <iostream>
#include <stdexcept>
#include <boost/chrono.hpp>
#include <boost/coroutine2/all.hpp>
#include <boost/cstdint.hpp>
#include <boost/program_options.hpp>
#include "bind_processor.hpp"
#include "clock.hpp"
#include "cycle.hpp"
typedef boost::coroutines2::protected_fixedsize_stack stack_allocator;
typedef boost::coroutines2::coroutine< void > coro_type;
bool preserve = false;
boost::uint64_t jobs = 1000;
void fn( coro_type::pull_type & c)
{ while ( true) c(); }
duration_type measure_time( duration_type overhead)
{
stack_allocator stack_alloc;
time_point_type start( clock_type::now() );
for ( std::size_t i = 0; i < jobs; ++i) {
coro_type::push_type c( stack_alloc, fn, preserve);
}
duration_type total = clock_type::now() - start;
total -= overhead_clock(); // overhead of measurement
total /= jobs; // loops
return total;
}
# ifdef BOOST_CONTEXT_CYCLE
cycle_type measure_cycles( cycle_type overhead)
{
stack_allocator stack_alloc;
cycle_type start( cycles() );
for ( std::size_t i = 0; i < jobs; ++i) {
coro_type::push_type c( stack_alloc, fn, preserve);
}
cycle_type total = cycles() - start;
total -= overhead; // overhead of measurement
total /= jobs; // loops
return total;
}
# endif
int main( int argc, char * argv[])
{
try
{
bool bind = false;
boost::program_options::options_description desc("allowed options");
desc.add_options()
("help", "help message")
("bind,b", boost::program_options::value< bool >( & bind), "bind thread to CPU")
("fpu,f", boost::program_options::value< bool >( & preserve), "preserve FPU registers")
("jobs,j", boost::program_options::value< boost::uint64_t >( & jobs), "jobs to run");
boost::program_options::variables_map vm;
boost::program_options::store(
boost::program_options::parse_command_line(
argc,
argv,
desc),
vm);
boost::program_options::notify( vm);
if ( vm.count("help") ) {
std::cout << desc << std::endl;
return EXIT_SUCCESS;
}
if ( bind) bind_to_processor( 0);
duration_type overhead_c = overhead_clock();
std::cout << "overhead " << overhead_c.count() << " nano seconds" << std::endl;
boost::uint64_t res = measure_time( overhead_c).count();
std::cout << "average of " << res << " nano seconds" << std::endl;
#ifdef BOOST_CONTEXT_CYCLE
cycle_type overhead_y = overhead_cycle();
std::cout << "overhead " << overhead_y << " cpu cycles" << std::endl;
res = measure_cycles( overhead_y);
std::cout << "average of " << res << " cpu cycles" << std::endl;
#endif
return EXIT_SUCCESS;
}
catch ( std::exception const& e)
{ std::cerr << "exception: " << e.what() << std::endl; }
catch (...)
{ std::cerr << "unhandled exception" << std::endl; }
return EXIT_FAILURE;
}

View File

@ -0,0 +1,107 @@
// 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)
#include <cstdlib>
#include <iostream>
#include <stdexcept>
#include <boost/chrono.hpp>
#include <boost/coroutine2/all.hpp>
#include <boost/cstdint.hpp>
#include <boost/program_options.hpp>
#include "bind_processor.hpp"
#include "clock.hpp"
#include "cycle.hpp"
typedef boost::coroutines2::fixedsize_stack stack_allocator;
typedef boost::coroutines2::coroutine< void > coro_type;
bool preserve = false;
boost::uint64_t jobs = 1000;
void fn( coro_type::pull_type & c)
{ while ( true) c(); }
duration_type measure_time( duration_type overhead)
{
stack_allocator stack_alloc;
time_point_type start( clock_type::now() );
for ( std::size_t i = 0; i < jobs; ++i) {
coro_type::push_type c( stack_alloc, fn, preserve);
}
duration_type total = clock_type::now() - start;
total -= overhead_clock(); // overhead of measurement
total /= jobs; // loops
return total;
}
# ifdef BOOST_CONTEXT_CYCLE
cycle_type measure_cycles( cycle_type overhead)
{
stack_allocator stack_alloc;
cycle_type start( cycles() );
for ( std::size_t i = 0; i < jobs; ++i) {
coro_type::push_type c( stack_alloc, fn, preserve);
}
cycle_type total = cycles() - start;
total -= overhead; // overhead of measurement
total /= jobs; // loops
return total;
}
# endif
int main( int argc, char * argv[])
{
try
{
bool bind = false;
boost::program_options::options_description desc("allowed options");
desc.add_options()
("help", "help message")
("bind,b", boost::program_options::value< bool >( & bind), "bind thread to CPU")
("fpu,f", boost::program_options::value< bool >( & preserve), "preserve FPU registers")
("jobs,j", boost::program_options::value< boost::uint64_t >( & jobs), "jobs to run");
boost::program_options::variables_map vm;
boost::program_options::store(
boost::program_options::parse_command_line(
argc,
argv,
desc),
vm);
boost::program_options::notify( vm);
if ( vm.count("help") ) {
std::cout << desc << std::endl;
return EXIT_SUCCESS;
}
if ( bind) bind_to_processor( 0);
duration_type overhead_c = overhead_clock();
std::cout << "overhead " << overhead_c.count() << " nano seconds" << std::endl;
boost::uint64_t res = measure_time( overhead_c).count();
std::cout << "average of " << res << " nano seconds" << std::endl;
#ifdef BOOST_CONTEXT_CYCLE
cycle_type overhead_y = overhead_cycle();
std::cout << "overhead " << overhead_y << " cpu cycles" << std::endl;
res = measure_cycles( overhead_y);
std::cout << "average of " << res << " cpu cycles" << std::endl;
#endif
return EXIT_SUCCESS;
}
catch ( std::exception const& e)
{ std::cerr << "exception: " << e.what() << std::endl; }
catch (...)
{ std::cerr << "unhandled exception" << std::endl; }
return EXIT_FAILURE;
}

View File

@ -0,0 +1,198 @@
// 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)
#include <cstdlib>
#include <iostream>
#include <stdexcept>
#include <string>
#include <boost/chrono.hpp>
#include <boost/coroutine2/all.hpp>
#include <boost/cstdint.hpp>
#include <boost/program_options.hpp>
#include "bind_processor.hpp"
#include "clock.hpp"
#include "cycle.hpp"
bool preserve = false;
boost::uint64_t jobs = 1000;
struct X
{
std::string str;
X( std::string const& str_) :
str( str_)
{}
};
const X x("abc");
void fn_void( boost::coroutines2::coroutine< void >::push_type & c)
{ while ( true) c(); }
void fn_int( boost::coroutines2::coroutine< int >::push_type & c)
{ while ( true) c( 7); }
void fn_x( boost::coroutines2::coroutine< X >::push_type & c)
{
while ( true) c( x);
}
duration_type measure_time_void( duration_type overhead)
{
boost::coroutines2::coroutine< void >::pull_type c( fn_void, preserve);
time_point_type start( clock_type::now() );
for ( std::size_t i = 0; i < jobs; ++i) {
c();
}
duration_type total = clock_type::now() - start;
total -= overhead_clock(); // overhead of measurement
total /= jobs; // loops
total /= 2; // 2x jump_fcontext
return total;
}
duration_type measure_time_int( duration_type overhead)
{
boost::coroutines2::coroutine< int >::pull_type c( fn_int, preserve);
time_point_type start( clock_type::now() );
for ( std::size_t i = 0; i < jobs; ++i) {
c();
}
duration_type total = clock_type::now() - start;
total -= overhead_clock(); // overhead of measurement
total /= jobs; // loops
total /= 2; // 2x jump_fcontext
return total;
}
duration_type measure_time_x( duration_type overhead)
{
boost::coroutines2::coroutine< X >::pull_type c( fn_x, preserve);
time_point_type start( clock_type::now() );
for ( std::size_t i = 0; i < jobs; ++i) {
c();
}
duration_type total = clock_type::now() - start;
total -= overhead_clock(); // overhead of measurement
total /= jobs; // loops
total /= 2; // 2x jump_fcontext
return total;
}
# ifdef BOOST_CONTEXT_CYCLE
cycle_type measure_cycles_void( cycle_type overhead)
{
boost::coroutines2::coroutine< void >::pull_type c( fn_void, preserve);
cycle_type start( cycles() );
for ( std::size_t i = 0; i < jobs; ++i) {
c();
}
cycle_type total = cycles() - start;
total -= overhead; // overhead of measurement
total /= jobs; // loops
total /= 2; // 2x jump_fcontext
return total;
}
cycle_type measure_cycles_int( cycle_type overhead)
{
boost::coroutines2::coroutine< int >::pull_type c( fn_int, preserve);
cycle_type start( cycles() );
for ( std::size_t i = 0; i < jobs; ++i) {
c();
}
cycle_type total = cycles() - start;
total -= overhead; // overhead of measurement
total /= jobs; // loops
total /= 2; // 2x jump_fcontext
return total;
}
cycle_type measure_cycles_x( cycle_type overhead)
{
boost::coroutines2::coroutine< X >::pull_type c( fn_x, preserve);
cycle_type start( cycles() );
for ( std::size_t i = 0; i < jobs; ++i) {
c();
}
cycle_type total = cycles() - start;
total -= overhead; // overhead of measurement
total /= jobs; // loops
total /= 2; // 2x jump_fcontext
return total;
}
# endif
int main( int argc, char * argv[])
{
try
{
bool bind = false;
boost::program_options::options_description desc("allowed options");
desc.add_options()
("help", "help message")
("bind,b", boost::program_options::value< bool >( & bind), "bind thread to CPU")
("fpu,f", boost::program_options::value< bool >( & preserve), "preserve FPU registers")
("jobs,j", boost::program_options::value< boost::uint64_t >( & jobs), "jobs to run");
boost::program_options::variables_map vm;
boost::program_options::store(
boost::program_options::parse_command_line(
argc,
argv,
desc),
vm);
boost::program_options::notify( vm);
if ( vm.count("help") ) {
std::cout << desc << std::endl;
return EXIT_SUCCESS;
}
if ( bind) bind_to_processor( 0);
duration_type overhead_c = overhead_clock();
std::cout << "overhead " << overhead_c.count() << " nano seconds" << std::endl;
boost::uint64_t res = measure_time_void( overhead_c).count();
std::cout << "void: average of " << res << " nano seconds" << std::endl;
res = measure_time_int( overhead_c).count();
std::cout << "int: average of " << res << " nano seconds" << std::endl;
res = measure_time_x( overhead_c).count();
std::cout << "X: average of " << res << " nano seconds" << std::endl;
#ifdef BOOST_CONTEXT_CYCLE
cycle_type overhead_y = overhead_cycle();
std::cout << "overhead " << overhead_y << " cpu cycles" << std::endl;
res = measure_cycles_void( overhead_y);
std::cout << "void: average of " << res << " cpu cycles" << std::endl;
res = measure_cycles_int( overhead_y);
std::cout << "int: average of " << res << " cpu cycles" << std::endl;
res = measure_cycles_x( overhead_y);
std::cout << "X: average of " << res << " cpu cycles" << std::endl;
#endif
return EXIT_SUCCESS;
}
catch ( std::exception const& e)
{ std::cerr << "exception: " << e.what() << std::endl; }
catch (...)
{ std::cerr << "unhandled exception" << std::endl; }
return EXIT_FAILURE;
}

View File

@ -0,0 +1,73 @@
# 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)
# For more information, see http://www.boost.org/
import common ;
import feature ;
import indirect ;
import modules ;
import os ;
import toolset ;
project boost/coroutine2/performance/segmented_stack
: requirements
<library>/boost/chrono//boost_chrono
<library>/boost/coroutine//boost_coroutine
<library>/boost/program_options//boost_program_options
<segmented-stacks>on
<toolset>gcc,<segmented-stacks>on:<cxxflags>-fsplit-stack
<toolset>gcc,<segmented-stacks>on:<cxxflags>-DBOOST_USE_SEGMENTED_STACKS
<toolset>clang,<segmented-stacks>on:<cxxflags>-fsplit-stack
<toolset>clang,<segmented-stacks>on:<cxxflags>-DBOOST_USE_SEGMENTED_STACKS
<link>static
<threading>multi
<cxxflags>-DBOOST_DISABLE_ASSERTS
<optimization>speed
<variant>release
;
alias sources
: ../bind_processor_aix.cpp
: <target-os>aix
;
alias sources
: ../bind_processor_freebsd.cpp
: <target-os>freebsd
;
alias sources
: ../bind_processor_hpux.cpp
: <target-os>hpux
;
alias sources
: ../bind_processor_linux.cpp
: <target-os>linux
;
alias sources
: ../bind_processor_solaris.cpp
: <target-os>solaris
;
alias sources
: ../bind_processor_windows.cpp
: <target-os>windows
;
explicit sources ;
exe performance_create_segmented
: sources
performance_create_segmented.cpp
;
exe performance_switch
: sources
performance_switch.cpp
;

View File

@ -0,0 +1,107 @@
// 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)
#include <cstdlib>
#include <iostream>
#include <stdexcept>
#include <boost/chrono.hpp>
#include <boost/coroutine2/all.hpp>
#include <boost/cstdint.hpp>
#include <boost/program_options.hpp>
#include "../bind_processor.hpp"
#include "../clock.hpp"
#include "../cycle.hpp"
typedef boost::coroutines2::segmented_stack stack_allocator;
typedef boost::coroutines2::coroutine< void > coro_type;
bool preserve = false;
boost::uint64_t jobs = 1000;
void fn( coro_type::push_type & c)
{ while ( true) c(); }
duration_type measure_time( duration_type overhead)
{
stack_allocator stack_alloc;
time_point_type start( clock_type::now() );
for ( std::size_t i = 0; i < jobs; ++i) {
coro_type::pull_type c( stack_alloc, fn, preserve);
}
duration_type total = clock_type::now() - start;
total -= overhead_clock(); // overhead of measurement
total /= jobs; // loops
return total;
}
# ifdef BOOST_CONTEXT_CYCLE
cycle_type measure_cycles( cycle_type overhead)
{
stack_allocator stack_alloc;
cycle_type start( cycles() );
for ( std::size_t i = 0; i < jobs; ++i) {
coro_type::pull_type c( stack_alloc, fn, preserve);
}
cycle_type total = cycles() - start;
total -= overhead; // overhead of measurement
total /= jobs; // loops
return total;
}
# endif
int main( int argc, char * argv[])
{
try
{
bool preserve = false, unwind = true, bind = false;
boost::program_options::options_description desc("allowed options");
desc.add_options()
("help", "help message")
("bind,b", boost::program_options::value< bool >( & bind), "bind thread to CPU")
("fpu,f", boost::program_options::value< bool >( & preserve), "preserve FPU registers")
("jobs,j", boost::program_options::value< boost::uint64_t >( & jobs), "jobs to run");
boost::program_options::variables_map vm;
boost::program_options::store(
boost::program_options::parse_command_line(
argc,
argv,
desc),
vm);
boost::program_options::notify( vm);
if ( vm.count("help") ) {
std::cout << desc << std::endl;
return EXIT_SUCCESS;
}
if ( bind) bind_to_processor( 0);
duration_type overhead_c = overhead_clock();
std::cout << "overhead " << overhead_c.count() << " nano seconds" << std::endl;
boost::uint64_t res = measure_time( overhead_c).count();
std::cout << "average of " << res << " nano seconds" << std::endl;
#ifdef BOOST_CONTEXT_CYCLE
cycle_type overhead_y = overhead_cycle();
std::cout << "overhead " << overhead_y << " cpu cycles" << std::endl;
res = measure_cycles( overhead_y);
std::cout << "average of " << res << " cpu cycles" << std::endl;
#endif
return EXIT_SUCCESS;
}
catch ( std::exception const& e)
{ std::cerr << "exception: " << e.what() << std::endl; }
catch (...)
{ std::cerr << "unhandled exception" << std::endl; }
return EXIT_FAILURE;
}

View File

@ -0,0 +1,204 @@
// 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)
#include <cstdlib>
#include <iostream>
#include <stdexcept>
#include <string>
#include <boost/chrono.hpp>
#include <boost/coroutine2/all.hpp>
#include <boost/cstdint.hpp>
#include <boost/program_options.hpp>
#include "../bind_processor.hpp"
#include "../clock.hpp"
#include "../cycle.hpp"
bool preserve = false;
boost::uint64_t jobs = 1000;
struct X
{
std::string str;
X( std::string const& str_) :
str( str_)
{}
};
const X x("abc");
void fn_void( boost::coroutines2::coroutine< void >::push_type & c)
{ while ( true) c(); }
void fn_int( boost::coroutines2::coroutine< int >::push_type & c)
{ while ( true) c( 7); }
void fn_x( boost::coroutines2::coroutine< X >::push_type & c)
{
while ( true) c( x);
}
duration_type measure_time_void( duration_type overhead)
{
boost::coroutines2::segmented_stack stack_alloc;
boost::coroutines2::coroutine< void >::pull_type c( stack_alloc, fn_void, preserve);
time_point_type start( clock_type::now() );
for ( std::size_t i = 0; i < jobs; ++i) {
c();
}
duration_type total = clock_type::now() - start;
total -= overhead_clock(); // overhead of measurement
total /= jobs; // loops
total /= 2; // 2x jump_fcontext
return total;
}
duration_type measure_time_int( duration_type overhead)
{
boost::coroutines2::segmented_stack stack_alloc;
boost::coroutines2::coroutine< int >::pull_type c( stack_alloc, fn_int, preserve);
time_point_type start( clock_type::now() );
for ( std::size_t i = 0; i < jobs; ++i) {
c();
}
duration_type total = clock_type::now() - start;
total -= overhead_clock(); // overhead of measurement
total /= jobs; // loops
total /= 2; // 2x jump_fcontext
return total;
}
duration_type measure_time_x( duration_type overhead)
{
boost::coroutines2::segmented_stack stack_alloc;
boost::coroutines2::coroutine< X >::pull_type c( stack_alloc, fn_x, preserve);
time_point_type start( clock_type::now() );
for ( std::size_t i = 0; i < jobs; ++i) {
c();
}
duration_type total = clock_type::now() - start;
total -= overhead_clock(); // overhead of measurement
total /= jobs; // loops
total /= 2; // 2x jump_fcontext
return total;
}
# ifdef BOOST_CONTEXT_CYCLE
cycle_type measure_cycles_void( cycle_type overhead)
{
boost::coroutines2::segmented_stack stack_alloc;
boost::coroutines2::coroutine< void >::pull_type c( stack_alloc, fn_void, preserve);
cycle_type start( cycles() );
for ( std::size_t i = 0; i < jobs; ++i) {
c();
}
cycle_type total = cycles() - start;
total -= overhead; // overhead of measurement
total /= jobs; // loops
total /= 2; // 2x jump_fcontext
return total;
}
cycle_type measure_cycles_int( cycle_type overhead)
{
boost::coroutines2::segmented_stack stack_alloc;
boost::coroutines2::coroutine< int >::pull_type c( stack_alloc, fn_int, preserve);
cycle_type start( cycles() );
for ( std::size_t i = 0; i < jobs; ++i) {
c();
}
cycle_type total = cycles() - start;
total -= overhead; // overhead of measurement
total /= jobs; // loops
total /= 2; // 2x jump_fcontext
return total;
}
cycle_type measure_cycles_x( cycle_type overhead)
{
boost::coroutines2::segmented_stack stack_alloc;
boost::coroutines2::coroutine< X >::pull_type c( stack_alloc, fn_x, preserve);
cycle_type start( cycles() );
for ( std::size_t i = 0; i < jobs; ++i) {
c();
}
cycle_type total = cycles() - start;
total -= overhead; // overhead of measurement
total /= jobs; // loops
total /= 2; // 2x jump_fcontext
return total;
}
# endif
int main( int argc, char * argv[])
{
try
{
bool bind = false;
boost::program_options::options_description desc("allowed options");
desc.add_options()
("help", "help message")
("bind,b", boost::program_options::value< bool >( & bind), "bind thread to CPU")
("fpu,f", boost::program_options::value< bool >( & preserve), "preserve FPU registers")
("jobs,j", boost::program_options::value< boost::uint64_t >( & jobs), "jobs to run");
boost::program_options::variables_map vm;
boost::program_options::store(
boost::program_options::parse_command_line(
argc,
argv,
desc),
vm);
boost::program_options::notify( vm);
if ( vm.count("help") ) {
std::cout << desc << std::endl;
return EXIT_SUCCESS;
}
if ( bind) bind_to_processor( 0);
duration_type overhead_c = overhead_clock();
std::cout << "overhead " << overhead_c.count() << " nano seconds" << std::endl;
boost::uint64_t res = measure_time_void( overhead_c).count();
std::cout << "void: average of " << res << " nano seconds" << std::endl;
res = measure_time_int( overhead_c).count();
std::cout << "int: average of " << res << " nano seconds" << std::endl;
res = measure_time_x( overhead_c).count();
std::cout << "X: average of " << res << " nano seconds" << std::endl;
#ifdef BOOST_CONTEXT_CYCLE
cycle_type overhead_y = overhead_cycle();
std::cout << "overhead " << overhead_y << " cpu cycles" << std::endl;
res = measure_cycles_void( overhead_y);
std::cout << "void: average of " << res << " cpu cycles" << std::endl;
res = measure_cycles_int( overhead_y);
std::cout << "int: average of " << res << " cpu cycles" << std::endl;
res = measure_cycles_x( overhead_y);
std::cout << "X: average of " << res << " cpu cycles" << std::endl;
#endif
return EXIT_SUCCESS;
}
catch ( std::exception const& e)
{ std::cerr << "exception: " << e.what() << std::endl; }
catch (...)
{ std::cerr << "unhandled exception" << std::endl; }
return EXIT_FAILURE;
}

29
test/Jamfile.v2 Normal file
View File

@ -0,0 +1,29 @@
# 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)
import common ;
import feature ;
import indirect ;
import modules ;
import os ;
import testing ;
import toolset ;
project boost/coroutine2/test
: requirements
<library>../../test/build//boost_unit_test_framework
<library>/boost/context//boost_context
<toolset>gcc,<segmented-stacks>on:<cxxflags>-fsplit-stack
<toolset>gcc,<segmented-stacks>on:<cxxflags>-DBOOST_USE_SEGMENTED_STACKS
<toolset>clang,<segmented-stacks>on:<cxxflags>-fsplit-stack
<toolset>clang,<segmented-stacks>on:<cxxflags>-DBOOST_USE_SEGMENTED_STACKS
<link>static
<threading>multi
;
test-suite "coroutine2" :
[ run test_coroutine.cpp ]
;

599
test/test_coroutine.cpp Normal file
View File

@ -0,0 +1,599 @@
// 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)
#include <algorithm>
#include <cstdio>
#include <iostream>
#include <sstream>
#include <stdexcept>
#include <string>
#include <tuple>
#include <vector>
#include <boost/assert.hpp>
#include <boost/test/unit_test.hpp>
#include <boost/coroutine2/coroutine.hpp>
namespace coro = boost::coroutines2;
int value1 = 0;
std::string value2 = "";
bool value3 = false;
double value4 = .0;
int * value5 = 0;
int& value6 = value1;
int& value7 = value1;
int value8 = 0;
int value9 = 0;
struct X : private boost::noncopyable
{
X() { value1 = 7; }
~X() { value1 = 0; }
};
class copyable
{
public:
bool state;
copyable() :
state( false)
{}
copyable( int) :
state( true)
{}
void operator()( coro::coroutine< int >::push_type &)
{ value3 = state; }
};
class moveable
{
public:
bool state;
moveable() :
state( false)
{}
moveable( int) :
state( true)
{}
moveable( moveable const&) = delete;
moveable & operator=( moveable const&) = delete;
moveable( moveable && other) :
state( false)
{ std::swap( state, other.state); }
moveable & operator=( moveable && other)
{
if ( this != & other) {
state = other.state;
other.state = false;
}
return * this;
}
void operator()( coro::coroutine< int >::push_type &)
{ value3 = state; }
};
class movedata
{
public:
int i;
movedata( int i_) :
i( i_)
{}
movedata( movedata const&) = delete;
movedata & operator=( movedata const&) = delete;
movedata( movedata && other) :
i( 0)
{ std::swap( i, other.i); }
movedata & operator=( movedata && other)
{
if ( this != & other) {
i = other.i;
other.i = 0;
}
return * this;
}
};
struct my_exception {};
void f1( coro::coroutine< void >::push_type & c)
{
while ( c)
c();
}
void f2( coro::coroutine< void >::push_type &)
{ ++value1; }
void f3( coro::coroutine< void >::push_type & c)
{
++value1;
c();
++value1;
}
void f4( coro::coroutine< int >::push_type & c)
{
c( 3);
c( 7);
}
void f5( coro::coroutine< std::string >::push_type & c)
{
std::string res("abc");
c( res);
res = "xyz";
c( res);
}
void f6( coro::coroutine< int >::pull_type & c)
{ value1 = c.get(); }
void f7( coro::coroutine< std::string >::pull_type & c)
{ value2 = c.get(); }
void f8( coro::coroutine< std::tuple< double, double > >::pull_type & c)
{
double x = 0, y = 0;
std::tie( x, y) = c.get();
value4 = x + y;
c();
std::tie( x, y) = c.get();
value4 = x + y;
}
void f9( coro::coroutine< int * >::pull_type & c)
{ value5 = c.get(); }
void f91( coro::coroutine< int const* >::pull_type & c)
{ value5 = const_cast< int * >( c.get() ); }
void f10( coro::coroutine< int & >::pull_type & c)
{
int const& i = c.get();
value5 = const_cast< int * >( & i);
}
void f101( coro::coroutine< int const& >::pull_type & c)
{
int const& i = c.get();
value5 = const_cast< int * >( & i);
}
void f11( coro::coroutine< std::tuple< int, int > >::pull_type & c)
{
std::tie( value8, value9) = c.get();
}
void f12( coro::coroutine< void >::pull_type & c)
{
X x_;
c();
c();
}
template< typename E >
void f14( coro::coroutine< void >::pull_type &, E const& e)
{ throw e; }
void f16( coro::coroutine< int >::push_type & c)
{
c( 1);
c( 2);
c( 3);
c( 4);
c( 5);
}
void f17( coro::coroutine< int >::pull_type & c, std::vector< int > & vec)
{
int x = c.get();
while ( 5 > x)
{
vec.push_back( x);
x = c().get();
}
}
void f19( coro::coroutine< int& >::push_type & c, int & i1, int & i2, int & i3)
{
c( i1);
c( i2);
c( i3);
}
void f20( coro::coroutine< int >::push_type &)
{}
void f21( coro::coroutine< int >::pull_type & c)
{
while ( c)
{
value1 = c.get();
c();
}
}
void f22( coro::coroutine< movedata >::pull_type & c)
{
movedata mv( c.get() );
value1 = mv.i;
}
void test_move()
{
{
coro::coroutine< int >::pull_type coro1( f20);
coro::coroutine< int >::pull_type coro2( f16);
BOOST_CHECK( ! coro1);
BOOST_CHECK( coro2);
BOOST_CHECK_EQUAL( 1, coro2.get() );
coro2();
BOOST_CHECK_EQUAL( 2, coro2.get() );
coro1 = std::move( coro2);
BOOST_CHECK( coro1);
BOOST_CHECK( ! coro2);
coro1();
BOOST_CHECK_EQUAL( 3, coro1.get() );
BOOST_CHECK( ! coro2);
}
{
value3 = false;
copyable cp( 3);
BOOST_CHECK( cp.state);
BOOST_CHECK( ! value3);
coro::coroutine< int >::pull_type coro( cp);
BOOST_CHECK( cp.state);
BOOST_CHECK( value3);
}
{
value3 = false;
moveable mv( 7);
BOOST_CHECK( mv.state);
BOOST_CHECK( ! value3);
coro::coroutine< int >::pull_type coro( std::move( mv) );
BOOST_CHECK( ! mv.state);
BOOST_CHECK( value3);
}
{
value1 = 0;
movedata mv( 7);
BOOST_CHECK_EQUAL( 0, value1);
BOOST_CHECK_EQUAL( 7, mv.i);
coro::coroutine< movedata >::push_type coro( f22);
coro( std::move( mv) );
BOOST_CHECK_EQUAL( 7, value1);
BOOST_CHECK_EQUAL( 0, mv.i);
}
}
void test_complete()
{
value1 = 0;
coro::coroutine< void >::pull_type coro( f2);
BOOST_CHECK( ! coro);
BOOST_CHECK_EQUAL( ( int)1, value1);
}
void test_jump()
{
value1 = 0;
coro::coroutine< void >::pull_type coro( f3);
BOOST_CHECK( coro);
BOOST_CHECK_EQUAL( ( int)1, value1);
coro();
BOOST_CHECK( ! coro);
BOOST_CHECK_EQUAL( ( int)2, value1);
}
void test_result_int()
{
coro::coroutine< int >::pull_type coro( f4);
BOOST_CHECK( coro);
int result = coro.get();
BOOST_CHECK( coro);
BOOST_CHECK_EQUAL( 3, result);
result = coro().get();
BOOST_CHECK( coro);
BOOST_CHECK_EQUAL( 7, result);
coro();
BOOST_CHECK( ! coro);
}
void test_result_string()
{
coro::coroutine< std::string >::pull_type coro( f5);
BOOST_CHECK( coro);
std::string result = coro.get();
BOOST_CHECK( coro);
BOOST_CHECK_EQUAL( std::string("abc"), result);
result = coro().get();
BOOST_CHECK( coro);
BOOST_CHECK_EQUAL( std::string("xyz"), result);
coro();
BOOST_CHECK( ! coro);
}
void test_arg_int()
{
value1 = 0;
coro::coroutine< int >::push_type coro( f6);
BOOST_CHECK( coro);
coro( 3);
BOOST_CHECK( ! coro);
BOOST_CHECK_EQUAL( 3, value1);
}
void test_arg_string()
{
value2 = "";
coro::coroutine< std::string >::push_type coro( f7);
BOOST_CHECK( coro);
coro( std::string("abc") );
BOOST_CHECK( ! coro);
BOOST_CHECK_EQUAL( std::string("abc"), value2);
}
void test_fp()
{
value4 = 0;
coro::coroutine< std::tuple< double, double > >::push_type coro( f8);
BOOST_CHECK( coro);
coro( std::make_tuple( 7.35, 3.14) );
BOOST_CHECK( coro);
BOOST_CHECK_EQUAL( ( double) 10.49, value4);
value4 = 0;
coro( std::make_tuple( 1.15, 3.14) );
BOOST_CHECK( ! coro);
BOOST_CHECK_EQUAL( ( double) 4.29, value4);
}
void test_ptr()
{
value5 = 0;
int a = 3;
coro::coroutine< int * >::push_type coro( f9);
BOOST_CHECK( coro);
coro( & a);
BOOST_CHECK( ! coro);
BOOST_CHECK_EQUAL( & a, value5);
}
void test_const_ptr()
{
value5 = 0;
int a = 3;
coro::coroutine< int const* >::push_type coro( f91);
BOOST_CHECK( coro);
coro( & a);
BOOST_CHECK( ! coro);
BOOST_CHECK_EQUAL( & a, value5);
}
void test_ref()
{
value5 = 0;
int a = 3;
coro::coroutine< int & >::push_type coro( f10);
BOOST_CHECK( coro);
coro( a);
BOOST_CHECK( ! coro);
BOOST_CHECK_EQUAL( & a, value5);
}
void test_const_ref()
{
value5 = 0;
int a = 3;
coro::coroutine< int const& >::push_type coro( f101);
BOOST_CHECK( coro);
coro( a);
BOOST_CHECK( ! coro);
BOOST_CHECK_EQUAL( & a, value5);
}
void test_tuple()
{
value8 = 0;
value9 = 0;
int a = 3, b = 7;
std::tuple< int, int > tpl( a, b);
BOOST_CHECK_EQUAL( a, std::get< 0 >( tpl) );
BOOST_CHECK_EQUAL( b, std::get< 1 >( tpl) );
coro::coroutine< std::tuple< int, int > >::push_type coro( f11);
BOOST_CHECK( coro);
coro( tpl);
BOOST_CHECK( ! coro);
BOOST_CHECK_EQUAL( a, value8);
BOOST_CHECK_EQUAL( b, value9);
}
void test_unwind()
{
value1 = 0;
{
coro::coroutine< void >::push_type coro( f12);
BOOST_CHECK( coro);
BOOST_CHECK_EQUAL( ( int) 0, value1);
coro();
BOOST_CHECK( coro);
BOOST_CHECK_EQUAL( ( int) 7, value1);
coro();
BOOST_CHECK_EQUAL( ( int) 7, value1);
}
BOOST_CHECK_EQUAL( ( int) 0, value1);
}
void test_exceptions()
{
bool thrown = false;
std::runtime_error ex("abc");
try
{
coro::coroutine< void >::push_type coro(
std::bind( f14< std::runtime_error >, std::placeholders::_1, ex) );
BOOST_CHECK( coro);
coro();
BOOST_CHECK( ! coro);
BOOST_CHECK( false);
}
catch ( std::runtime_error const&)
{ thrown = true; }
catch ( std::exception const&)
{}
catch (...)
{}
BOOST_CHECK( thrown);
}
void test_input_iterator()
{
{
using std::begin;
using std::end;
std::vector< int > vec;
coro::coroutine< int >::pull_type coro( f16);
coro::coroutine< int >::pull_type::iterator e = end( coro);
for (
coro::coroutine< int >::pull_type::iterator i = begin( coro);
i != e; ++i)
{ vec.push_back( * i); }
BOOST_CHECK_EQUAL( ( std::size_t)5, vec.size() );
BOOST_CHECK_EQUAL( ( int)1, vec[0] );
BOOST_CHECK_EQUAL( ( int)2, vec[1] );
BOOST_CHECK_EQUAL( ( int)3, vec[2] );
BOOST_CHECK_EQUAL( ( int)4, vec[3] );
BOOST_CHECK_EQUAL( ( int)5, vec[4] );
}
{
std::vector< int > vec;
coro::coroutine< int >::pull_type coro( f16);
for ( auto i : coro)
{ vec.push_back( i); }
BOOST_CHECK_EQUAL( ( std::size_t)5, vec.size() );
BOOST_CHECK_EQUAL( ( int)1, vec[0] );
BOOST_CHECK_EQUAL( ( int)2, vec[1] );
BOOST_CHECK_EQUAL( ( int)3, vec[2] );
BOOST_CHECK_EQUAL( ( int)4, vec[3] );
BOOST_CHECK_EQUAL( ( int)5, vec[4] );
}
{
int i1 = 1, i2 = 2, i3 = 3;
coro::coroutine< int& >::pull_type coro(
std::bind( f19, std::placeholders::_1, std::ref( i1), std::ref( i2), std::ref( i3) ) );
int counter = 1;
for ( int & i : coro) {
switch ( counter) {
case 1:
BOOST_CHECK_EQUAL( & i1, & i);
break;
case 2:
BOOST_CHECK_EQUAL( & i2, & i);
break;
case 3:
BOOST_CHECK_EQUAL( & i3, & i);
break;
default:
BOOST_ASSERT( false);
}
++counter;
}
}
}
void test_output_iterator()
{
using std::begin;
using std::end;
int counter = 0;
std::vector< int > vec;
coro::coroutine< int >::push_type coro(
[&vec]( coro::coroutine< int >::pull_type & c) {
int x = c.get();
while ( 5 > x)
{
vec.push_back( x);
x = c().get();
}
});
// coro::coroutine< int >::push_type coro(
// std::bind( f17, std::placeholders::_1, std::ref( vec) ) );
coro::coroutine< int >::push_type::iterator e( end( coro) );
for ( coro::coroutine< int >::push_type::iterator i( begin( coro) );
i != e; ++i)
{
i = ++counter;
}
BOOST_CHECK_EQUAL( ( std::size_t)4, vec.size() );
BOOST_CHECK_EQUAL( ( int)1, vec[0] );
BOOST_CHECK_EQUAL( ( int)2, vec[1] );
BOOST_CHECK_EQUAL( ( int)3, vec[2] );
BOOST_CHECK_EQUAL( ( int)4, vec[3] );
}
void test_no_result()
{
coro::coroutine< int >::pull_type coro( f20);
BOOST_CHECK( ! coro);
}
boost::unit_test::test_suite * init_unit_test_suite( int, char* [])
{
boost::unit_test::test_suite * test =
BOOST_TEST_SUITE("Boost.coroutine: coroutine test suite");
test->add( BOOST_TEST_CASE( & test_move) );
test->add( BOOST_TEST_CASE( & test_complete) );
test->add( BOOST_TEST_CASE( & test_jump) );
test->add( BOOST_TEST_CASE( & test_result_int) );
test->add( BOOST_TEST_CASE( & test_result_string) );
test->add( BOOST_TEST_CASE( & test_arg_int) );
test->add( BOOST_TEST_CASE( & test_arg_string) );
test->add( BOOST_TEST_CASE( & test_fp) );
test->add( BOOST_TEST_CASE( & test_ptr) );
test->add( BOOST_TEST_CASE( & test_const_ptr) );
test->add( BOOST_TEST_CASE( & test_no_result) );
test->add( BOOST_TEST_CASE( & test_ref) );
test->add( BOOST_TEST_CASE( & test_const_ref) );
test->add( BOOST_TEST_CASE( & test_tuple) );
test->add( BOOST_TEST_CASE( & test_unwind) );
test->add( BOOST_TEST_CASE( & test_exceptions) );
test->add( BOOST_TEST_CASE( & test_input_iterator) );
test->add( BOOST_TEST_CASE( & test_output_iterator) );
return test;
}