diff options
Diffstat (limited to 'src/boost/libs/coroutine')
58 files changed, 5247 insertions, 0 deletions
diff --git a/src/boost/libs/coroutine/README.md b/src/boost/libs/coroutine/README.md new file mode 100644 index 00000000..2e145891 --- /dev/null +++ b/src/boost/libs/coroutine/README.md @@ -0,0 +1,14 @@ +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. + +Note that boost.coroutine is deprecated - boost.coroutine2 is its successor. +If you are forced to use a pre-C++11 compiler you should still use boost.coroutine. diff --git a/src/boost/libs/coroutine/build/Jamfile.v2 b/src/boost/libs/coroutine/build/Jamfile.v2 new file mode 100644 index 00000000..bc32ec4e --- /dev/null +++ b/src/boost/libs/coroutine/build/Jamfile.v2 @@ -0,0 +1,45 @@ + +# Copyright Oliver Kowalke 2009. +# Distributed under the Boost Software License, Version 1.0. +# (See accompanying file LICENSE_1_0.txt or copy at +# http://www.boost.org/LICENSE_1_0.txt) + +import feature ; +import modules ; +import toolset ; + +project boost/coroutine + : requirements + <library>/boost/context//boost_context + <library>/boost/thread//boost_thread + <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_COROUTINES_DYN_LINK=1 + <define>BOOST_COROUTINES_SOURCE + : usage-requirements + <link>shared:<define>BOOST_COROUTINES_DYN_LINK=1 + : source-location ../src + ; + +alias stack_traits_sources + : windows/stack_traits.cpp + : <target-os>windows + ; + +alias stack_traits_sources + : posix/stack_traits.cpp + ; + +explicit stack_traits_sources ; + +lib boost_coroutine + : detail/coroutine_context.cpp + exceptions.cpp + stack_traits_sources + : <link>shared:<library>../../context/build//boost_context + <link>shared:<library>../../thread/build//boost_thread + ; + +boost-install boost_coroutine ; diff --git a/src/boost/libs/coroutine/example/asymmetric/Jamfile.v2 b/src/boost/libs/coroutine/example/asymmetric/Jamfile.v2 new file mode 100644 index 00000000..c1cba673 --- /dev/null +++ b/src/boost/libs/coroutine/example/asymmetric/Jamfile.v2 @@ -0,0 +1,63 @@ +# Boost.Coroutine Library Examples Jamfile + +# Copyright Oliver Kowalke 2009. +# Distributed under the Boost Software License, Version 1.0. +# (See accompanying file LICENSE_1_0.txt or copy at +# http://www.boost.org/LICENSE_1_0.txt) + +# For more information, see http://www.boost.org/ + +import common ; +import feature ; +import indirect ; +import modules ; +import os ; +import toolset ; + +project boost/coroutine/example/asymmetric + : requirements + <library>/boost/context//boost_context + <library>/boost/coroutine//boost_coroutine + <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>shared + <threading>multi + ; + +exe chaining + : chaining.cpp + ; +exe echo + : echo.cpp + ; +exe exception + : exception.cpp + ; +exe fibonacci + : fibonacci.cpp + ; +exe layout + : layout.cpp + ; +exe parallel + : parallel.cpp + ; +exe power + : power.cpp + ; +exe same_fringe + : same_fringe.cpp + ; +exe segmented_stack + : segmented_stack.cpp + ; +exe simple + : simple.cpp + test.cpp + ; +exe unwind + : unwind.cpp + ; diff --git a/src/boost/libs/coroutine/example/asymmetric/X.h b/src/boost/libs/coroutine/example/asymmetric/X.h new file mode 100644 index 00000000..254d44f6 --- /dev/null +++ b/src/boost/libs/coroutine/example/asymmetric/X.h @@ -0,0 +1,13 @@ +#ifndef X_H +#define X_H + +struct X +{ + int i; + + X( int i_) : + i( i_) + {} +}; + +#endif // X_H diff --git a/src/boost/libs/coroutine/example/asymmetric/chaining.cpp b/src/boost/libs/coroutine/example/asymmetric/chaining.cpp new file mode 100644 index 00000000..4a4dd997 --- /dev/null +++ b/src/boost/libs/coroutine/example/asymmetric/chaining.cpp @@ -0,0 +1,206 @@ + +// 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 <boost/coroutine/all.hpp> + +#include <iostream> +#include <iomanip> +#include <string> +#include <cctype> +#include <sstream> + +#include <boost/bind.hpp> +#include <boost/foreach.hpp> + +typedef boost::coroutines::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. At + // the time, I wished for a way to resume at the suspend point! + BOOST_FOREACH(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) +{ + BOOST_FOREACH(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) +{ + BOOST_FOREACH(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; + } +} + +int main(int argc, char *argv[]) +{ + // 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 << "\nreadlines:\n"; + std::istringstream infile(data); + // Each coroutine-function has a small, specific job to do. Instead of + // adding conditional logic to a large, complex input function, the + // caller composes smaller functions into the desired processing + // chain. + coro_t::pull_type reader(boost::bind(readlines, _1, boost::ref(infile))); + coro_t::pull_type tracer(boost::bind(trace, _1, boost::ref(reader))); + BOOST_FOREACH(std::string line, tracer) + { + std::cout << "got: " << line << "\n"; + } + } + + { + std::cout << "\ncompose a chain:\n"; + std::istringstream infile(data); + coro_t::pull_type reader(boost::bind(readlines, _1, boost::ref(infile))); + coro_t::pull_type tokenizer(boost::bind(tokenize, _1, boost::ref(reader))); + coro_t::pull_type tracer(boost::bind(trace, _1, boost::ref(tokenizer))); + BOOST_FOREACH(std::string token, tracer) + { + // just iterate, we're already pulling through tracer + } + } + + { + std::cout << "\nfilter:\n"; + std::istringstream infile(data); + coro_t::pull_type reader(boost::bind(readlines, _1, boost::ref(infile))); + coro_t::pull_type tokenizer(boost::bind(tokenize, _1, boost::ref(reader))); + coro_t::pull_type filter(boost::bind(only_words, _1, boost::ref(tokenizer))); + coro_t::pull_type tracer(boost::bind(trace, _1, boost::ref(filter))); + BOOST_FOREACH(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(boost::bind(readlines, _1, boost::ref(infile))); + coro_t::pull_type tokenizer(boost::bind(tokenize, _1, boost::ref(reader))); + coro_t::pull_type filter(boost::bind(only_words, _1, boost::ref(tokenizer))); + coro_t::push_type writer(boost::bind(layout, _1, 5, 15)); + BOOST_FOREACH(std::string token, filter) + { + writer(token); + } + } + + { + std::cout << "\ncalling layout() directly:\n"; + std::istringstream infile(data); + coro_t::pull_type reader(boost::bind(readlines, _1, boost::ref(infile))); + coro_t::pull_type tokenizer(boost::bind(tokenize, _1, boost::ref(reader))); + coro_t::pull_type filter(boost::bind(only_words, _1, boost::ref(tokenizer))); + // Because of the symmetry of the API, we can directly call layout() + // instead of using it as a coroutine-function. + layout(filter, 5, 15); + } + + { + std::cout << "\nfiltering output:\n"; + std::istringstream infile(data); + coro_t::pull_type reader(boost::bind(readlines, _1, boost::ref(infile))); + coro_t::pull_type tokenizer(boost::bind(tokenize, _1, boost::ref(reader))); + coro_t::push_type writer(boost::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(boost::bind(only_words, boost::ref(writer), _1)); + BOOST_FOREACH(std::string token, tokenizer) + { + filter(token); + } + } + + std::cout << "\nDone" << std::endl; + + return EXIT_SUCCESS; +} diff --git a/src/boost/libs/coroutine/example/asymmetric/echo.cpp b/src/boost/libs/coroutine/example/asymmetric/echo.cpp new file mode 100644 index 00000000..ba3e82e2 --- /dev/null +++ b/src/boost/libs/coroutine/example/asymmetric/echo.cpp @@ -0,0 +1,48 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include <boost/coroutine/all.hpp> + +#include <cstdlib> +#include <iostream> + +#include <boost/bind.hpp> + +typedef boost::coroutines::asymmetric_coroutine< void >::pull_type pull_coro_t; +typedef boost::coroutines::asymmetric_coroutine< void >::push_type push_coro_t; + +void echo( pull_coro_t & source, int i) +{ + std::cout << i; + source(); +} + +void runit( push_coro_t & sink1) +{ + std::cout << "started! "; + for ( int i = 0; i < 10; ++i) + { + push_coro_t sink2( boost::bind( echo, _1, i) ); + while ( sink2) + sink2(); + sink1(); + } +} + +int main( int argc, char * argv[]) +{ + { + pull_coro_t source( runit); + while ( source) { + std::cout << "-"; + source(); + } + } + + std::cout << "\nDone" << std::endl; + + return EXIT_SUCCESS; +} diff --git a/src/boost/libs/coroutine/example/asymmetric/exception.cpp b/src/boost/libs/coroutine/example/asymmetric/exception.cpp new file mode 100644 index 00000000..5a8ac6a5 --- /dev/null +++ b/src/boost/libs/coroutine/example/asymmetric/exception.cpp @@ -0,0 +1,53 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include <boost/coroutine/all.hpp> + +#include <cstdlib> +#include <iostream> +#include <stdexcept> +#include <string> + +#include <boost/bind.hpp> +#include <boost/throw_exception.hpp> + +typedef boost::coroutines::asymmetric_coroutine< int >::pull_type pull_coro_t; +typedef boost::coroutines::asymmetric_coroutine< int >::push_type push_coro_t; + +struct my_exception : public std::runtime_error +{ + my_exception( std::string const& str) : + std::runtime_error( str) + {} +}; + +void echo( push_coro_t & sink, int j) +{ + for ( int i = 0; i < j; ++i) + { + if ( i == 5) boost::throw_exception( my_exception("abc") ); + sink( i); + } +} + +int main( int argc, char * argv[]) +{ + pull_coro_t source( boost::bind( echo, _1, 10) ); + try + { + while ( source) + { + std::cout << source.get() << std::endl; + source(); + } + } + catch ( my_exception const& ex) + { std::cout << "exception: " << ex.what() << std::endl; } + + std::cout << "\nDone" << std::endl; + + return EXIT_SUCCESS; +} diff --git a/src/boost/libs/coroutine/example/asymmetric/fibonacci.cpp b/src/boost/libs/coroutine/example/asymmetric/fibonacci.cpp new file mode 100644 index 00000000..bba8cc4a --- /dev/null +++ b/src/boost/libs/coroutine/example/asymmetric/fibonacci.cpp @@ -0,0 +1,43 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include <boost/coroutine/all.hpp> + +#include <cstdlib> +#include <iostream> + +#include <boost/range.hpp> + +void fibonacci( boost::coroutines::asymmetric_coroutine< int >::push_type & sink) +{ + int first = 1, second = 1; + sink( first); + sink( second); + while ( true) + { + int third = first + second; + first = second; + second = third; + sink( third); + } +} + +int main() +{ + boost::coroutines::asymmetric_coroutine< int >::pull_type source( fibonacci); + boost::range_iterator< + boost::coroutines::asymmetric_coroutine< int >::pull_type + >::type it( boost::begin( source) ); + for ( int i = 0; i < 10; ++i) + { + std::cout << * it << " "; + ++it; + } + + std::cout << "\nDone" << std::endl; + + return EXIT_SUCCESS; +} diff --git a/src/boost/libs/coroutine/example/asymmetric/layout.cpp b/src/boost/libs/coroutine/example/asymmetric/layout.cpp new file mode 100644 index 00000000..bc1924f6 --- /dev/null +++ b/src/boost/libs/coroutine/example/asymmetric/layout.cpp @@ -0,0 +1,74 @@ + +// 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 <boost/coroutine/all.hpp> + +#include <iostream> +#include <iomanip> +#include <vector> +#include <string> +#include <utility> + +#include <boost/assign/list_of.hpp> +#include <boost/bind.hpp> +#include <boost/range.hpp> + +struct FinalEOL +{ + ~FinalEOL() { std::cout << std::endl; } +}; + +void layout(boost::coroutines::asymmetric_coroutine<std::string>::pull_type& in, 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 (! 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; + } +} + +int main(int argc, char *argv[]) +{ + std::vector<std::string> words = boost::assign::list_of + ("peas") + ("porridge") + ("hot") + ("peas") + ("porridge") + ("cold") + ("peas") + ("porridge") + ("in") + ("the") + ("pot") + ("nine") + ("days") + ("old") + ; + + boost::coroutines::asymmetric_coroutine<std::string>::push_type writer( + boost::bind(layout, _1, 5, 15)); + + std::copy(boost::begin(words), boost::end(words), boost::begin(writer)); + + std::cout << "\nDone" << std::endl; + + return EXIT_SUCCESS; +} diff --git a/src/boost/libs/coroutine/example/asymmetric/parallel.cpp b/src/boost/libs/coroutine/example/asymmetric/parallel.cpp new file mode 100644 index 00000000..35027936 --- /dev/null +++ b/src/boost/libs/coroutine/example/asymmetric/parallel.cpp @@ -0,0 +1,50 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include <boost/coroutine/all.hpp> + +#include <cstdlib> +#include <iostream> + +#include <boost/bind.hpp> + +void first( boost::coroutines::asymmetric_coroutine< void >::push_type & sink) +{ + std::cout << "started first! "; + for ( int i = 0; i < 10; ++i) + { + sink(); + std::cout << "a" << i; + } +} + +void second( boost::coroutines::asymmetric_coroutine< void >::push_type & sink) +{ + std::cout << "started second! "; + for ( int i = 0; i < 10; ++i) + { + sink(); + std::cout << "b" << i; + } +} + +int main( int argc, char * argv[]) +{ + { + boost::coroutines::asymmetric_coroutine< void >::pull_type source1( boost::bind( first, _1) ); + boost::coroutines::asymmetric_coroutine< void >::pull_type source2( boost::bind( second, _1) ); + while ( source1 && source2) { + source1(); + std::cout << " "; + source2(); + std::cout << " "; + } + } + + std::cout << "\nDone" << std::endl; + + return EXIT_SUCCESS; +} diff --git a/src/boost/libs/coroutine/example/asymmetric/power.cpp b/src/boost/libs/coroutine/example/asymmetric/power.cpp new file mode 100644 index 00000000..df16d0fc --- /dev/null +++ b/src/boost/libs/coroutine/example/asymmetric/power.cpp @@ -0,0 +1,48 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include <boost/coroutine/all.hpp> + +#include <cstdlib> +#include <iostream> + +#include <boost/bind.hpp> +#include <boost/foreach.hpp> +#include <boost/range.hpp> + +void power( boost::coroutines::asymmetric_coroutine< int >::push_type & sink, int number, int exponent) +{ + int counter = 0; + int result = 1; + while ( counter++ < exponent) + { + result = result * number; + sink( result); + } +} + +int main() +{ + { + std::cout << "using range functions" << std::endl; + boost::coroutines::asymmetric_coroutine< int >::pull_type source( boost::bind( power, _1, 2, 8) ); + boost::coroutines::asymmetric_coroutine< int >::pull_type::iterator e( boost::end( source) ); + for ( boost::coroutines::asymmetric_coroutine< int >::pull_type::iterator i( boost::begin( source) ); + i != e; ++i) + std::cout << * i << " "; + } + + { + std::cout << "\nusing BOOST_FOREACH" << std::endl; + boost::coroutines::asymmetric_coroutine< int >::pull_type source( boost::bind( power, _1, 2, 8) ); + BOOST_FOREACH( int i, source) + { std::cout << i << " "; } + } + + std::cout << "\nDone" << std::endl; + + return EXIT_SUCCESS; +} diff --git a/src/boost/libs/coroutine/example/asymmetric/same_fringe.cpp b/src/boost/libs/coroutine/example/asymmetric/same_fringe.cpp new file mode 100644 index 00000000..8860eccd --- /dev/null +++ b/src/boost/libs/coroutine/example/asymmetric/same_fringe.cpp @@ -0,0 +1,162 @@ + +// 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 <boost/coroutine/all.hpp> + +#include <cstddef> +#include <cstdlib> +#include <iostream> +#include <iterator> +#include <string> +#include <utility> + +#include <boost/bind.hpp> +#include <boost/range.hpp> +#include <boost/shared_ptr.hpp> + +struct node +{ + typedef boost::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::coroutines::asymmetric_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::coroutines::asymmetric_coroutine<std::string>::pull_type left_d_reader( + boost::bind(traverse, left_d, _1)); + std::cout << "left tree from d:\n"; + std::copy(boost::begin(left_d_reader), + boost::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::coroutines::asymmetric_coroutine<std::string>::pull_type right_b_reader( + boost::bind(traverse, right_b, _1)); + std::cout << "right tree from b:\n"; + std::copy(boost::begin(right_b_reader), + boost::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::coroutines::asymmetric_coroutine<std::string>::pull_type right_x_reader( + boost::bind(traverse, right_x, _1)); + std::cout << "right tree from x:\n"; + std::copy(boost::begin(right_x_reader), + boost::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::coroutines::asymmetric_coroutine<std::string>::pull_type left_d_reader( + boost::bind(traverse, left_d, _1)); + + node::ptr_t right_b(create_right_tree_from("b")); + boost::coroutines::asymmetric_coroutine<std::string>::pull_type right_b_reader( + boost::bind(traverse, right_b, _1)); + + std::cout << "left tree from d == right tree from b? " + << std::boolalpha + << std::equal(boost::begin(left_d_reader), + boost::end(left_d_reader), + boost::begin(right_b_reader)) + << std::endl; + } + + { + node::ptr_t left_d(create_left_tree_from("d")); + boost::coroutines::asymmetric_coroutine<std::string>::pull_type left_d_reader( + boost::bind(traverse, left_d, _1)); + + node::ptr_t right_x(create_right_tree_from("x")); + boost::coroutines::asymmetric_coroutine<std::string>::pull_type right_x_reader( + boost::bind(traverse, right_x, _1)); + + std::cout << "left tree from d == right tree from x? " + << std::boolalpha + << std::equal(boost::begin(left_d_reader), + boost::end(left_d_reader), + boost::begin(right_x_reader)) + << std::endl; + } + + std::cout << "Done" << std::endl; + + return EXIT_SUCCESS; +} diff --git a/src/boost/libs/coroutine/example/asymmetric/segmented_stack.cpp b/src/boost/libs/coroutine/example/asymmetric/segmented_stack.cpp new file mode 100644 index 00000000..b8b27ab1 --- /dev/null +++ b/src/boost/libs/coroutine/example/asymmetric/segmented_stack.cpp @@ -0,0 +1,63 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include <boost/coroutine/all.hpp> + +#include <iostream> + +#include <boost/assert.hpp> +#include <boost/config.hpp> + +int count = 384; + +#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); + } +} + +void foo( boost::coroutines::asymmetric_coroutine< void >::pull_type & source) +{ + bar( count); + source(); +} + + +int main( int argc, char * argv[]) +{ +#if defined(BOOST_USE_SEGMENTED_STACKS) + std::cout << "using segmented stacks: allocates " << count << " * 4kB == " << 4 * count << "kB on stack, "; + std::cout << "initial stack size = " << boost::coroutines::stack_allocator::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::coroutines::stack_allocator::traits_type::default_size() / 1024 << "kB" << std::endl; + std::cout << "application might fail" << std::endl; +#endif + + boost::coroutines::asymmetric_coroutine< void >::push_type sink( foo); + sink(); + + std::cout << "Done" << std::endl; + + return 0; +} diff --git a/src/boost/libs/coroutine/example/asymmetric/simple.cpp b/src/boost/libs/coroutine/example/asymmetric/simple.cpp new file mode 100644 index 00000000..045bed84 --- /dev/null +++ b/src/boost/libs/coroutine/example/asymmetric/simple.cpp @@ -0,0 +1,53 @@ +#include <boost/coroutine/all.hpp> + +#include <cstdlib> +#include <iostream> + +#include <boost/bind.hpp> + +#include "X.h" + +typedef boost::coroutines::asymmetric_coroutine< X& >::pull_type pull_coro_t; +typedef boost::coroutines::asymmetric_coroutine< X& >::push_type push_coro_t; + +void fn1( push_coro_t & sink) +{ + for ( int i = 0; i < 10; ++i) + { + X x( i); + sink( x); + } +} + +void fn2( pull_coro_t & source) +{ + while ( source) { + X & x = source.get(); + std::cout << "i = " << x.i << std::endl; + source(); + } +} + +int main( int argc, char * argv[]) +{ + { + pull_coro_t source( fn1); + while ( source) { + X & x = source.get(); + std::cout << "i = " << x.i << std::endl; + source(); + } + } + { + push_coro_t sink( fn2); + for ( int i = 0; i < 10; ++i) + { + X x( i); + sink( x); + } + } + std::cout << "Done" << std::endl; + + return EXIT_SUCCESS; +} + diff --git a/src/boost/libs/coroutine/example/asymmetric/test.cpp b/src/boost/libs/coroutine/example/asymmetric/test.cpp new file mode 100644 index 00000000..c6de170b --- /dev/null +++ b/src/boost/libs/coroutine/example/asymmetric/test.cpp @@ -0,0 +1,45 @@ +#include <boost/coroutine/all.hpp> + +#include <boost/bind.hpp> + +#include "X.h" + +typedef boost::coroutines::asymmetric_coroutine< X& >::pull_type pull_coro_t; +typedef boost::coroutines::asymmetric_coroutine< X& >::push_type push_coro_t; + +void foo1( push_coro_t & sink) +{ + for ( int i = 0; i < 10; ++i) + { + X x( i); + sink( x); + } +} + +void foo2( pull_coro_t & source) +{ + while ( source) { + X & x = source.get(); + source(); + } +} + +void bar() +{ + { + pull_coro_t source( foo1); + while ( source) { + X & x = source.get(); + source(); + } + } + { + push_coro_t sink( foo2); + for ( int i = 0; i < 10; ++i) + { + X x( i); + sink( x); + } + } +} + diff --git a/src/boost/libs/coroutine/example/asymmetric/tree.h b/src/boost/libs/coroutine/example/asymmetric/tree.h new file mode 100644 index 00000000..0a45237e --- /dev/null +++ b/src/boost/libs/coroutine/example/asymmetric/tree.h @@ -0,0 +1,125 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef TREE_H +#define TREE_H + +#include <boost/coroutine/all.hpp> + +#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; } + +class tree_visitor : public visitor +{ +private: + boost::coroutines::asymmetric_coroutine< leaf & >::push_type & c_; + +public: + tree_visitor( boost::coroutines::asymmetric_coroutine< leaf & >::push_type & c) : + c_( c) + {} + + void visit( branch & b) + { + if ( b.left) b.left->accept( * this); + if ( b.right) b.right->accept( * this); + } + + void visit( leaf & l) + { c_( l); } +}; + +void enumerate_leafs( boost::coroutines::asymmetric_coroutine< leaf & >::push_type & c, node::ptr_t root) +{ + tree_visitor v( c); + root->accept( v); +} + +#if defined(_MSC_VER) +# pragma warning(pop) +#endif + +#endif // TREE_H diff --git a/src/boost/libs/coroutine/example/asymmetric/unwind.cpp b/src/boost/libs/coroutine/example/asymmetric/unwind.cpp new file mode 100644 index 00000000..b8d9d432 --- /dev/null +++ b/src/boost/libs/coroutine/example/asymmetric/unwind.cpp @@ -0,0 +1,45 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include <boost/coroutine/all.hpp> + +#include <cstdlib> +#include <iostream> + +#include <boost/bind.hpp> + +struct X : private boost::noncopyable +{ + X() { std::cout << "X()" << std::endl; } + ~X() { std::cout << "~X()" << std::endl; } +}; + +void fn( boost::coroutines::asymmetric_coroutine< void >::push_type & sink) +{ + X x; + int i = 0; + while ( true) + { + std::cout << "fn() : " << ++i << std::endl; + sink(); + } +} + +int main( int argc, char * argv[]) +{ + { + boost::coroutines::asymmetric_coroutine< void >::pull_type source( fn); + for ( int k = 0; k < 3; ++k) + { + source(); + } + std::cout << "destroying coroutine and unwinding stack" << std::endl; + } + + std::cout << "\nDone" << std::endl; + + return EXIT_SUCCESS; +} diff --git a/src/boost/libs/coroutine/example/symmetric/Jamfile.v2 b/src/boost/libs/coroutine/example/symmetric/Jamfile.v2 new file mode 100644 index 00000000..c88a44f8 --- /dev/null +++ b/src/boost/libs/coroutine/example/symmetric/Jamfile.v2 @@ -0,0 +1,49 @@ +# Boost.Coroutine Library Examples Jamfile + +# Copyright Oliver Kowalke 2009. +# Distributed under the Boost Software License, Version 1.0. +# (See accompanying file LICENSE_1_0.txt or copy at +# http://www.boost.org/LICENSE_1_0.txt) + +# For more information, see http://www.boost.org/ + +import common ; +import feature ; +import indirect ; +import modules ; +import os ; +import toolset ; + +project boost/coroutine/example/symmetric + : requirements + <library>/boost/context//boost_context + <library>/boost/coroutine//boost_coroutine + <library>/boost/program_options//boost_program_options + <library>/boost/random//boost_random + <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 dice_game + : dice_game.cpp + ; + +exe merge_arrays + : merge_arrays.cpp + ; + +exe unwind + : unwind.cpp + ; + +exe segmented_stack + : segmented_stack.cpp + ; diff --git a/src/boost/libs/coroutine/example/symmetric/dice_game.cpp b/src/boost/libs/coroutine/example/symmetric/dice_game.cpp new file mode 100644 index 00000000..09eabf5a --- /dev/null +++ b/src/boost/libs/coroutine/example/symmetric/dice_game.cpp @@ -0,0 +1,70 @@ + +// Copyright Keld Helsgaun 2000, 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 <boost/coroutine/all.hpp> + +#include <cstdlib> +#include <iostream> + +#include <boost/bind.hpp> +#include <boost/move/move.hpp> +#include <boost/random/random_device.hpp> +#include <boost/random/uniform_int_distribution.hpp> + +typedef boost::coroutines::symmetric_coroutine< void > coro_t; + +class player +{ +private: + int die() + { + boost::random::uniform_int_distribution<> dist( 1, 6); + return dist( gen); + } + + void run_( coro_t::yield_type & yield) + { + int sum = 0; + while ( ( sum += die() ) < 100) + yield( nxt->coro); + std::cout << "player " << id << " winns" << std::endl; + } + + player( player const&); + player & operator=( player const&); + +public: + int id; + player * nxt; + coro_t::call_type coro; + boost::random::random_device gen; + + player( int id_) : + id( id_), nxt( 0), + coro( boost::bind( & player::run_, this, _1) ), + gen() + {} + + void run() + { coro(); } +}; + +int main( int argc, char * argv[]) +{ + player * first = new player( 1); + player * p = first; + for ( int i = 2; i <= 4; ++i) + { + p->nxt = new player( i); + p = p->nxt; + } + p->nxt = first; + first->run(); + + std::cout << "Done" << std::endl; + + return EXIT_SUCCESS; +} diff --git a/src/boost/libs/coroutine/example/symmetric/merge_arrays.cpp b/src/boost/libs/coroutine/example/symmetric/merge_arrays.cpp new file mode 100644 index 00000000..d72f2f7e --- /dev/null +++ b/src/boost/libs/coroutine/example/symmetric/merge_arrays.cpp @@ -0,0 +1,105 @@ + +// Copyright Keld Helsgaun 2000, 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 <boost/coroutine/all.hpp> + +#include <cstdlib> +#include <cstddef> +#include <iostream> +#include <vector> + +#include <boost/bind.hpp> +#include <boost/foreach.hpp> + +typedef boost::coroutines::symmetric_coroutine< void > coro_t; + +class merger +{ +private: + std::size_t max_; + std::vector< int > & to_; + + void run_( coro_t::yield_type & yield) + { + while ( idx < from.size() ) + { + if ( other->from[other->idx] < from[idx]) + yield( other->coro); + to_.push_back(from[idx++]); + } + while ( to_.size() < max_) + to_.push_back( other->from[other->idx++]); + } + + merger( merger const&); + merger & operator=( merger const&); + +public: + std::vector< int > const& from; + std::size_t idx; + merger * other; + coro_t::call_type coro; + + merger( std::vector< int > const& from_, std::vector< int > & to, std::size_t max) : + max_( max), + to_( to), + from( from_), + idx( 0), + other( 0), + coro( boost::bind( & merger::run_, this, _1) ) + {} + + void run() + { coro(); } +}; + +std::vector< int > merge( std::vector< int > const& a, std::vector< int > const& b) +{ + std::vector< int > c; + merger ma( a, c, a.size() + b. size() ); + merger mb( b, c, a.size() + b. size() ); + + ma.other = & mb; + mb.other = & ma; + + ma.run(); + + return c; +} + +void print( std::string const& name, std::vector< int > const& v) +{ + std::cout << name << " : "; + BOOST_FOREACH( int itm, v) + { std::cout << itm << " "; } + std::cout << "\n"; +} + +int main( int argc, char * argv[]) +{ + std::vector< int > a; + a.push_back( 1); + a.push_back( 5); + a.push_back( 6); + a.push_back( 10); + print( "a", a); + + std::vector< int > b; + b.push_back( 2); + b.push_back( 4); + b.push_back( 7); + b.push_back( 8); + b.push_back( 9); + b.push_back( 13); + print( "b", b); + + std::vector< int > c = merge( a, b); + print( "c", c); + + std::cout << "Done" << std::endl; + + return EXIT_SUCCESS; +} diff --git a/src/boost/libs/coroutine/example/symmetric/segmented_stack.cpp b/src/boost/libs/coroutine/example/symmetric/segmented_stack.cpp new file mode 100644 index 00000000..8f45c601 --- /dev/null +++ b/src/boost/libs/coroutine/example/symmetric/segmented_stack.cpp @@ -0,0 +1,67 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include <boost/coroutine/all.hpp> + +#include <iostream> + +#include <boost/assert.hpp> +#include <boost/config.hpp> +#include <boost/thread.hpp> + +int count = 384; + +#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); + } +} + +void foo( boost::coroutines::symmetric_coroutine< void >::yield_type &) +{ + bar( count); +} + +void thread_fn() +{ + { + boost::coroutines::symmetric_coroutine< void >::call_type coro( foo); + coro(); + } +} + +int main( int argc, char * argv[]) +{ +#if defined(BOOST_USE_SEGMENTED_STACKS) + std::cout << "using segmented stacks: allocates " << count << " * 4kB == " << 4 * count << "kB on stack, "; + std::cout << "initial stack size = " << boost::coroutines::stack_allocator::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::coroutines::stack_allocator::traits_type::default_size() / 1024 << "kB" << std::endl; + std::cout << "application might fail" << std::endl; +#endif + + boost::thread( thread_fn).join(); + + return 0; +} diff --git a/src/boost/libs/coroutine/example/symmetric/simple.cpp b/src/boost/libs/coroutine/example/symmetric/simple.cpp new file mode 100644 index 00000000..9fbd9ec6 --- /dev/null +++ b/src/boost/libs/coroutine/example/symmetric/simple.cpp @@ -0,0 +1,47 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include <boost/coroutine/all.hpp> + +#include <cstdlib> +#include <iostream> + +#include <boost/bind.hpp> + +typedef boost::coroutines::symmetric_coroutine< void > coro_t; + +coro_t::call_type * c1 = 0; +coro_t::call_type * c2 = 0; + +void foo( coro_t::yield_type & yield) +{ + std::cout << "foo1" << std::endl; + yield( * c2); + std::cout << "foo2" << std::endl; + yield( * c2); + std::cout << "foo3" << std::endl; +} + +void bar( coro_t::yield_type & yield) +{ + std::cout << "bar1" << std::endl; + yield( * c1); + std::cout << "bar2" << std::endl; + yield( * c1); + std::cout << "bar3" << std::endl; +} + +int main( int argc, char * argv[]) +{ + coro_t::call_type coro1( foo); + coro_t::call_type coro2( bar); + c1 = & coro1; + c2 = & coro2; + coro1(); + std::cout << "Done" << std::endl; + + return EXIT_SUCCESS; +} diff --git a/src/boost/libs/coroutine/example/symmetric/unwind.cpp b/src/boost/libs/coroutine/example/symmetric/unwind.cpp new file mode 100644 index 00000000..2af574e6 --- /dev/null +++ b/src/boost/libs/coroutine/example/symmetric/unwind.cpp @@ -0,0 +1,51 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include <boost/coroutine/all.hpp> + +#include <cstdlib> +#include <iostream> + +#include <boost/bind.hpp> + +struct X : private boost::noncopyable +{ + X() { std::cout << "X()" << std::endl; } + ~X() { std::cout << "~X()" << std::endl; } +}; + +typedef boost::coroutines::symmetric_coroutine< void > coro_t; + +coro_t::call_type * c1 = 0; +coro_t::call_type * c2 = 0; + +void foo( coro_t::yield_type & yield) +{ + X x; + std::cout << "foo() entered" << std::endl; + yield( * c2); + yield( * c2); + std::cout << "foo() finished" << std::endl; +} + +void bar( coro_t::yield_type & yield) +{ + std::cout << "bar() entered" << std::endl; + yield( * c1); + std::cout << "bar() finished" << std::endl; +} + +int main( int argc, char * argv[]) +{ + coro_t::call_type coro1( foo); + coro_t::call_type coro2( bar); + c1 = & coro1; + c2 = & coro2; + coro1(); + std::cout << "Done" << std::endl; + + return EXIT_SUCCESS; +} diff --git a/src/boost/libs/coroutine/index.html b/src/boost/libs/coroutine/index.html new file mode 100644 index 00000000..0ade6cbd --- /dev/null +++ b/src/boost/libs/coroutine/index.html @@ -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> diff --git a/src/boost/libs/coroutine/meta/libraries.json b/src/boost/libs/coroutine/meta/libraries.json new file mode 100644 index 00000000..c6841d5b --- /dev/null +++ b/src/boost/libs/coroutine/meta/libraries.json @@ -0,0 +1,15 @@ +{ + "key": "coroutine", + "status": "deprecated", + "name": "Coroutine", + "authors": [ + "Oliver Kowalke" + ], + "description": "Coroutine library.", + "category": [ + "Concurrent" + ], + "maintainers": [ + "Oliver Kowalke <oliver.kowalke -at- gmail.com>" + ] +} diff --git a/src/boost/libs/coroutine/performance/asymmetric/Jamfile.v2 b/src/boost/libs/coroutine/performance/asymmetric/Jamfile.v2 new file mode 100644 index 00000000..2baab38d --- /dev/null +++ b/src/boost/libs/coroutine/performance/asymmetric/Jamfile.v2 @@ -0,0 +1,83 @@ + +# Copyright Oliver Kowalke 2009. +# Distributed under the Boost Software License, Version 1.0. +# (See accompanying file LICENSE_1_0.txt or copy at +# http://www.boost.org/LICENSE_1_0.txt) + +# For more information, see http://www.boost.org/ + +import common ; +import feature ; +import indirect ; +import modules ; +import os ; +import toolset ; + +project boost/coroutine/performance/asymmetric + : requirements + <library>/boost/chrono//boost_chrono + <library>/boost/context//boost_context + <library>/boost/coroutine//boost_coroutine + <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_create_prealloc + : sources + performance_create_prealloc.cpp + ; + +exe performance_switch + : sources + performance_switch.cpp + ; diff --git a/src/boost/libs/coroutine/performance/asymmetric/performance_create_prealloc.cpp b/src/boost/libs/coroutine/performance/asymmetric/performance_create_prealloc.cpp new file mode 100644 index 00000000..350d4399 --- /dev/null +++ b/src/boost/libs/coroutine/performance/asymmetric/performance_create_prealloc.cpp @@ -0,0 +1,116 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include <cstdlib> +#include <iostream> +#include <stdexcept> + +#include <boost/chrono.hpp> +#include <boost/coroutine/all.hpp> +#include <boost/cstdint.hpp> +#include <boost/program_options.hpp> + +#include "../bind_processor.hpp" +#include "../clock.hpp" +#include "../cycle.hpp" +#include "../preallocated_stack_allocator.hpp" + +typedef preallocated_stack_allocator stack_allocator; +typedef boost::coroutines::asymmetric_coroutine< void > coro_type; + +boost::coroutines::flag_fpu_t preserve_fpu = boost::coroutines::fpu_not_preserved; +boost::coroutines::flag_unwind_t unwind_stack = boost::coroutines::stack_unwind; +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( fn, + boost::coroutines::attributes( unwind_stack, preserve_fpu), + stack_alloc); + } + 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( fn, + boost::coroutines::attributes( unwind_stack, preserve_fpu), + stack_alloc); + } + 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") + ("unwind,u", boost::program_options::value< bool >( & unwind), "unwind coroutine-stack") + ("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 ( preserve) preserve_fpu = boost::coroutines::fpu_preserved; + if ( ! unwind) unwind_stack = boost::coroutines::no_stack_unwind; + 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; +} diff --git a/src/boost/libs/coroutine/performance/asymmetric/performance_create_protected.cpp b/src/boost/libs/coroutine/performance/asymmetric/performance_create_protected.cpp new file mode 100644 index 00000000..90f46a45 --- /dev/null +++ b/src/boost/libs/coroutine/performance/asymmetric/performance_create_protected.cpp @@ -0,0 +1,113 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include <cstdlib> +#include <iostream> +#include <stdexcept> + +#include <boost/chrono.hpp> +#include <boost/coroutine/all.hpp> +#include <boost/cstdint.hpp> +#include <boost/program_options.hpp> + +#include "../bind_processor.hpp" +#include "../clock.hpp" +#include "../cycle.hpp" + +typedef boost::coroutines::protected_stack_allocator stack_allocator; +typedef boost::coroutines::asymmetric_coroutine< void > coro_type; + +boost::coroutines::flag_fpu_t preserve_fpu = boost::coroutines::fpu_not_preserved; +boost::coroutines::flag_unwind_t unwind_stack = boost::coroutines::stack_unwind; +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( fn, + boost::coroutines::attributes( unwind_stack, preserve_fpu), stack_alloc); + } + 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( fn, + boost::coroutines::attributes( unwind_stack, preserve_fpu), stack_alloc); + } + 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") + ("unwind,u", boost::program_options::value< bool >( & unwind), "unwind coroutine-stack") + ("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 ( preserve) preserve_fpu = boost::coroutines::fpu_preserved; + if ( ! unwind) unwind_stack = boost::coroutines::no_stack_unwind; + 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; +} diff --git a/src/boost/libs/coroutine/performance/asymmetric/performance_create_standard.cpp b/src/boost/libs/coroutine/performance/asymmetric/performance_create_standard.cpp new file mode 100644 index 00000000..6736a1e7 --- /dev/null +++ b/src/boost/libs/coroutine/performance/asymmetric/performance_create_standard.cpp @@ -0,0 +1,113 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include <cstdlib> +#include <iostream> +#include <stdexcept> + +#include <boost/chrono.hpp> +#include <boost/coroutine/all.hpp> +#include <boost/cstdint.hpp> +#include <boost/program_options.hpp> + +#include "../bind_processor.hpp" +#include "../clock.hpp" +#include "../cycle.hpp" + +typedef boost::coroutines::standard_stack_allocator stack_allocator; +typedef boost::coroutines::asymmetric_coroutine< void > coro_type; + +boost::coroutines::flag_fpu_t preserve_fpu = boost::coroutines::fpu_not_preserved; +boost::coroutines::flag_unwind_t unwind_stack = boost::coroutines::stack_unwind; +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( fn, + boost::coroutines::attributes( unwind_stack, preserve_fpu), stack_alloc); + } + 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( fn, + boost::coroutines::attributes( unwind_stack, preserve_fpu), stack_alloc); + } + 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") + ("unwind,u", boost::program_options::value< bool >( & unwind), "unwind coroutine-stack") + ("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 ( preserve) preserve_fpu = boost::coroutines::fpu_preserved; + if ( ! unwind) unwind_stack = boost::coroutines::no_stack_unwind; + 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; +} diff --git a/src/boost/libs/coroutine/performance/asymmetric/performance_switch.cpp b/src/boost/libs/coroutine/performance/asymmetric/performance_switch.cpp new file mode 100644 index 00000000..c99946b4 --- /dev/null +++ b/src/boost/libs/coroutine/performance/asymmetric/performance_switch.cpp @@ -0,0 +1,205 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include <cstdlib> +#include <iostream> +#include <stdexcept> +#include <string> + +#include <boost/chrono.hpp> +#include <boost/coroutine/all.hpp> +#include <boost/cstdint.hpp> +#include <boost/program_options.hpp> + +#include "../bind_processor.hpp" +#include "../clock.hpp" +#include "../cycle.hpp" + +boost::coroutines::flag_fpu_t preserve_fpu = boost::coroutines::fpu_not_preserved; +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::coroutines::asymmetric_coroutine< void >::push_type & c) +{ while ( true) c(); } + +void fn_int( boost::coroutines::asymmetric_coroutine< int >::push_type & c) +{ while ( true) c( 7); } + +void fn_x( boost::coroutines::asymmetric_coroutine< X >::push_type & c) +{ + while ( true) c( x); +} + +duration_type measure_time_void( duration_type overhead) +{ + boost::coroutines::asymmetric_coroutine< void >::pull_type c( fn_void, + boost::coroutines::attributes( preserve_fpu) ); + + 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::coroutines::asymmetric_coroutine< int >::pull_type c( fn_int, + boost::coroutines::attributes( preserve_fpu) ); + + 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::coroutines::asymmetric_coroutine< X >::pull_type c( fn_x, + boost::coroutines::attributes( preserve_fpu) ); + + 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::coroutines::asymmetric_coroutine< void >::pull_type c( fn_void, + boost::coroutines::attributes( preserve_fpu) ); + + 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::coroutines::asymmetric_coroutine< int >::pull_type c( fn_int, + boost::coroutines::attributes( preserve_fpu) ); + + 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::coroutines::asymmetric_coroutine< X >::pull_type c( fn_x, + boost::coroutines::attributes( preserve_fpu) ); + + 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 preserve = false, 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 ( preserve) preserve_fpu = boost::coroutines::fpu_preserved; + 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; +} diff --git a/src/boost/libs/coroutine/performance/asymmetric/segmented/Jamfile.v2 b/src/boost/libs/coroutine/performance/asymmetric/segmented/Jamfile.v2 new file mode 100644 index 00000000..073f7e7a --- /dev/null +++ b/src/boost/libs/coroutine/performance/asymmetric/segmented/Jamfile.v2 @@ -0,0 +1,68 @@ + +# Copyright Oliver Kowalke 2009. +# Distributed under the Boost Software License, Version 1.0. +# (See accompanying file LICENSE_1_0.txt or copy at +# http://www.boost.org/LICENSE_1_0.txt) + +# For more information, see http://www.boost.org/ + +import common ; +import feature ; +import indirect ; +import modules ; +import os ; +import toolset ; + +project boost/coroutine/performance/segmented + : requirements + <library>/boost/chrono//boost_chrono + <library>/boost/context//boost_context + <library>/boost/coroutine//boost_coroutine + <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_segmented + : sources + performance_create_segmented.cpp + ; diff --git a/src/boost/libs/coroutine/performance/asymmetric/segmented/performance_create_segmented.cpp b/src/boost/libs/coroutine/performance/asymmetric/segmented/performance_create_segmented.cpp new file mode 100644 index 00000000..c174e685 --- /dev/null +++ b/src/boost/libs/coroutine/performance/asymmetric/segmented/performance_create_segmented.cpp @@ -0,0 +1,106 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include <cstdlib> +#include <iostream> +#include <stdexcept> + +#include <boost/chrono.hpp> +#include <boost/coroutine/all.hpp> +#include <boost/cstdint.hpp> +#include <boost/program_options.hpp> + +#include "../../bind_processor.hpp" +#include "../../clock.hpp" +#include "../../cycle.hpp" + +boost::coroutines::flag_fpu_t preserve_fpu = boost::coroutines::fpu_not_preserved; +boost::coroutines::flag_unwind_t unwind_stack = boost::coroutines::stack_unwind; +boost::uint64_t jobs = 1000; + +void fn( boost::coroutines::asymmetric_coroutine< void >::push_type & c) +{ while ( true) c(); } + +duration_type measure_time( duration_type overhead) +{ + time_point_type start( clock_type::now() ); + for ( std::size_t i = 0; i < jobs; ++i) { + boost::coroutines::asymmetric_coroutine< void >::pull_type c( fn, + boost::coroutines::attributes( unwind_stack, preserve_fpu) ); + } + 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) +{ + cycle_type start( cycles() ); + for ( std::size_t i = 0; i < jobs; ++i) { + boost::coroutines::asymmetric_coroutine< void >::pull_type c( fn, + boost::coroutines::attributes( unwind_stack, preserve_fpu) ); + } + 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") + ("unwind,u", boost::program_options::value< bool >( & unwind), "unwind coroutine-stack") + ("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 ( preserve) preserve_fpu = boost::coroutines::fpu_preserved; + if ( ! unwind) unwind_stack = boost::coroutines::no_stack_unwind; + 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; +} diff --git a/src/boost/libs/coroutine/performance/bind_processor.hpp b/src/boost/libs/coroutine/performance/bind_processor.hpp new file mode 100644 index 00000000..f89b5074 --- /dev/null +++ b/src/boost/libs/coroutine/performance/bind_processor.hpp @@ -0,0 +1,12 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BIND_TO_PROCESSOR_H +#define BIND_TO_PROCESSOR_H + +void bind_to_processor( unsigned int n); + +#endif // BIND_TO_PROCESSOR_H diff --git a/src/boost/libs/coroutine/performance/bind_processor_aix.cpp b/src/boost/libs/coroutine/performance/bind_processor_aix.cpp new file mode 100644 index 00000000..8c477fca --- /dev/null +++ b/src/boost/libs/coroutine/performance/bind_processor_aix.cpp @@ -0,0 +1,25 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#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> diff --git a/src/boost/libs/coroutine/performance/bind_processor_freebsd.cpp b/src/boost/libs/coroutine/performance/bind_processor_freebsd.cpp new file mode 100644 index 00000000..7e28b706 --- /dev/null +++ b/src/boost/libs/coroutine/performance/bind_processor_freebsd.cpp @@ -0,0 +1,29 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#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> diff --git a/src/boost/libs/coroutine/performance/bind_processor_hpux.cpp b/src/boost/libs/coroutine/performance/bind_processor_hpux.cpp new file mode 100644 index 00000000..af33c112 --- /dev/null +++ b/src/boost/libs/coroutine/performance/bind_processor_hpux.cpp @@ -0,0 +1,31 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#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> diff --git a/src/boost/libs/coroutine/performance/bind_processor_linux.cpp b/src/boost/libs/coroutine/performance/bind_processor_linux.cpp new file mode 100644 index 00000000..3fb95881 --- /dev/null +++ b/src/boost/libs/coroutine/performance/bind_processor_linux.cpp @@ -0,0 +1,30 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#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> diff --git a/src/boost/libs/coroutine/performance/bind_processor_solaris.cpp b/src/boost/libs/coroutine/performance/bind_processor_solaris.cpp new file mode 100644 index 00000000..0830c1b4 --- /dev/null +++ b/src/boost/libs/coroutine/performance/bind_processor_solaris.cpp @@ -0,0 +1,26 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#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> diff --git a/src/boost/libs/coroutine/performance/bind_processor_windows.cpp b/src/boost/libs/coroutine/performance/bind_processor_windows.cpp new file mode 100644 index 00000000..6fefbc65 --- /dev/null +++ b/src/boost/libs/coroutine/performance/bind_processor_windows.cpp @@ -0,0 +1,24 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#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> diff --git a/src/boost/libs/coroutine/performance/clock.hpp b/src/boost/libs/coroutine/performance/clock.hpp new file mode 100644 index 00000000..6103951f --- /dev/null +++ b/src/boost/libs/coroutine/performance/clock.hpp @@ -0,0 +1,44 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) +// +#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(); + } +}; + +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 diff --git a/src/boost/libs/coroutine/performance/cycle.hpp b/src/boost/libs/coroutine/performance/cycle.hpp new file mode 100644 index 00000000..c26c8598 --- /dev/null +++ b/src/boost/libs/coroutine/performance/cycle.hpp @@ -0,0 +1,26 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#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 diff --git a/src/boost/libs/coroutine/performance/cycle_i386.hpp b/src/boost/libs/coroutine/performance/cycle_i386.hpp new file mode 100644 index 00000000..a3eb7039 --- /dev/null +++ b/src/boost/libs/coroutine/performance/cycle_i386.hpp @@ -0,0 +1,83 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#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 diff --git a/src/boost/libs/coroutine/performance/cycle_x86-64.hpp b/src/boost/libs/coroutine/performance/cycle_x86-64.hpp new file mode 100644 index 00000000..fae0226c --- /dev/null +++ b/src/boost/libs/coroutine/performance/cycle_x86-64.hpp @@ -0,0 +1,79 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#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 diff --git a/src/boost/libs/coroutine/performance/preallocated_stack_allocator.hpp b/src/boost/libs/coroutine/performance/preallocated_stack_allocator.hpp new file mode 100644 index 00000000..3651ae1b --- /dev/null +++ b/src/boost/libs/coroutine/performance/preallocated_stack_allocator.hpp @@ -0,0 +1,56 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_FIBER_PREALLOCATED_STACK_ALLOCATOR_H +#define BOOST_FIBER_PREALLOCATED_STACK_ALLOCATOR_H + +#include <cstddef> +#include <cstdlib> +#include <stdexcept> +#include <vector> + +#include <boost/assert.hpp> +#include <boost/config.hpp> +#include <boost/coroutine/standard_stack_allocator.hpp> +#include <boost/coroutine/stack_context.hpp> +#include <boost/shared_ptr.hpp> +#include <boost/utility.hpp> + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_PREFIX +#endif + +class preallocated_stack_allocator +{ +private: + boost::coroutines::stack_context stack_ctx_; + +public: + preallocated_stack_allocator() : + stack_ctx_() + { + boost::coroutines::standard_stack_allocator allocator; + allocator.allocate( + stack_ctx_, + boost::coroutines::standard_stack_allocator::traits_type::default_size() ); + } + + void allocate( boost::coroutines::stack_context & ctx, std::size_t size) + { + ctx.sp = stack_ctx_.sp; + ctx.size = stack_ctx_.size; + } + + void deallocate( boost::coroutines::stack_context & ctx) + { + } +}; + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_SUFFIX +#endif + +#endif // BOOST_FIBER_PREALLOCATED_STACK_ALLOCATOR_H diff --git a/src/boost/libs/coroutine/performance/symmetric/Jamfile.v2 b/src/boost/libs/coroutine/performance/symmetric/Jamfile.v2 new file mode 100644 index 00000000..585b03c4 --- /dev/null +++ b/src/boost/libs/coroutine/performance/symmetric/Jamfile.v2 @@ -0,0 +1,83 @@ + +# Copyright Oliver Kowalke 2009. +# Distributed under the Boost Software License, Version 1.0. +# (See accompanying file LICENSE_1_0.txt or copy at +# http://www.boost.org/LICENSE_1_0.txt) + +# For more information, see http://www.boost.org/ + +import common ; +import feature ; +import indirect ; +import modules ; +import os ; +import toolset ; + +project boost/coroutine/performance/symmetric + : requirements + <library>/boost/chrono//boost_chrono + <library>/boost/context//boost_context + <library>/boost/coroutine//boost_coroutine + <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_create_prealloc + : sources + performance_create_prealloc.cpp + ; + +exe performance_switch + : sources + performance_switch.cpp + ; diff --git a/src/boost/libs/coroutine/performance/symmetric/performance_create_prealloc.cpp b/src/boost/libs/coroutine/performance/symmetric/performance_create_prealloc.cpp new file mode 100644 index 00000000..bfc549f9 --- /dev/null +++ b/src/boost/libs/coroutine/performance/symmetric/performance_create_prealloc.cpp @@ -0,0 +1,114 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include <cstdlib> +#include <iostream> +#include <stdexcept> + +#include <boost/chrono.hpp> +#include <boost/coroutine/all.hpp> +#include <boost/cstdint.hpp> +#include <boost/program_options.hpp> + +#include "../bind_processor.hpp" +#include "../clock.hpp" +#include "../cycle.hpp" +#include "../preallocated_stack_allocator.hpp" + +typedef preallocated_stack_allocator stack_allocator; +typedef boost::coroutines::symmetric_coroutine< void > coro_type; + +boost::coroutines::flag_fpu_t preserve_fpu = boost::coroutines::fpu_not_preserved; +boost::coroutines::flag_unwind_t unwind_stack = boost::coroutines::stack_unwind; +boost::uint64_t jobs = 1000; + +void fn( coro_type::yield_type &) {} + +duration_type measure_time( duration_type overhead) +{ + stack_allocator stack_alloc; + boost::coroutines::attributes attrs( unwind_stack, preserve_fpu); + + time_point_type start( clock_type::now() ); + for ( std::size_t i = 0; i < jobs; ++i) { + coro_type::call_type c( fn, attrs, stack_alloc); + } + duration_type total = clock_type::now() - start; + total -= overhead; // 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::call_type c( fn, + boost::coroutines::attributes( unwind_stack, preserve_fpu), + stack_alloc); + } + 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") + ("unwind,u", boost::program_options::value< bool >( & unwind), "unwind coroutine-stack") + ("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 ( preserve) preserve_fpu = boost::coroutines::fpu_preserved; + if ( ! unwind) unwind_stack = boost::coroutines::no_stack_unwind; + 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; +} diff --git a/src/boost/libs/coroutine/performance/symmetric/performance_create_protected.cpp b/src/boost/libs/coroutine/performance/symmetric/performance_create_protected.cpp new file mode 100644 index 00000000..eb6520e7 --- /dev/null +++ b/src/boost/libs/coroutine/performance/symmetric/performance_create_protected.cpp @@ -0,0 +1,112 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include <cstdlib> +#include <iostream> +#include <stdexcept> + +#include <boost/chrono.hpp> +#include <boost/coroutine/all.hpp> +#include <boost/cstdint.hpp> +#include <boost/program_options.hpp> + +#include "../bind_processor.hpp" +#include "../clock.hpp" +#include "../cycle.hpp" + +typedef boost::coroutines::protected_stack_allocator stack_allocator; +typedef boost::coroutines::symmetric_coroutine< void > coro_type; + +boost::coroutines::flag_fpu_t preserve_fpu = boost::coroutines::fpu_not_preserved; +boost::coroutines::flag_unwind_t unwind_stack = boost::coroutines::stack_unwind; +boost::uint64_t jobs = 1000; + +void fn( coro_type::yield_type &) {} + +duration_type measure_time( duration_type overhead) +{ + stack_allocator stack_alloc; + boost::coroutines::attributes attrs( unwind_stack, preserve_fpu); + + time_point_type start( clock_type::now() ); + for ( std::size_t i = 0; i < jobs; ++i) { + coro_type::call_type c( fn, attrs, stack_alloc); + } + duration_type total = clock_type::now() - start; + total -= overhead; // 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::call_type c( fn, + boost::coroutines::attributes( unwind_stack, preserve_fpu), stack_alloc); + } + 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") + ("unwind,u", boost::program_options::value< bool >( & unwind), "unwind coroutine-stack") + ("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 ( preserve) preserve_fpu = boost::coroutines::fpu_preserved; + if ( ! unwind) unwind_stack = boost::coroutines::no_stack_unwind; + 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; +} diff --git a/src/boost/libs/coroutine/performance/symmetric/performance_create_standard.cpp b/src/boost/libs/coroutine/performance/symmetric/performance_create_standard.cpp new file mode 100644 index 00000000..91df005b --- /dev/null +++ b/src/boost/libs/coroutine/performance/symmetric/performance_create_standard.cpp @@ -0,0 +1,112 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include <cstdlib> +#include <iostream> +#include <stdexcept> + +#include <boost/chrono.hpp> +#include <boost/coroutine/all.hpp> +#include <boost/cstdint.hpp> +#include <boost/program_options.hpp> + +#include "../bind_processor.hpp" +#include "../clock.hpp" +#include "../cycle.hpp" + +typedef boost::coroutines::standard_stack_allocator stack_allocator; +typedef boost::coroutines::symmetric_coroutine< void > coro_type; + +boost::coroutines::flag_fpu_t preserve_fpu = boost::coroutines::fpu_not_preserved; +boost::coroutines::flag_unwind_t unwind_stack = boost::coroutines::stack_unwind; +boost::uint64_t jobs = 1000; + +void fn( coro_type::yield_type &) {} + +duration_type measure_time( duration_type overhead) +{ + stack_allocator stack_alloc; + boost::coroutines::attributes attrs( unwind_stack, preserve_fpu); + + time_point_type start( clock_type::now() ); + for ( std::size_t i = 0; i < jobs; ++i) { + coro_type::call_type c( fn, attrs, stack_alloc); + } + duration_type total = clock_type::now() - start; + total -= overhead; // 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::call_type c( fn, + boost::coroutines::attributes( unwind_stack, preserve_fpu), stack_alloc); + } + 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") + ("unwind,u", boost::program_options::value< bool >( & unwind), "unwind coroutine-stack") + ("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 ( preserve) preserve_fpu = boost::coroutines::fpu_preserved; + if ( ! unwind) unwind_stack = boost::coroutines::no_stack_unwind; + 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; +} diff --git a/src/boost/libs/coroutine/performance/symmetric/performance_switch.cpp b/src/boost/libs/coroutine/performance/symmetric/performance_switch.cpp new file mode 100644 index 00000000..c97b7a62 --- /dev/null +++ b/src/boost/libs/coroutine/performance/symmetric/performance_switch.cpp @@ -0,0 +1,207 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include <cstdlib> +#include <iostream> +#include <stdexcept> +#include <string> + +#include <boost/chrono.hpp> +#include <boost/coroutine/all.hpp> +#include <boost/cstdint.hpp> +#include <boost/program_options.hpp> + +#include "../bind_processor.hpp" +#include "../clock.hpp" +#include "../cycle.hpp" + +boost::coroutines::flag_fpu_t preserve_fpu = boost::coroutines::fpu_not_preserved; +boost::uint64_t jobs = 1000; +time_point_type end; + +struct X +{ + std::string str; + + X( std::string const& str_) : + str( str_) + {} +}; + +const X x("abc"); + +void fn_void( boost::coroutines::symmetric_coroutine< void >::yield_type & yield) +{ while( true) yield(); } + +void fn_int( boost::coroutines::symmetric_coroutine< int >::yield_type & yield) +{ while( true) yield(); } + +void fn_x( boost::coroutines::symmetric_coroutine< X >::yield_type & yield) +{ while( true) yield(); } + +duration_type measure_time_void( duration_type overhead) +{ + boost::coroutines::symmetric_coroutine< void >::call_type c( fn_void, + boost::coroutines::attributes( preserve_fpu) ); + c(); + + 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::coroutines::symmetric_coroutine< int >::call_type c( fn_int, + boost::coroutines::attributes( preserve_fpu) ); + + time_point_type start( clock_type::now() ); + for ( std::size_t i = 0; i < jobs; ++i) { + c( i); + } + 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::coroutines::symmetric_coroutine< X >::call_type c( fn_x, + boost::coroutines::attributes( preserve_fpu) ); + + X x("abc"); + time_point_type start( clock_type::now() ); + for ( std::size_t i = 0; i < jobs; ++i) { + c( x); + } + 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::coroutines::symmetric_coroutine< void >::call_type c( fn_void, + boost::coroutines::attributes( preserve_fpu) ); + + 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::coroutines::symmetric_coroutine< int >::call_type c( fn_int, + boost::coroutines::attributes( preserve_fpu) ); + + cycle_type start( cycles() ); + for ( std::size_t i = 0; i < jobs; ++i) { + c( i); + } + 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::coroutines::symmetric_coroutine< X >::call_type c( fn_x, + boost::coroutines::attributes( preserve_fpu) ); + + X x("abc"); + cycle_type start( cycles() ); + for ( std::size_t i = 0; i < jobs; ++i) { + c( x); + } + 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 preserve = false, 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 ( preserve) preserve_fpu = boost::coroutines::fpu_preserved; + 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; +} diff --git a/src/boost/libs/coroutine/performance/symmetric/segmented/Jamfile.v2 b/src/boost/libs/coroutine/performance/symmetric/segmented/Jamfile.v2 new file mode 100644 index 00000000..073f7e7a --- /dev/null +++ b/src/boost/libs/coroutine/performance/symmetric/segmented/Jamfile.v2 @@ -0,0 +1,68 @@ + +# Copyright Oliver Kowalke 2009. +# Distributed under the Boost Software License, Version 1.0. +# (See accompanying file LICENSE_1_0.txt or copy at +# http://www.boost.org/LICENSE_1_0.txt) + +# For more information, see http://www.boost.org/ + +import common ; +import feature ; +import indirect ; +import modules ; +import os ; +import toolset ; + +project boost/coroutine/performance/segmented + : requirements + <library>/boost/chrono//boost_chrono + <library>/boost/context//boost_context + <library>/boost/coroutine//boost_coroutine + <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_segmented + : sources + performance_create_segmented.cpp + ; diff --git a/src/boost/libs/coroutine/performance/symmetric/segmented/performance_create_segmented.cpp b/src/boost/libs/coroutine/performance/symmetric/segmented/performance_create_segmented.cpp new file mode 100644 index 00000000..8d1e8c1a --- /dev/null +++ b/src/boost/libs/coroutine/performance/symmetric/segmented/performance_create_segmented.cpp @@ -0,0 +1,105 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include <cstdlib> +#include <iostream> +#include <stdexcept> + +#include <boost/chrono.hpp> +#include <boost/coroutine/all.hpp> +#include <boost/cstdint.hpp> +#include <boost/program_options.hpp> + +#include "../../bind_processor.hpp" +#include "../../clock.hpp" +#include "../../cycle.hpp" + +boost::coroutines::flag_fpu_t preserve_fpu = boost::coroutines::fpu_not_preserved; +boost::coroutines::flag_unwind_t unwind_stack = boost::coroutines::stack_unwind; +boost::uint64_t jobs = 1000; + +void fn( boost::coroutines::symmetric_coroutine< void >::yield_type &) {} + +duration_type measure_time( duration_type overhead) +{ + time_point_type start( clock_type::now() ); + for ( std::size_t i = 0; i < jobs; ++i) { + boost::coroutines::symmetric_coroutine< void >::call_type c( fn, + boost::coroutines::attributes( unwind_stack, preserve_fpu) ); + } + duration_type total = clock_type::now() - start; + total -= overhead; // overhead of measurement + total /= jobs; // loops + + return total; +} + +# ifdef BOOST_CONTEXT_CYCLE +cycle_type measure_cycles( cycle_type overhead) +{ + cycle_type start( cycles() ); + for ( std::size_t i = 0; i < jobs; ++i) { + boost::coroutines::symmetric_coroutine< void >::call_type c( fn, + boost::coroutines::attributes( unwind_stack, preserve_fpu) ); + } + 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") + ("unwind,u", boost::program_options::value< bool >( & unwind), "unwind coroutine-stack") + ("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 ( preserve) preserve_fpu = boost::coroutines::fpu_preserved; + if ( ! unwind) unwind_stack = boost::coroutines::no_stack_unwind; + 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; +} diff --git a/src/boost/libs/coroutine/src/detail/coroutine_context.cpp b/src/boost/libs/coroutine/src/detail/coroutine_context.cpp new file mode 100644 index 00000000..6a6d5248 --- /dev/null +++ b/src/boost/libs/coroutine/src/detail/coroutine_context.cpp @@ -0,0 +1,82 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include "boost/coroutine/detail/coroutine_context.hpp" + +#include "boost/coroutine/detail/data.hpp" + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_PREFIX +#endif + +#if defined(_MSC_VER) +# pragma warning(push) +# pragma warning(disable:4355) +#endif + +#if defined(BOOST_USE_SEGMENTED_STACKS) +extern "C" { + +void __splitstack_getcontext( void * [BOOST_CONTEXT_SEGMENTS]); + +void __splitstack_setcontext( void * [BOOST_CONTEXT_SEGMENTS]); + +} +#endif + +namespace boost { +namespace coroutines { +namespace detail { + +coroutine_context::coroutine_context() : + palloc_(), + ctx_( 0) +{} + +coroutine_context::coroutine_context( ctx_fn fn, preallocated const& palloc) : + palloc_( palloc), + ctx_( context::detail::make_fcontext( palloc_.sp, palloc_.size, fn) ) +{} + +coroutine_context::coroutine_context( coroutine_context const& other) : + palloc_( other.palloc_), + ctx_( other.ctx_) +{} + +coroutine_context & +coroutine_context::operator=( coroutine_context const& other) +{ + if ( this == & other) return * this; + + palloc_ = other.palloc_; + ctx_ = other.ctx_; + + return * this; +} + +void * +coroutine_context::jump( coroutine_context & other, void * param) +{ +#if defined(BOOST_USE_SEGMENTED_STACKS) + __splitstack_getcontext( palloc_.sctx.segments_ctx); + __splitstack_setcontext( other.palloc_.sctx.segments_ctx); +#endif + data_t data = { this, param }; + context::detail::transfer_t t = context::detail::jump_fcontext( other.ctx_, & data); + data_t * ret = static_cast< data_t * >( t.data); + ret->from->ctx_ = t.fctx; + return ret->data; +} + +}}} + +#if defined(_MSC_VER) +# pragma warning(pop) +#endif + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_SUFFIX +#endif diff --git a/src/boost/libs/coroutine/src/exceptions.cpp b/src/boost/libs/coroutine/src/exceptions.cpp new file mode 100644 index 00000000..d32eb546 --- /dev/null +++ b/src/boost/libs/coroutine/src/exceptions.cpp @@ -0,0 +1,37 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include "boost/coroutine/exceptions.hpp" + +namespace boost { +namespace coroutines { + +class coroutine_error_category : public system::error_category +{ +public: + virtual const char* name() const BOOST_NOEXCEPT + { return "coroutine"; } + + virtual std::string message( int ev) const + { + switch (BOOST_SCOPED_ENUM_NATIVE(coroutine_errc)(ev)) + { + case coroutine_errc::no_data: + return std::string("Operation not permitted because coroutine " + "has no valid result."); + } + return std::string("unspecified coroutine_errc value\n"); + } +}; + +BOOST_COROUTINES_DECL +system::error_category const& coroutine_category() BOOST_NOEXCEPT +{ + static coroutines::coroutine_error_category cat; + return cat; +} + +}} diff --git a/src/boost/libs/coroutine/src/posix/stack_traits.cpp b/src/boost/libs/coroutine/src/posix/stack_traits.cpp new file mode 100644 index 00000000..b6299432 --- /dev/null +++ b/src/boost/libs/coroutine/src/posix/stack_traits.cpp @@ -0,0 +1,112 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include "boost/coroutine/stack_traits.hpp" + +extern "C" { +#include <signal.h> +#include <sys/resource.h> +#include <sys/time.h> +#include <unistd.h> +} + +//#if _POSIX_C_SOURCE >= 200112L + +#include <algorithm> +#include <cmath> + +#include <boost/assert.hpp> +#include <boost/thread.hpp> + +#if !defined (SIGSTKSZ) +# define SIGSTKSZ (8 * 1024) +# define UDEF_SIGSTKSZ +#endif + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_PREFIX +#endif + +namespace { + +void pagesize_( std::size_t * size) +{ + // conform to POSIX.1-2001 + * size = ::sysconf( _SC_PAGESIZE); +} + +void stacksize_limit_( rlimit * limit) +{ + // conforming to POSIX.1-2001 +#if defined(BOOST_DISABLE_ASSERTS) || defined(NDEBUG) + ::getrlimit( RLIMIT_STACK, limit); +#else + const int result = ::getrlimit( RLIMIT_STACK, limit); + BOOST_ASSERT( 0 == result); +#endif +} + +std::size_t pagesize() +{ + static std::size_t size = 0; + static boost::once_flag flag; + boost::call_once( flag, pagesize_, & size); + return size; +} + +rlimit stacksize_limit() +{ + static rlimit limit; + static boost::once_flag flag; + boost::call_once( flag, stacksize_limit_, & limit); + return limit; +} + +} + +namespace boost { +namespace coroutines { + +bool +stack_traits::is_unbounded() BOOST_NOEXCEPT +{ return RLIM_INFINITY == stacksize_limit().rlim_max; } + +std::size_t +stack_traits::page_size() BOOST_NOEXCEPT +{ return pagesize(); } + +std::size_t +stack_traits::default_size() BOOST_NOEXCEPT +{ + std::size_t size = 8 * minimum_size(); + if ( is_unbounded() ) return size; + + BOOST_ASSERT( maximum_size() >= minimum_size() ); + return maximum_size() == size + ? size + : (std::min)( size, maximum_size() ); +} + +std::size_t +stack_traits::minimum_size() BOOST_NOEXCEPT +{ return SIGSTKSZ; } + +std::size_t +stack_traits::maximum_size() BOOST_NOEXCEPT +{ + BOOST_ASSERT( ! is_unbounded() ); + return static_cast< std::size_t >( stacksize_limit().rlim_max); +} + +}} + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_SUFFIX +#endif + +#ifdef UDEF_SIGSTKSZ +# undef SIGSTKSZ +#endif diff --git a/src/boost/libs/coroutine/src/windows/stack_traits.cpp b/src/boost/libs/coroutine/src/windows/stack_traits.cpp new file mode 100644 index 00000000..bdf417a7 --- /dev/null +++ b/src/boost/libs/coroutine/src/windows/stack_traits.cpp @@ -0,0 +1,114 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include "boost/coroutine/stack_traits.hpp" + +extern "C" { +#include <windows.h> +} + +//#if defined (BOOST_WINDOWS) || _POSIX_C_SOURCE >= 200112L + +#include <algorithm> +#include <cmath> +#include <cstddef> +#include <cstring> +#include <stdexcept> + +#include <boost/assert.hpp> +#include <boost/coroutine/detail/config.hpp> +#include <boost/thread.hpp> + +#include <boost/coroutine/stack_context.hpp> + +// 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) + +// Windows seams not to provide a constant or function +// telling the minimal stacksize +# define MIN_STACKSIZE 8 * 1024 +#else +# define MIN_STACKSIZE 4 * 1024 +#endif + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_PREFIX +#endif + +namespace { + +void system_info_( SYSTEM_INFO * si) +{ ::GetSystemInfo( si); } + +SYSTEM_INFO system_info() +{ + static SYSTEM_INFO si; + static boost::once_flag flag; + boost::call_once( flag, static_cast< void(*)( SYSTEM_INFO *) >( system_info_), & si); + return si; +} + +std::size_t pagesize() +{ return static_cast< std::size_t >( system_info().dwPageSize); } + +std::size_t page_count( std::size_t stacksize) +{ + return static_cast< std::size_t >( + std::floor( + static_cast< float >( stacksize) / pagesize() ) ); +} + +} + +namespace boost { +namespace coroutines { + +// Windows seams not to provide a limit for the stacksize +// libcoco uses 32k+4k bytes as minimum +bool +stack_traits::is_unbounded() BOOST_NOEXCEPT +{ return true; } + +std::size_t +stack_traits::page_size() BOOST_NOEXCEPT +{ return pagesize(); } + +std::size_t +stack_traits::default_size() BOOST_NOEXCEPT +{ + std::size_t size = 64 * 1024; // 64 kB + if ( is_unbounded() ) + return (std::max)( size, minimum_size() ); + + BOOST_ASSERT( maximum_size() >= minimum_size() ); + return maximum_size() == minimum_size() + ? minimum_size() + : ( std::min)( size, maximum_size() ); +} + +// because Windows seams not to provide a limit for minimum stacksize +std::size_t +stack_traits::minimum_size() BOOST_NOEXCEPT +{ return MIN_STACKSIZE; } + +// because Windows seams not to provide a limit for maximum stacksize +// maximum_size() can never be called (pre-condition ! is_unbounded() ) +std::size_t +stack_traits::maximum_size() BOOST_NOEXCEPT +{ + BOOST_ASSERT( ! is_unbounded() ); + return 1 * 1024 * 1024 * 1024; // 1GB +} + +}} + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_SUFFIX +#endif diff --git a/src/boost/libs/coroutine/test/Jamfile.v2 b/src/boost/libs/coroutine/test/Jamfile.v2 new file mode 100644 index 00000000..3481e0f7 --- /dev/null +++ b/src/boost/libs/coroutine/test/Jamfile.v2 @@ -0,0 +1,33 @@ +# Boost.Coroutine Library Tests Jamfile + +# Copyright Oliver Kowalke 2009. +# Distributed under the Boost Software License, Version 1.0. +# (See accompanying file LICENSE_1_0.txt or copy at +# http://www.boost.org/LICENSE_1_0.txt) + +import common ; +import feature ; +import indirect ; +import modules ; +import os ; +import testing ; +import toolset ; + +project boost/coroutine/test + : requirements + <library>/boost/context//boost_context + <library>/boost/coroutine//boost_coroutine + <library>/boost/program_options//boost_program_options + <library>/boost/test///boost_unit_test_framework + <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 "coroutine" : + [ run test_asymmetric_coroutine.cpp ] + [ run test_symmetric_coroutine.cpp ] + ; diff --git a/src/boost/libs/coroutine/test/test_asymmetric_coroutine.cpp b/src/boost/libs/coroutine/test/test_asymmetric_coroutine.cpp new file mode 100644 index 00000000..124271a1 --- /dev/null +++ b/src/boost/libs/coroutine/test/test_asymmetric_coroutine.cpp @@ -0,0 +1,627 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include <boost/coroutine/asymmetric_coroutine.hpp> + +#include <algorithm> +#include <iostream> +#include <sstream> +#include <stdexcept> +#include <string> +#include <vector> + +#include <cstdio> + +#include <boost/assert.hpp> +#include <boost/bind.hpp> +#include <boost/foreach.hpp> +#include <boost/move/move.hpp> +#include <boost/range.hpp> +#include <boost/ref.hpp> +#include <boost/test/unit_test.hpp> +#include <boost/tuple/tuple.hpp> +#include <boost/utility.hpp> + +namespace coro = boost::coroutines; + +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::asymmetric_coroutine< int >::push_type &) + { value3 = state; } +}; + +class moveable +{ +private: + BOOST_MOVABLE_BUT_NOT_COPYABLE( moveable) + +public: + bool state; + + moveable() : + state( false) + {} + + moveable( int) : + state( true) + {} + + moveable( BOOST_RV_REF( moveable) other) : + state( false) + { std::swap( state, other.state); } + + moveable & operator=( BOOST_RV_REF( moveable) other) + { + if ( this == & other) return * this; + moveable tmp( boost::move( other) ); + std::swap( state, tmp.state); + return * this; + } + + void operator()( coro::asymmetric_coroutine< int >::push_type &) + { value3 = state; } +}; + +struct my_exception {}; + +void f1( coro::asymmetric_coroutine< void >::push_type & c) +{ + while ( c) + c(); +} + +void f2( coro::asymmetric_coroutine< void >::push_type &) +{ ++value1; } + +void f3( coro::asymmetric_coroutine< void >::push_type & c) +{ + ++value1; + c(); + ++value1; +} + +void f4( coro::asymmetric_coroutine< int >::push_type & c) +{ + c( 3); + c( 7); +} + +void f5( coro::asymmetric_coroutine< std::string >::push_type & c) +{ + std::string res("abc"); + c( res); + res = "xyz"; + c( res); +} + +void f6( coro::asymmetric_coroutine< int >::pull_type & c) +{ value1 = c.get(); } + +void f7( coro::asymmetric_coroutine< std::string >::pull_type & c) +{ value2 = c.get(); } + +void f8( coro::asymmetric_coroutine< boost::tuple< double, double > >::pull_type & c) +{ + double x = 0, y = 0; + boost::tie( x, y) = c.get(); + value4 = x + y; + c(); + boost::tie( x, y) = c.get(); + value4 = x + y; +} + +void f9( coro::asymmetric_coroutine< int * >::pull_type & c) +{ value5 = c.get(); } + +void f91( coro::asymmetric_coroutine< int const* >::pull_type & c) +{ value5 = const_cast< int * >( c.get() ); } + +void f10( coro::asymmetric_coroutine< int & >::pull_type & c) +{ + int const& i = c.get(); + value5 = const_cast< int * >( & i); +} + +void f101( coro::asymmetric_coroutine< int const& >::pull_type & c) +{ + int const& i = c.get(); + value5 = const_cast< int * >( & i); +} + +void f11( coro::asymmetric_coroutine< boost::tuple< int, int > >::pull_type & c) +{ + boost::tie( value8, value9) = c.get(); +} + +void f12( coro::asymmetric_coroutine< void >::pull_type & c) +{ + X x_; + c(); + c(); +} + +template< typename E > +void f14( coro::asymmetric_coroutine< void >::pull_type &, E const& e) +{ throw e; } + +void f16( coro::asymmetric_coroutine< int >::push_type & c) +{ + c( 1); + c( 2); + c( 3); + c( 4); + c( 5); +} + +void f17( coro::asymmetric_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::asymmetric_coroutine< int* >::push_type & c, std::vector< int * > & vec) +{ + BOOST_FOREACH( int * ptr, vec) + { c( ptr); } +} + +void f20( coro::asymmetric_coroutine< int >::push_type &) +{} + +void f21( coro::asymmetric_coroutine< int >::pull_type & c) +{ + while ( c) + { + value1 = c.get(); + c(); + } +} + +void test_move() +{ + { + coro::asymmetric_coroutine< void >::pull_type coro1; + coro::asymmetric_coroutine< void >::pull_type coro2( f1); + BOOST_CHECK( ! coro1); + BOOST_CHECK( coro2); + coro2(); + coro1 = boost::move( coro2); + BOOST_CHECK( coro1); + coro1(); + BOOST_CHECK( ! coro2); + } + + { + value3 = false; + copyable cp( 3); + BOOST_CHECK( cp.state); + BOOST_CHECK( ! value3); + coro::asymmetric_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::asymmetric_coroutine< int >::pull_type coro( boost::move( mv) ); + BOOST_CHECK( ! mv.state); + BOOST_CHECK( value3); + } +} + +void test_complete() +{ + value1 = 0; + + coro::asymmetric_coroutine< void >::pull_type coro( f2); + BOOST_CHECK( ! coro); + BOOST_CHECK_EQUAL( ( int)1, value1); +} + +void test_jump() +{ + value1 = 0; + + coro::asymmetric_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::asymmetric_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::asymmetric_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::asymmetric_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::asymmetric_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::asymmetric_coroutine< boost::tuple< double, double > >::push_type coro( f8); + BOOST_CHECK( coro); + coro( boost::make_tuple( 7.35, 3.14) ); + BOOST_CHECK( coro); + BOOST_CHECK_EQUAL( ( double) 10.49, value4); + + value4 = 0; + coro( boost::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::asymmetric_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::asymmetric_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::asymmetric_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::asymmetric_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; + boost::tuple< int, int > tpl( a, b); + BOOST_CHECK_EQUAL( a, tpl.get< 0 >() ); + BOOST_CHECK_EQUAL( b, tpl.get< 1 >() ); + coro::asymmetric_coroutine< boost::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::asymmetric_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_no_unwind() +{ + value1 = 0; + { + coro::asymmetric_coroutine< void >::push_type coro( + f12, + coro::attributes( + coro::stack_allocator::traits_type::default_size(), + coro::no_stack_unwind) ); + 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) 7, value1); +} + +void test_exceptions() +{ + bool thrown = false; + std::runtime_error ex("abc"); + try + { + coro::asymmetric_coroutine< void >::push_type coro( boost::bind( f14< std::runtime_error >, _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() +{ + { + std::vector< int > vec; + coro::asymmetric_coroutine< int >::pull_type coro( f16); + BOOST_FOREACH( int 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] ); + } + { + std::vector< int > vec; + coro::asymmetric_coroutine< int >::pull_type coro( f16); + coro::asymmetric_coroutine< int >::pull_type::iterator e = boost::end( coro); + for ( + coro::asymmetric_coroutine< int >::pull_type::iterator i = boost::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] ); + } + { + int i1 = 1, i2 = 2, i3 = 3; + std::vector< int* > vec_in; + vec_in.push_back( & i1); + vec_in.push_back( & i2); + vec_in.push_back( & i3); + std::vector< int* > vec_out; + coro::asymmetric_coroutine< int* >::pull_type coro( boost::bind( f19, _1, boost::ref( vec_in) ) ); + coro::asymmetric_coroutine< int* >::pull_type::iterator e = boost::end( coro); + for ( + coro::asymmetric_coroutine< int* >::pull_type::iterator i = boost::begin( coro); + i != e; ++i) + { + int * p = * i; + vec_out.push_back( p); + } + BOOST_CHECK_EQUAL( ( std::size_t)3, vec_out.size() ); + BOOST_CHECK_EQUAL( & i1, vec_out[0] ); + BOOST_CHECK_EQUAL( & i2, vec_out[1] ); + BOOST_CHECK_EQUAL( & i3, vec_out[2] ); + } +} + +void test_output_iterator() +{ + int counter = 0; + std::vector< int > vec; + coro::asymmetric_coroutine< int >::push_type coro( + boost::bind( f17, _1, boost::ref( vec) ) ); + coro::asymmetric_coroutine< int >::push_type::iterator e( boost::end( coro) ); + for ( coro::asymmetric_coroutine< int >::push_type::iterator i( boost::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_invalid_result() +{ + bool catched = false; + coro::asymmetric_coroutine< int >::pull_type coro( f20); + BOOST_CHECK( ! coro); + try + { + int i = coro.get(); + (void)i; + } + catch ( coro::invalid_result const&) + { + catched = true; + } + BOOST_CHECK( catched); +} +void test_move_coro() +{ + value1 = 0; + + coro::asymmetric_coroutine< int >::push_type coro1( f21); + coro::asymmetric_coroutine< int >::push_type coro2; + BOOST_CHECK( coro1); + BOOST_CHECK( ! coro2); + + coro1( 1); + BOOST_CHECK_EQUAL( ( int)1, value1); + + coro2 = boost::move( coro1); + BOOST_CHECK( ! coro1); + BOOST_CHECK( coro2); + + coro2( 2); + BOOST_CHECK_EQUAL( ( int)2, value1); + + coro1 = boost::move( coro2); + BOOST_CHECK( coro1); + BOOST_CHECK( ! coro2); + + coro1( 3); + BOOST_CHECK_EQUAL( ( int)3, value1); + + coro2 = boost::move( coro1); + BOOST_CHECK( ! coro1); + BOOST_CHECK( coro2); + + coro2( 4); + BOOST_CHECK_EQUAL( ( int)4, value1); +} + +void foo( coro::asymmetric_coroutine< int >::push_type & yield) +{ + yield( 1); +} + +coro::asymmetric_coroutine< int >::pull_type make_range() +{ + return coro::asymmetric_coroutine< int >::pull_type( foo); +} + +template< typename Range > +void const_func( Range const& r) +{ + begin( r); +} + +void test_range() +{ + const_func( make_range() ); +} + +boost::unit_test::test_suite * init_unit_test_suite( int, char* []) +{ + boost::unit_test::test_suite * test = + BOOST_TEST_SUITE("Boost.coroutine: asymmetric 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_invalid_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_no_unwind) ); + test->add( BOOST_TEST_CASE( & test_exceptions) ); + test->add( BOOST_TEST_CASE( & test_input_iterator) ); + test->add( BOOST_TEST_CASE( & test_output_iterator) ); + test->add( BOOST_TEST_CASE( & test_range) ); + + return test; +} diff --git a/src/boost/libs/coroutine/test/test_symmetric_coroutine.cpp b/src/boost/libs/coroutine/test/test_symmetric_coroutine.cpp new file mode 100644 index 00000000..55015cee --- /dev/null +++ b/src/boost/libs/coroutine/test/test_symmetric_coroutine.cpp @@ -0,0 +1,604 @@ + +// Copyright Oliver Kowalke 2009. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include <boost/coroutine/symmetric_coroutine.hpp> + +#include <algorithm> +#include <iostream> +#include <sstream> +#include <stdexcept> +#include <string> +#include <vector> + +#include <cstdio> + +#include <boost/assert.hpp> +#include <boost/bind.hpp> +#include <boost/foreach.hpp> +#include <boost/lexical_cast.hpp> +#include <boost/move/move.hpp> +#include <boost/range.hpp> +#include <boost/ref.hpp> +#include <boost/test/unit_test.hpp> +#include <boost/tuple/tuple.hpp> +#include <boost/utility.hpp> + +namespace coro = boost::coroutines; + +bool value1 = false; +int value2 = 0; +std::string value3; + +typedef void( * coro_fn_void)(coro::symmetric_coroutine< void* >::yield_type &); + +coro::symmetric_coroutine< void >::call_type * term_coro = 0; + +struct X +{ + int i; + + X() : + i( 0) + {} + + X( int i_) : + i( i_) + {} +}; + +X * p = 0; + +struct Y +{ + Y() + { value2 = 7; } + + ~Y() + { value2 = 0; } +}; + +template< typename X, typename I > +void trampoline( coro::symmetric_coroutine< void* >::yield_type & yield) +{ + void * vp = yield.get(); + X * x = static_cast< X * >( vp); + I i( yield); + x->d = & i; + i.suspend(); + i.run(); +} + +struct B +{ + virtual ~B() {} + + virtual void foo() = 0; +}; + +class D : public B +{ +public: + int count; + coro::symmetric_coroutine< void* >::call_type call; + coro::symmetric_coroutine< void* >::yield_type * yield; + + D( coro::symmetric_coroutine< void* >::yield_type & yield_) : + B(), + count( 0), + call(), + yield( & yield_) + {} + + void foo() {} + + void resume() + { call( 0); } + + void suspend() + { ( *yield)(); } + + void run() + { + while ( yield && * yield) + { + ++count; + suspend(); + } + } +}; + +struct T +{ + D * d; + + T() : + d( 0) + {} +}; + +class copyable +{ +public: + bool state; + + copyable() : + state( false) + {} + + copyable( int) : + state( true) + {} + + void operator()( coro::symmetric_coroutine< bool >::yield_type & yield) + { + if ( yield) + value1 = yield.get(); + } +}; + +class moveable +{ +private: + BOOST_MOVABLE_BUT_NOT_COPYABLE( moveable) + +public: + bool state; + + moveable() : + state( false) + {} + + moveable( int) : + state( true) + {} + + moveable( BOOST_RV_REF( moveable) other) : + state( false) + { std::swap( state, other.state); } + + moveable & operator=( BOOST_RV_REF( moveable) other) + { + if ( this == & other) return * this; + moveable tmp( boost::move( other) ); + std::swap( state, tmp.state); + return * this; + } + + void operator()( coro::symmetric_coroutine< int >::yield_type &) + { value1 = state; } +}; + +void empty( coro::symmetric_coroutine< void >::yield_type &) {} + +void f2( coro::symmetric_coroutine< void >::yield_type &) +{ ++value2; } + +void f3( coro::symmetric_coroutine< X >::yield_type & yield) +{ value2 = yield.get().i; } + +void f4( coro::symmetric_coroutine< X& >::yield_type & yield) +{ + X & x = yield.get(); + p = & x; +} + +void f5( coro::symmetric_coroutine< X* >::yield_type & yield) +{ p = yield.get(); } + +void f6( coro::symmetric_coroutine< void >::yield_type & yield) +{ + Y y; + yield( *term_coro); +} + +void f7( coro::symmetric_coroutine< int >::yield_type & yield) +{ + value2 = yield.get(); + yield( *term_coro); + value2 = yield.get(); +} + +template< typename E > +void f9( coro::symmetric_coroutine< void >::yield_type &, E const& e) +{ throw e; } + +void f10( coro::symmetric_coroutine< int >::yield_type & yield, + coro::symmetric_coroutine< int >::call_type & other) +{ + int i = yield.get(); + yield( other, i); + value2 = yield.get(); +} + +void f101( coro::symmetric_coroutine< int >::yield_type & yield) +{ value2 = yield.get(); } + +void f11( coro::symmetric_coroutine< void >::yield_type & yield, + coro::symmetric_coroutine< void >::call_type & other) +{ + yield( other); + value2 = 7; +} + +void f111( coro::symmetric_coroutine< void >::yield_type &) +{ value2 = 3; } + +void f12( coro::symmetric_coroutine< X& >::yield_type & yield, + coro::symmetric_coroutine< X& >::call_type & other) +{ + yield( other, yield.get()); + p = & yield.get(); +} + +void f121( coro::symmetric_coroutine< X& >::yield_type & yield) +{ p = & yield.get(); } + +void f14( coro::symmetric_coroutine< int >::yield_type & yield, + coro::symmetric_coroutine< std::string >::call_type & other) +{ + std::string str( boost::lexical_cast< std::string >( yield.get() ) ); + yield( other, str); + value2 = yield.get(); +} + +void f141( coro::symmetric_coroutine< std::string >::yield_type & yield) +{ value3 = yield.get(); } + +void f15( coro::symmetric_coroutine< int >::yield_type & yield, + int offset, + coro::symmetric_coroutine< int >::call_type & other) +{ + int x = yield.get(); + value2 += x + offset; + yield( other, x); + x = yield.get(); + value2 += x + offset; + yield( other, x); +} + +void f151( coro::symmetric_coroutine< int >::yield_type & yield, + int offset) +{ + int x = yield.get(); + value2 += x + offset; + yield(); + x = yield.get(); + value2 += x + offset; +} + +void f16( coro::symmetric_coroutine< int >::yield_type & yield) +{ + while ( yield) + { + value2 = yield.get(); + yield(); + } +} + +void test_move() +{ + { + coro::symmetric_coroutine< void >::call_type coro1; + coro::symmetric_coroutine< void >::call_type coro2( empty); + BOOST_CHECK( ! coro1); + BOOST_CHECK( coro2); + coro1 = boost::move( coro2); + BOOST_CHECK( coro1); + BOOST_CHECK( ! coro2); + } + + { + value1 = false; + copyable cp( 3); + BOOST_CHECK( cp.state); + BOOST_CHECK( ! value1); + coro::symmetric_coroutine< bool >::call_type coro( cp); + coro( true); + BOOST_CHECK( cp.state); + BOOST_CHECK( value1); + } + + { + value1 = false; + moveable mv( 7); + BOOST_CHECK( mv.state); + BOOST_CHECK( ! value1); + coro::symmetric_coroutine< int >::call_type coro( boost::move( mv) ); + coro( 7); + BOOST_CHECK( ! mv.state); + BOOST_CHECK( value1); + } +} + +void test_complete() +{ + value2 = 0; + + coro::symmetric_coroutine< void >::call_type coro( f2); + BOOST_CHECK( coro); + coro(); + BOOST_CHECK( ! coro); + BOOST_CHECK_EQUAL( ( int)1, value2); +} + +void test_yield() +{ + value2 = 0; + + coro::symmetric_coroutine< int >::call_type coro3( + boost::bind( f151, _1, 3) ); + BOOST_CHECK( coro3); + coro::symmetric_coroutine< int >::call_type coro2( + boost::bind( f15, _1, 2, boost::ref( coro3) ) ); + BOOST_CHECK( coro2); + coro::symmetric_coroutine< int >::call_type coro1( + boost::bind( f15, _1, 1, boost::ref( coro2) ) ); + BOOST_CHECK( coro1); + + BOOST_CHECK_EQUAL( ( int)0, value2); + coro1( 1); + BOOST_CHECK( coro3); + BOOST_CHECK( coro2); + BOOST_CHECK( coro1); + BOOST_CHECK_EQUAL( ( int)9, value2); + coro1( 2); + BOOST_CHECK( ! coro3); + BOOST_CHECK( coro2); + BOOST_CHECK( coro1); + BOOST_CHECK_EQUAL( ( int)21, value2); +} + +void test_pass_value() +{ + value2 = 0; + + X x(7); + BOOST_CHECK_EQUAL( ( int)7, x.i); + BOOST_CHECK_EQUAL( 0, value2); + coro::symmetric_coroutine< X >::call_type coro( f3); + BOOST_CHECK( coro); + coro(7); + BOOST_CHECK( ! coro); + BOOST_CHECK_EQUAL( ( int)7, x.i); + BOOST_CHECK_EQUAL( 7, value2); +} + +void test_pass_reference() +{ + p = 0; + + X x; + coro::symmetric_coroutine< X& >::call_type coro( f4); + BOOST_CHECK( coro); + coro( x); + BOOST_CHECK( ! coro); + BOOST_CHECK( p == & x); +} + +void test_pass_pointer() +{ + p = 0; + + X x; + coro::symmetric_coroutine< X* >::call_type coro( f5); + BOOST_CHECK( coro); + coro( & x); + BOOST_CHECK( ! coro); + BOOST_CHECK( p == & x); +} + +void test_unwind() +{ + value2 = 0; + { + coro::symmetric_coroutine< void >::call_type coro( f6); + coro::symmetric_coroutine< void >::call_type coro_e( empty); + BOOST_CHECK( coro); + BOOST_CHECK( coro_e); + term_coro = & coro_e; + BOOST_CHECK_EQUAL( ( int) 0, value2); + coro(); + BOOST_CHECK( coro); + BOOST_CHECK_EQUAL( ( int) 7, value2); + } + BOOST_CHECK_EQUAL( ( int) 0, value2); +} + +void test_no_unwind() +{ + value2 = 0; + { + coro::symmetric_coroutine< void >::call_type coro( f6, + coro::attributes( + coro::stack_allocator::traits_type::default_size(), + coro::no_stack_unwind) ); + coro::symmetric_coroutine< void >::call_type coro_e( empty); + BOOST_CHECK( coro); + BOOST_CHECK( coro_e); + term_coro = & coro_e; + BOOST_CHECK_EQUAL( ( int) 0, value2); + coro(); + BOOST_CHECK( coro); + BOOST_CHECK_EQUAL( ( int) 7, value2); + } + BOOST_CHECK_EQUAL( ( int) 7, value2); +} + +void test_termination() +{ + value2 = 0; + + coro::symmetric_coroutine< int >::call_type coro( f7); + coro::symmetric_coroutine< void >::call_type coro_e( empty); + BOOST_CHECK( coro); + BOOST_CHECK( coro_e); + term_coro = & coro_e; + BOOST_CHECK_EQUAL( ( int) 0, value2); + coro(3); + BOOST_CHECK( coro); + BOOST_CHECK_EQUAL( ( int) 3, value2); + coro(7); + BOOST_CHECK( ! coro); + BOOST_CHECK_EQUAL( ( int) 7, value2); +} + +void test_yield_to_void() +{ + value2 = 0; + + coro::symmetric_coroutine< void >::call_type coro_other( f111); + coro::symmetric_coroutine< void >::call_type coro( boost::bind( f11, _1, boost::ref( coro_other) ) ); + BOOST_CHECK( coro_other); + BOOST_CHECK( coro); + BOOST_CHECK_EQUAL( ( int) 0, value2); + coro(); + BOOST_CHECK( ! coro_other); + BOOST_CHECK( coro); + BOOST_CHECK_EQUAL( ( int) 3, value2); + coro(); + BOOST_CHECK( ! coro_other); + BOOST_CHECK( ! coro); + BOOST_CHECK_EQUAL( ( int) 7, value2); +} + +void test_yield_to_int() +{ + value2 = 0; + + coro::symmetric_coroutine< int >::call_type coro_other( f101); + coro::symmetric_coroutine< int >::call_type coro( boost::bind( f10, _1, boost::ref( coro_other) ) ); + BOOST_CHECK( coro_other); + BOOST_CHECK( coro); + BOOST_CHECK_EQUAL( ( int) 0, value2); + coro(3); + BOOST_CHECK( ! coro_other); + BOOST_CHECK( coro); + BOOST_CHECK_EQUAL( ( int) 3, value2); + coro(7); + BOOST_CHECK( ! coro_other); + BOOST_CHECK( ! coro); + BOOST_CHECK_EQUAL( ( int) 7, value2); +} + +void test_yield_to_ref() +{ + p = 0; + + coro::symmetric_coroutine< X& >::call_type coro_other( f121); + coro::symmetric_coroutine< X& >::call_type coro( boost::bind( f12, _1, boost::ref( coro_other) ) ); + BOOST_CHECK( coro_other); + BOOST_CHECK( coro); + BOOST_CHECK( 0 == p); + X x1(3); + coro( x1); + BOOST_CHECK( ! coro_other); + BOOST_CHECK( coro); + BOOST_CHECK_EQUAL( p->i, x1.i); + BOOST_CHECK( p == & x1); + X x2(7); + coro(x2); + BOOST_CHECK( ! coro_other); + BOOST_CHECK( ! coro); + BOOST_CHECK_EQUAL( p->i, x2.i); + BOOST_CHECK( p == & x2); +} + +void test_yield_to_different() +{ + value2 = 0; + value3 = ""; + + coro::symmetric_coroutine< std::string >::call_type coro_other( f141); + coro::symmetric_coroutine< int >::call_type coro( boost::bind( f14, _1, boost::ref( coro_other) ) ); + BOOST_CHECK( coro_other); + BOOST_CHECK( coro); + BOOST_CHECK_EQUAL( ( int) 0, value2); + BOOST_CHECK( value3.empty() ); + coro(3); + BOOST_CHECK( ! coro_other); + BOOST_CHECK( coro); + BOOST_CHECK_EQUAL( "3", value3); + coro(7); + BOOST_CHECK( ! coro_other); + BOOST_CHECK( ! coro); + BOOST_CHECK_EQUAL( ( int) 7, value2); +} + +void test_move_coro() +{ + value2 = 0; + + coro::symmetric_coroutine< int >::call_type coro1( f16); + coro::symmetric_coroutine< int >::call_type coro2; + BOOST_CHECK( coro1); + BOOST_CHECK( ! coro2); + + coro1( 1); + BOOST_CHECK_EQUAL( ( int)1, value2); + + coro2 = boost::move( coro1); + BOOST_CHECK( ! coro1); + BOOST_CHECK( coro2); + + coro2( 2); + BOOST_CHECK_EQUAL( ( int)2, value2); + + coro1 = boost::move( coro2); + BOOST_CHECK( coro1); + BOOST_CHECK( ! coro2); + + coro1( 3); + BOOST_CHECK_EQUAL( ( int)3, value2); + + coro2 = boost::move( coro1); + BOOST_CHECK( ! coro1); + BOOST_CHECK( coro2); + + coro2( 4); + BOOST_CHECK_EQUAL( ( int)4, value2); +} + +void test_vptr() +{ + D * d = 0; + T t; + coro_fn_void fn = trampoline< T, D >; + coro::symmetric_coroutine< void* >::call_type call( fn); + call( & t); + d = t.d; + BOOST_CHECK( 0 != d); + d->call = boost::move( call); + + BOOST_CHECK_EQUAL( ( int) 0, d->count); + d->resume(); + BOOST_CHECK_EQUAL( ( int) 1, d->count); + d->resume(); + BOOST_CHECK_EQUAL( ( int) 2, d->count); +} + +boost::unit_test::test_suite * init_unit_test_suite( int, char* []) +{ + boost::unit_test::test_suite * test = + BOOST_TEST_SUITE("Boost.coroutine: symmetric coroutine test suite"); + + test->add( BOOST_TEST_CASE( & test_move) ); + test->add( BOOST_TEST_CASE( & test_complete) ); + test->add( BOOST_TEST_CASE( & test_yield) ); + test->add( BOOST_TEST_CASE( & test_pass_value) ); + test->add( BOOST_TEST_CASE( & test_pass_reference) ); + test->add( BOOST_TEST_CASE( & test_pass_pointer) ); + test->add( BOOST_TEST_CASE( & test_termination) ); + test->add( BOOST_TEST_CASE( & test_unwind) ); + test->add( BOOST_TEST_CASE( & test_no_unwind) ); + test->add( BOOST_TEST_CASE( & test_yield_to_void) ); + test->add( BOOST_TEST_CASE( & test_yield_to_int) ); + test->add( BOOST_TEST_CASE( & test_yield_to_ref) ); + test->add( BOOST_TEST_CASE( & test_yield_to_different) ); + test->add( BOOST_TEST_CASE( & test_move_coro) ); + test->add( BOOST_TEST_CASE( & test_vptr) ); + + return test; +} |