mirror of
https://github.com/boostorg/coroutine2.git
synced 2025-05-09 15:14:01 +00:00
initial import
This commit is contained in:
parent
9b56338411
commit
81b2255ddf
11
README.md
Normal file
11
README.md
Normal 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
34
build/Jamfile.v2
Normal 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
33
doc/Jamfile.v2
Normal 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
17
doc/acknowledgements.qbk
Normal 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
13
doc/architectures.qbk
Normal 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
708
doc/asymmetric.qbk
Normal 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
107
doc/attributes.qbk
Normal 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
85
doc/coro.qbk
Normal 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
95
doc/coroutine.qbk
Normal 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
BIN
doc/images/event_model.dia
Normal file
Binary file not shown.
BIN
doc/images/event_model.png
Normal file
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
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
BIN
doc/images/foo_bar_seq.dia
Normal file
Binary file not shown.
BIN
doc/images/foo_bar_seq.png
Normal file
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
BIN
doc/images/same_fringe.dia
Normal file
Binary file not shown.
BIN
doc/images/same_fringe.png
Normal file
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
106
doc/intro.qbk
Normal 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
567
doc/motivation.qbk
Normal 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
36
doc/overview.qbk
Normal 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
40
doc/performance.qbk
Normal 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
289
doc/stack.qbk
Normal 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
49
example/Jamfile.v2
Normal 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
34
example/fibonacci.cpp
Normal 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
53
example/layout.cpp
Normal 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
109
example/parser.cpp
Normal 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
176
example/same_fringe.cpp
Normal 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
65
example/segmented.cpp
Normal 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
24
example/simple.cpp
Normal 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
97
example/tree.h
Normal 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
|
15
include/boost/coroutine2/all.hpp
Normal file
15
include/boost/coroutine2/all.hpp
Normal 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
|
39
include/boost/coroutine2/coroutine.hpp
Normal file
39
include/boost/coroutine2/coroutine.hpp
Normal 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
|
49
include/boost/coroutine2/detail/config.hpp
Normal file
49
include/boost/coroutine2/detail/config.hpp
Normal 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
|
44
include/boost/coroutine2/detail/coroutine.hpp
Normal file
44
include/boost/coroutine2/detail/coroutine.hpp
Normal 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
|
33
include/boost/coroutine2/detail/forced_unwind.hpp
Normal file
33
include/boost/coroutine2/detail/forced_unwind.hpp
Normal 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
|
102
include/boost/coroutine2/detail/pull_control_block.hpp
Normal file
102
include/boost/coroutine2/detail/pull_control_block.hpp
Normal 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
|
259
include/boost/coroutine2/detail/pull_control_block.ipp
Normal file
259
include/boost/coroutine2/detail/pull_control_block.ipp
Normal 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
|
320
include/boost/coroutine2/detail/pull_coroutine.hpp
Normal file
320
include/boost/coroutine2/detail/pull_coroutine.hpp
Normal 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
|
226
include/boost/coroutine2/detail/pull_coroutine.ipp
Normal file
226
include/boost/coroutine2/detail/pull_coroutine.ipp
Normal 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
|
106
include/boost/coroutine2/detail/push_control_block.hpp
Normal file
106
include/boost/coroutine2/detail/push_control_block.hpp
Normal 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
|
285
include/boost/coroutine2/detail/push_control_block.ipp
Normal file
285
include/boost/coroutine2/detail/push_control_block.ipp
Normal 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
|
242
include/boost/coroutine2/detail/push_coroutine.hpp
Normal file
242
include/boost/coroutine2/detail/push_coroutine.hpp
Normal 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
|
208
include/boost/coroutine2/detail/push_coroutine.ipp
Normal file
208
include/boost/coroutine2/detail/push_coroutine.ipp
Normal 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
|
57
include/boost/coroutine2/detail/rref.hpp
Normal file
57
include/boost/coroutine2/detail/rref.hpp
Normal 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
|
37
include/boost/coroutine2/detail/state.hpp
Normal file
37
include/boost/coroutine2/detail/state.hpp
Normal 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
|
33
include/boost/coroutine2/fixedsize_stack.hpp
Normal file
33
include/boost/coroutine2/fixedsize_stack.hpp
Normal 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
|
33
include/boost/coroutine2/protected_fixedsize_stack.hpp
Normal file
33
include/boost/coroutine2/protected_fixedsize_stack.hpp
Normal 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
|
37
include/boost/coroutine2/segmented_stack.hpp
Normal file
37
include/boost/coroutine2/segmented_stack.hpp
Normal 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
14
index.html
Normal 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>© 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
14
meta/libraries.json
Normal 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
77
performance/Jamfile.v2
Normal 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
|
||||
;
|
12
performance/bind_processor.hpp
Normal file
12
performance/bind_processor.hpp
Normal 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
|
25
performance/bind_processor_aix.cpp
Normal file
25
performance/bind_processor_aix.cpp
Normal 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>
|
29
performance/bind_processor_freebsd.cpp
Normal file
29
performance/bind_processor_freebsd.cpp
Normal 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>
|
31
performance/bind_processor_hpux.cpp
Normal file
31
performance/bind_processor_hpux.cpp
Normal 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>
|
30
performance/bind_processor_linux.cpp
Normal file
30
performance/bind_processor_linux.cpp
Normal 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>
|
26
performance/bind_processor_solaris.cpp
Normal file
26
performance/bind_processor_solaris.cpp
Normal 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>
|
24
performance/bind_processor_windows.cpp
Normal file
24
performance/bind_processor_windows.cpp
Normal 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
45
performance/clock.hpp
Normal 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
26
performance/cycle.hpp
Normal 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
|
83
performance/cycle_i386.hpp
Normal file
83
performance/cycle_i386.hpp
Normal 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
|
79
performance/cycle_x86-64.hpp
Normal file
79
performance/cycle_x86-64.hpp
Normal 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
|
107
performance/performance_create_protected.cpp
Normal file
107
performance/performance_create_protected.cpp
Normal 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;
|
||||
}
|
107
performance/performance_create_standard.cpp
Normal file
107
performance/performance_create_standard.cpp
Normal 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;
|
||||
}
|
198
performance/performance_switch.cpp
Normal file
198
performance/performance_switch.cpp
Normal 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;
|
||||
}
|
73
performance/segmented/Jamfile.v2
Normal file
73
performance/segmented/Jamfile.v2
Normal 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
|
||||
;
|
107
performance/segmented/performance_create_segmented.cpp
Normal file
107
performance/segmented/performance_create_segmented.cpp
Normal 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;
|
||||
}
|
204
performance/segmented/performance_switch.cpp
Normal file
204
performance/segmented/performance_switch.cpp
Normal 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
29
test/Jamfile.v2
Normal 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
599
test/test_coroutine.cpp
Normal 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;
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user