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