diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 18:45:59 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 18:45:59 +0000 |
commit | 19fcec84d8d7d21e796c7624e521b60d28ee21ed (patch) | |
tree | 42d26aa27d1e3f7c0b8bd3fd14e7d7082f5008dc /src/boost/libs/coroutine2 | |
parent | Initial commit. (diff) | |
download | ceph-upstream/16.2.11+ds.tar.xz ceph-upstream/16.2.11+ds.zip |
Adding upstream version 16.2.11+ds.upstream/16.2.11+dsupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/boost/libs/coroutine2')
28 files changed, 2328 insertions, 0 deletions
diff --git a/src/boost/libs/coroutine2/README.md b/src/boost/libs/coroutine2/README.md new file mode 100644 index 000000000..784164a3d --- /dev/null +++ b/src/boost/libs/coroutine2/README.md @@ -0,0 +1,14 @@ +boost.coroutine2 +=============== + +boost.coroutine2 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. + +boost.coroutine2 requires C++11! +Note that boost.coroutine2 is the successor of the deprectated boost.coroutine. diff --git a/src/boost/libs/coroutine2/example/Jamfile.v2 b/src/boost/libs/coroutine2/example/Jamfile.v2 new file mode 100644 index 000000000..67bd232eb --- /dev/null +++ b/src/boost/libs/coroutine2/example/Jamfile.v2 @@ -0,0 +1,45 @@ + +# Copyright Oliver Kowalke 2014. +# Distributed under the Boost Software License, Version 1.0. +# (See accompanying file LICENSE_1_0.txt or copy at +# http://www.boost.org/LICENSE_1_0.txt) + +# For more information, see http://www.boost.org/ + +import common ; +import feature ; +import indirect ; +import modules ; +import os ; +import toolset ; + +project boost/coroutine2/example + : requirements + <library>/boost/context//boost_context + <toolset>gcc,<segmented-stacks>on:<cxxflags>-fsplit-stack + <toolset>gcc,<segmented-stacks>on:<cxxflags>-DBOOST_USE_SEGMENTED_STACKS + <toolset>clang,<segmented-stacks>on:<cxxflags>-fsplit-stack + <toolset>clang,<segmented-stacks>on:<cxxflags>-DBOOST_USE_SEGMENTED_STACKS + <link>static + <threading>multi + ; + +exe fibonacci + : fibonacci.cpp + ; + +exe same_fringe + : same_fringe.cpp + ; + +exe layout + : layout.cpp + ; + +exe parser + : parser.cpp + ; + +exe segmented + : segmented.cpp + ; diff --git a/src/boost/libs/coroutine2/example/fibonacci.cpp b/src/boost/libs/coroutine2/example/fibonacci.cpp new file mode 100644 index 000000000..547342cb2 --- /dev/null +++ b/src/boost/libs/coroutine2/example/fibonacci.cpp @@ -0,0 +1,30 @@ + +// Copyright Oliver Kowalke 2014. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include <cstdlib> +#include <iostream> + +#include <boost/coroutine2/all.hpp> + +int main() { + boost::coroutines2::coroutine< int >::pull_type source( + []( boost::coroutines2::coroutine< int >::push_type & sink) { + int first = 1, second = 1; + sink( first); + sink( second); + for ( int i = 0; i < 8; ++i) { + int third = first + second; + first = second; + second = third; + sink( third); + } + }); + for ( auto i : source) { + std::cout << i << " "; + } + std::cout << "\nDone" << std::endl; + return EXIT_SUCCESS; +} diff --git a/src/boost/libs/coroutine2/example/layout.cpp b/src/boost/libs/coroutine2/example/layout.cpp new file mode 100644 index 000000000..e8f69e01b --- /dev/null +++ b/src/boost/libs/coroutine2/example/layout.cpp @@ -0,0 +1,50 @@ + +// Copyright Nat Goodspeed 2013. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include <iostream> +#include <iomanip> +#include <vector> +#include <string> +#include <utility> + +#include <boost/coroutine2/all.hpp> + +struct FinalEOL{ + ~FinalEOL(){ + std::cout << std::endl; + } +}; + +int main(int argc,char* argv[]){ + using std::begin; + using std::end; + std::vector<std::string> words{ + "peas", "porridge", "hot", "peas", + "porridge", "cold", "peas", "porridge", + "in", "the", "pot", "nine", + "days", "old" }; + int num=5,width=15; + boost::coroutines2::coroutine<std::string>::push_type writer( + [&](boost::coroutines2::coroutine<std::string>::pull_type& in){ + // finish the last line when we leave by whatever means + FinalEOL eol; + // pull values from upstream, lay them out 'num' to a line + for (;;){ + for(int i=0;i<num;++i){ + // when we exhaust the input, stop + if(!in) return; + std::cout << std::setw(width) << in.get(); + // now that we've handled this item, advance to next + in(); + } + // after 'num' items, line break + std::cout << std::endl; + } + }); + std::copy(begin(words),end(words),begin(writer)); + std::cout << "\nDone"; + return EXIT_SUCCESS; +} diff --git a/src/boost/libs/coroutine2/example/parser.cpp b/src/boost/libs/coroutine2/example/parser.cpp new file mode 100644 index 000000000..7aef477eb --- /dev/null +++ b/src/boost/libs/coroutine2/example/parser.cpp @@ -0,0 +1,122 @@ + +// Copyright Oliver Kowalke 2014. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include <cctype> +#include <cstdio> +#include <exception> +#include <functional> +#include <iostream> +#include <sstream> + +#include <boost/coroutine2/all.hpp> + +class parser_error : public std::runtime_error { +public: + parser_error() : + std::runtime_error("parsing failed") { + } +}; + +/* + * grammar: + * P ---> E '\0' + * E ---> T {('+'|'-') T} + * T ---> S {('*'|'/') S} + * S ---> digit | '(' E ')' + */ +class Parser{ + char next; + std::istream& is; + std::function<void(char)> cb; + + char pull(){ + return std::char_traits<char>::to_char_type(is.get()); + } + + void scan(){ + do{ + next=pull(); + } + while(isspace(next)); + } + +public: + Parser(std::istream& is_,std::function<void(char)> cb_) : + next(), is(is_), cb(cb_) + {} + + void run() { + scan(); + E(); + } + +private: + void E(){ + T(); + while (next=='+'||next=='-'){ + cb(next); + scan(); + T(); + } + } + + void T(){ + S(); + while (next=='*'||next=='/'){ + cb(next); + scan(); + S(); + } + } + + void S(){ + if (std::isdigit(next)){ + cb(next); + scan(); + } + else if(next=='('){ + cb(next); + scan(); + E(); + if (next==')'){ + cb(next); + scan(); + }else{ + throw parser_error(); + } + } + else{ + throw parser_error(); + } + } +}; + +typedef boost::coroutines2::coroutine< char > coro_t; + +int main() { + try { + std::istringstream is("1+1"); + // invert control flow + coro_t::pull_type seq( + [&is]( coro_t::push_type & yield) { + Parser p( is, + [&yield](char ch){ + yield(ch); + }); + p.run(); + }); + // user-code pulls parsed data from parser + for(char c:seq){ + printf("Parsed: %c\n",c); + } + std::cout << "\nDone" << std::endl; + return EXIT_SUCCESS; + } catch ( std::exception const& ex) { + std::cerr << "exception: " << ex.what() << std::endl; + } + return EXIT_FAILURE; +} + diff --git a/src/boost/libs/coroutine2/example/same_fringe.cpp b/src/boost/libs/coroutine2/example/same_fringe.cpp new file mode 100644 index 000000000..a056105bd --- /dev/null +++ b/src/boost/libs/coroutine2/example/same_fringe.cpp @@ -0,0 +1,172 @@ + +// Copyright Nat Goodspeed 2013. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSEstd::placeholders::_1_0.txt or copy at +// http://www.boost.org/LICENSEstd::placeholders::_1_0.txt) + +#include <cstddef> +#include <cstdlib> +#include <iostream> +#include <iterator> +#include <string> +#include <utility> + +#include <boost/coroutine2/all.hpp> + +struct node { + typedef std::shared_ptr< node > ptr_t; + + // Each tree node has an optional left subtree, an optional right subtree + // and a value of its own. The value is considered to be between the left + // subtree and the right. + ptr_t left, right; + std::string value; + + // construct leaf + node(const std::string& v): + left(),right(),value(v) { + } + + // construct nonleaf + node(ptr_t l, const std::string& v, ptr_t r): + left(l),right(r),value(v) { + } + + static ptr_t create(const std::string& v) { + return ptr_t(new node(v)); + } + + static ptr_t create(ptr_t l, const std::string& v, ptr_t r) { + return ptr_t(new node(l, v, r)); + } +}; + +node::ptr_t create_left_tree_from(const std::string& root) { + /* -------- + root + / \ + b e + / \ + a c + -------- */ + + return node::create( + node::create( + node::create("a"), + "b", + node::create("c")), + root, + node::create("e")); +} + +node::ptr_t create_right_tree_from(const std::string& root) { + /* -------- + root + / \ + a d + / \ + c e + -------- */ + + return node::create( + node::create("a"), + root, + node::create( + node::create("c"), + "d", + node::create("e"))); +} + +// recursively walk the tree, delivering values in order +void traverse(node::ptr_t n, boost::coroutines2::coroutine<std::string>::push_type& out) { + if (n->left) + traverse(n->left,out); + out(n->value); + if (n->right) + traverse(n->right,out); +} + +int main() { + { + node::ptr_t left_d(create_left_tree_from("d")); + node::ptr_t right_b(create_right_tree_from("b")); + node::ptr_t right_x(create_right_tree_from("x")); + { + boost::coroutines2::coroutine<std::string>::pull_type left_d_reader( + [&]( boost::coroutines2::coroutine<std::string>::push_type & out) { + traverse(left_d,out); + }); + std::cout << "left tree from d:\n"; + std::copy(begin(left_d_reader), + end(left_d_reader), + std::ostream_iterator<std::string>(std::cout, " ")); + std::cout << std::endl; + + boost::coroutines2::coroutine<std::string>::pull_type right_b_reader( + [&]( boost::coroutines2::coroutine<std::string>::push_type & out) { + traverse(right_b,out); + }); + std::cout << "right tree from b:\n"; + std::copy(begin(right_b_reader), + end(right_b_reader), + std::ostream_iterator<std::string>(std::cout, " ")); + std::cout << std::endl; + + boost::coroutines2::coroutine<std::string>::pull_type right_x_reader( + [&]( boost::coroutines2::coroutine<std::string>::push_type & out) { + traverse(right_x,out); + }); + std::cout << "right tree from x:\n"; + std::copy(begin(right_x_reader), + end(right_x_reader), + std::ostream_iterator<std::string>(std::cout, " ")); + std::cout << std::endl; + } + } + { + node::ptr_t left_d(create_left_tree_from("d")); + node::ptr_t right_b(create_right_tree_from("b")); + { + boost::coroutines2::coroutine<std::string>::pull_type left_d_reader( + [&]( boost::coroutines2::coroutine<std::string>::push_type & out) { + traverse(left_d,out); + }); + + boost::coroutines2::coroutine<std::string>::pull_type right_b_reader( + [&]( boost::coroutines2::coroutine<std::string>::push_type & out) { + traverse(right_b,out); + }); + + std::cout << "left tree from d == right tree from b? " + << std::boolalpha + << std::equal(begin(left_d_reader), + end(left_d_reader), + begin(right_b_reader)) + << std::endl; + } + } + { + node::ptr_t left_d(create_left_tree_from("d")); + node::ptr_t right_x(create_right_tree_from("x")); + { + boost::coroutines2::coroutine<std::string>::pull_type left_d_reader( + [&]( boost::coroutines2::coroutine<std::string>::push_type & out) { + traverse(left_d,out); + }); + + boost::coroutines2::coroutine<std::string>::pull_type right_x_reader( + [&]( boost::coroutines2::coroutine<std::string>::push_type & out) { + traverse(right_x,out); + }); + + std::cout << "left tree from d == right tree from x? " + << std::boolalpha + << std::equal(begin(left_d_reader), + end(left_d_reader), + begin(right_x_reader)) + << std::endl; + } + } + std::cout << "Done" << std::endl; + return EXIT_SUCCESS; +} diff --git a/src/boost/libs/coroutine2/example/segmented.cpp b/src/boost/libs/coroutine2/example/segmented.cpp new file mode 100644 index 000000000..07280551b --- /dev/null +++ b/src/boost/libs/coroutine2/example/segmented.cpp @@ -0,0 +1,48 @@ + +// Copyright Oliver Kowalke 2014. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include <cstdlib> +#include <iostream> +#include <memory> + +#include <boost/coroutine2/all.hpp> + +#ifdef BOOST_MSVC //MS VisualStudio +__declspec(noinline) void access( char *buf); +#else // GCC +void access( char *buf) __attribute__ ((noinline)); +#endif +void access( char *buf) { + buf[0] = '\0'; +} + +void bar( int i) { + char buf[4 * 1024]; + if ( i > 0) { + access( buf); + std::cout << i << ". iteration" << std::endl; + bar( i - 1); + } +} + +int main() { + int count = 384; +#if defined(BOOST_USE_SEGMENTED_STACKS) + std::cout << "using segmented_stack stacks: allocates " << count << " * 4kB == " << 4 * count << "kB on stack, "; + std::cout << "initial stack size = " << boost::context::segmented_stack::traits_type::default_size() / 1024 << "kB" << std::endl; + std::cout << "application should not fail" << std::endl; +#else + std::cout << "using standard stacks: allocates " << count << " * 4kB == " << 4 * count << "kB on stack, "; + std::cout << "initial stack size = " << boost::context::fixedsize_stack::traits_type::default_size() / 1024 << "kB" << std::endl; + std::cout << "application might fail" << std::endl; +#endif + boost::coroutines2::coroutine< void >::pull_type coro{ + [count](boost::coroutines2::coroutine< void >::push_type & coro){ + bar( count); + }}; + std::cout << "main: done" << std::endl; + return EXIT_SUCCESS; +} diff --git a/src/boost/libs/coroutine2/example/tree.h b/src/boost/libs/coroutine2/example/tree.h new file mode 100644 index 000000000..28df52730 --- /dev/null +++ b/src/boost/libs/coroutine2/example/tree.h @@ -0,0 +1,97 @@ + +// Copyright Oliver Kowalke 2014. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef TREE_H +#define TREE_H + +#include <cstddef> +#include <string> + +#include <boost/assert.hpp> +#include <boost/config.hpp> +#include <boost/intrusive_ptr.hpp> + +#if defined(_MSC_VER) +# pragma warning(push) +# pragma warning(disable:4355) +#endif + +struct branch; +struct leaf; + +struct visitor +{ + virtual ~visitor() {}; + + virtual void visit( branch & b) = 0; + virtual void visit( leaf & l) = 0; +}; + +struct node +{ + typedef boost::intrusive_ptr< node > ptr_t; + + std::size_t use_count; + + node() : + use_count( 0) + {} + + virtual ~node() {} + + virtual void accept( visitor & v) = 0; + + friend inline void intrusive_ptr_add_ref( node * p) + { ++p->use_count; } + + friend inline void intrusive_ptr_release( node * p) + { if ( 0 == --p->use_count) delete p; } +}; + +struct branch : public node +{ + node::ptr_t left; + node::ptr_t right; + + static ptr_t create( node::ptr_t left_, node::ptr_t right_) + { return ptr_t( new branch( left_, right_) ); } + + branch( node::ptr_t left_, node::ptr_t right_) : + left( left_), right( right_) + {} + + void accept( visitor & v) + { v.visit( * this); } +}; + +struct leaf : public node +{ + std::string value; + + static ptr_t create( std::string const& value_) + { return ptr_t( new leaf( value_) ); } + + leaf( std::string const& value_) : + value( value_) + {} + + void accept( visitor & v) + { v.visit( * this); } +}; + +inline +bool operator==( leaf const& l, leaf const& r) +{ return l.value == r.value; } + +inline +bool operator!=( leaf const& l, leaf const& r) +{ return l.value != r.value; } + +#if defined(_MSC_VER) +# pragma warning(pop) +#endif + +#endif // TREE_H diff --git a/src/boost/libs/coroutine2/index.html b/src/boost/libs/coroutine2/index.html new file mode 100644 index 000000000..0ade6cbd2 --- /dev/null +++ b/src/boost/libs/coroutine2/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/coroutine2/meta/libraries.json b/src/boost/libs/coroutine2/meta/libraries.json new file mode 100644 index 000000000..37c84aef9 --- /dev/null +++ b/src/boost/libs/coroutine2/meta/libraries.json @@ -0,0 +1,14 @@ +{ + "key": "coroutine2", + "name": "Coroutine2", + "authors": [ + "Oliver Kowalke" + ], + "description": "(C++11) Coroutine library.", + "category": [ + "Concurrent" + ], + "maintainers": [ + "Oliver Kowalke <oliver.kowalke -at- gmail.com>" + ] +} diff --git a/src/boost/libs/coroutine2/performance/Jamfile.v2 b/src/boost/libs/coroutine2/performance/Jamfile.v2 new file mode 100644 index 000000000..b0860905d --- /dev/null +++ b/src/boost/libs/coroutine2/performance/Jamfile.v2 @@ -0,0 +1,67 @@ + +# Copyright Oliver Kowalke 2014. +# Distributed under the Boost Software License, Version 1.0. +# (See accompanying file LICENSE_1_0.txt or copy at +# http://www.boost.org/LICENSE_1_0.txt) + +# For more information, see http://www.boost.org/ + +import common ; +import feature ; +import indirect ; +import modules ; +import os ; +import toolset ; + +project boost/coroutine2/performance + : requirements + <library>/boost/chrono//boost_chrono + <library>/boost/context//boost_context + <library>/boost/program_options//boost_program_options + <toolset>gcc,<segmented-stacks>on:<cxxflags>-fsplit-stack + <toolset>gcc,<segmented-stacks>on:<cxxflags>-DBOOST_USE_SEGMENTED_STACKS + <toolset>clang,<segmented-stacks>on:<cxxflags>-fsplit-stack + <toolset>clang,<segmented-stacks>on:<cxxflags>-DBOOST_USE_SEGMENTED_STACKS + <link>static + <threading>multi + <cxxflags>-DBOOST_DISABLE_ASSERTS + <optimization>speed + <variant>release + ; + +alias sources + : bind_processor_aix.cpp + : <target-os>aix + ; + +alias sources + : bind_processor_freebsd.cpp + : <target-os>freebsd + ; + +alias sources + : bind_processor_hpux.cpp + : <target-os>hpux + ; + +alias sources + : bind_processor_linux.cpp + : <target-os>linux + ; + +alias sources + : bind_processor_solaris.cpp + : <target-os>solaris + ; + +alias sources + : bind_processor_windows.cpp + : <target-os>windows + ; + +explicit sources ; + +exe performance + : sources + performance.cpp + ; diff --git a/src/boost/libs/coroutine2/performance/bind_processor.hpp b/src/boost/libs/coroutine2/performance/bind_processor.hpp new file mode 100644 index 000000000..bdde48a21 --- /dev/null +++ b/src/boost/libs/coroutine2/performance/bind_processor.hpp @@ -0,0 +1,12 @@ + +// Copyright Oliver Kowalke 2014. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BIND_TO_PROCESSOR_H +#define BIND_TO_PROCESSOR_H + +void bind_to_processor( unsigned int n); + +#endif // BIND_TO_PROCESSOR_H diff --git a/src/boost/libs/coroutine2/performance/bind_processor_aix.cpp b/src/boost/libs/coroutine2/performance/bind_processor_aix.cpp new file mode 100644 index 000000000..423fad7c7 --- /dev/null +++ b/src/boost/libs/coroutine2/performance/bind_processor_aix.cpp @@ -0,0 +1,25 @@ + +// Copyright Oliver Kowalke 2014. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include "bind_processor.hpp" + +extern "C" +{ +#include <sys/processor.h> +#include <sys/thread.h> +} + +#include <stdexcept> + +#include <boost/config/abi_prefix.hpp> + +void bind_to_processor( unsigned int n) +{ + if ( ::bindprocessor( BINDTHREAD, ::thread_yield(), static_cast< cpu_t >( n) ) == -1) + throw std::runtime_error("::bindprocessor() failed"); +} + +#include <boost/config/abi_suffix.hpp> diff --git a/src/boost/libs/coroutine2/performance/bind_processor_freebsd.cpp b/src/boost/libs/coroutine2/performance/bind_processor_freebsd.cpp new file mode 100644 index 000000000..3fe8f66ef --- /dev/null +++ b/src/boost/libs/coroutine2/performance/bind_processor_freebsd.cpp @@ -0,0 +1,29 @@ + +// Copyright Oliver Kowalke 2014. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include "bind_processor.hpp" + +extern "C" +{ +#include <sys/param.h> +#include <sys/cpuset.h> +} + +#include <stdexcept> + +#include <boost/config/abi_prefix.hpp> + +void bind_to_processor( unsigned int n) +{ + cpuset_t cpuset; + CPU_ZERO( & cpuset); + CPU_SET( n, & cpuset); + + if ( ::cpuset_setaffinity( CPU_LEVEL_WHICH, CPU_WHICH_TID, -1, sizeof( cpuset), & cpuset) == -1) + throw std::runtime_error("::cpuset_setaffinity() failed"); +} + +#include <boost/config/abi_suffix.hpp> diff --git a/src/boost/libs/coroutine2/performance/bind_processor_hpux.cpp b/src/boost/libs/coroutine2/performance/bind_processor_hpux.cpp new file mode 100644 index 000000000..fe9d05257 --- /dev/null +++ b/src/boost/libs/coroutine2/performance/bind_processor_hpux.cpp @@ -0,0 +1,31 @@ + +// Copyright Oliver Kowalke 2014. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include "bind_processor.hpp" + +extern "C" +{ +#include <sys/pthread.h> +} + +#include <stdexcept> + +#include <boost/config/abi_prefix.hpp> + +void bind_to_processor( unsigned int n) +{ + ::pthread_spu_t spu; + int errno_( + ::pthread_processor_bind_np( + PTHREAD_BIND_FORCED_NP, + & spu, + static_cast< pthread_spu_t >( n), + PTHREAD_SELFTID_NP) ); + if ( errno_ != 0) + throw std::runtime_error("::pthread_processor_bind_np() failed"); +} + +#include <boost/config/abi_suffix.hpp> diff --git a/src/boost/libs/coroutine2/performance/bind_processor_linux.cpp b/src/boost/libs/coroutine2/performance/bind_processor_linux.cpp new file mode 100644 index 000000000..a68ca6854 --- /dev/null +++ b/src/boost/libs/coroutine2/performance/bind_processor_linux.cpp @@ -0,0 +1,30 @@ + +// Copyright Oliver Kowalke 2014. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include "bind_processor.hpp" + +extern "C" +{ +#include <pthread.h> +#include <sched.h> +} + +#include <stdexcept> + +#include <boost/config/abi_prefix.hpp> + +void bind_to_processor( unsigned int n) +{ + cpu_set_t cpuset; + CPU_ZERO( & cpuset); + CPU_SET( n, & cpuset); + + int errno_( ::pthread_setaffinity_np( ::pthread_self(), sizeof( cpuset), & cpuset) ); + if ( errno_ != 0) + throw std::runtime_error("::pthread_setaffinity_np() failed"); +} + +#include <boost/config/abi_suffix.hpp> diff --git a/src/boost/libs/coroutine2/performance/bind_processor_solaris.cpp b/src/boost/libs/coroutine2/performance/bind_processor_solaris.cpp new file mode 100644 index 000000000..3724a3cd5 --- /dev/null +++ b/src/boost/libs/coroutine2/performance/bind_processor_solaris.cpp @@ -0,0 +1,26 @@ + +// Copyright Oliver Kowalke 2014. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include "bind_processor.hpp" + +extern "C" +{ +#include <sys/types.h> +#include <sys/processor.h> +#include <sys/procset.h> +} + +#include <stdexcept> + +#include <boost/config/abi_prefix.hpp> + +void bind_to_processor( unsigned int n) +{ + if ( ::processor_bind( P_LWPID, P_MYID, static_cast< processorid_t >( n), 0) == -1) + throw std::runtime_error("::processor_bind() failed"); +} + +#include <boost/config/abi_suffix.hpp> diff --git a/src/boost/libs/coroutine2/performance/bind_processor_windows.cpp b/src/boost/libs/coroutine2/performance/bind_processor_windows.cpp new file mode 100644 index 000000000..7e5b930e4 --- /dev/null +++ b/src/boost/libs/coroutine2/performance/bind_processor_windows.cpp @@ -0,0 +1,24 @@ + +// Copyright Oliver Kowalke 2014. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include "bind_processor.hpp" + +extern "C" +{ +#include <windows.h> +} + +#include <stdexcept> + +#include <boost/config/abi_prefix.hpp> + +void bind_to_processor( unsigned int n) +{ + if ( ::SetThreadAffinityMask( ::GetCurrentThread(), ( DWORD_PTR)1 << n) == 0) + throw std::runtime_error("::SetThreadAffinityMask() failed"); +} + +#include <boost/config/abi_suffix.hpp> diff --git a/src/boost/libs/coroutine2/performance/clock.hpp b/src/boost/libs/coroutine2/performance/clock.hpp new file mode 100644 index 000000000..b897bb53b --- /dev/null +++ b/src/boost/libs/coroutine2/performance/clock.hpp @@ -0,0 +1,45 @@ + +// Copyright Oliver Kowalke 2014. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) +// +#ifndef CLOCK_H +#define CLOCK_H + +#include <algorithm> +#include <cstddef> +#include <numeric> +#include <vector> + +#include <boost/assert.hpp> +#include <boost/chrono.hpp> +#include <boost/cstdint.hpp> + +typedef boost::chrono::high_resolution_clock clock_type; +typedef clock_type::duration duration_type; +typedef clock_type::time_point time_point_type; + +struct clock_overhead +{ + boost::uint64_t operator()() + { + time_point_type start( clock_type::now() ); + return ( clock_type::now() - start).count(); + } +}; + +inline +duration_type overhead_clock() +{ + std::size_t iterations( 10); + std::vector< boost::uint64_t > overhead( iterations, 0); + for ( std::size_t i = 0; i < iterations; ++i) + std::generate( + overhead.begin(), overhead.end(), + clock_overhead() ); + BOOST_ASSERT( overhead.begin() != overhead.end() ); + return duration_type( std::accumulate( overhead.begin(), overhead.end(), 0) / iterations); +} + +#endif // CLOCK_H diff --git a/src/boost/libs/coroutine2/performance/cycle.hpp b/src/boost/libs/coroutine2/performance/cycle.hpp new file mode 100644 index 000000000..74d59dd3c --- /dev/null +++ b/src/boost/libs/coroutine2/performance/cycle.hpp @@ -0,0 +1,26 @@ + +// Copyright Oliver Kowalke 2014. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef CYCLE_H +#define CYCLE_H + +// x86_64 +// test x86_64 before i386 because icc might +// define __i686__ for x86_64 too +#if defined(__x86_64__) || defined(__x86_64) \ + || defined(__amd64__) || defined(__amd64) \ + || defined(_M_X64) || defined(_M_AMD64) +# include "cycle_x86-64.hpp" +// i386 +#elif defined(i386) || defined(__i386__) || defined(__i386) \ + || defined(__i486__) || defined(__i586__) || defined(__i686__) \ + || defined(__X86__) || defined(_X86_) || defined(__THW_INTEL__) \ + || defined(__I86__) || defined(__INTEL__) || defined(__IA32__) \ + || defined(_M_IX86) || defined(_I86_) +# include "cycle_i386.hpp" +#endif + +#endif // CYCLE_H diff --git a/src/boost/libs/coroutine2/performance/cycle_i386.hpp b/src/boost/libs/coroutine2/performance/cycle_i386.hpp new file mode 100644 index 000000000..0ea2ccefb --- /dev/null +++ b/src/boost/libs/coroutine2/performance/cycle_i386.hpp @@ -0,0 +1,83 @@ + +// Copyright Oliver Kowalke 2014. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef CYCLE_I386_H +#define CYCLE_I386_H + +#include <algorithm> +#include <numeric> +#include <cstddef> +#include <vector> + +#include <boost/assert.hpp> +#include <boost/bind.hpp> +#include <boost/cstdint.hpp> + +#define BOOST_CONTEXT_CYCLE + +typedef boost::uint64_t cycle_type; + +#if _MSC_VER +inline +cycle_type cycles() +{ + cycle_type c; + __asm { + cpuid + rdtsc + mov dword ptr [c + 0], eax + mov dword ptr [c + 4], edx + } + return c; +} +#elif defined(__GNUC__) || \ + defined(__INTEL_COMPILER) || defined(__ICC) || defined(_ECC) || defined(__ICL) +inline +cycle_type cycles() +{ + boost::uint32_t lo, hi; + + __asm__ __volatile__ ( + "xorl %%eax, %%eax\n" + "cpuid\n" + ::: "%eax", "%ebx", "%ecx", "%edx" + ); + __asm__ __volatile__ ("rdtsc" : "=a" (lo), "=d" (hi) ); + __asm__ __volatile__ ( + "xorl %%eax, %%eax\n" + "cpuid\n" + ::: "%eax", "%ebx", "%ecx", "%edx" + ); + + return ( cycle_type)hi << 32 | lo; +} +#else +# error "this compiler is not supported" +#endif + +struct cycle_overhead +{ + cycle_type operator()() + { + cycle_type start( cycles() ); + return cycles() - start; + } +}; + +inline +cycle_type overhead_cycle() +{ + std::size_t iterations( 10); + std::vector< cycle_type > overhead( iterations, 0); + for ( std::size_t i( 0); i < iterations; ++i) + std::generate( + overhead.begin(), overhead.end(), + cycle_overhead() ); + BOOST_ASSERT( overhead.begin() != overhead.end() ); + return std::accumulate( overhead.begin(), overhead.end(), 0) / iterations; +} + +#endif // CYCLE_I386_H diff --git a/src/boost/libs/coroutine2/performance/cycle_x86-64.hpp b/src/boost/libs/coroutine2/performance/cycle_x86-64.hpp new file mode 100644 index 000000000..625028579 --- /dev/null +++ b/src/boost/libs/coroutine2/performance/cycle_x86-64.hpp @@ -0,0 +1,79 @@ + +// Copyright Oliver Kowalke 2014. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef CYCLE_X86_64_H +#define CYCLE_X86_64_H + +#include <algorithm> +#include <numeric> +#include <cstddef> +#include <vector> + +#include <boost/assert.hpp> +#include <boost/bind.hpp> +#include <boost/cstdint.hpp> + +#define BOOST_CONTEXT_CYCLE + +typedef boost::uint64_t cycle_type; + +#if _MSC_VER >= 1400 +# include <intrin.h> +# pragma intrinsic(__rdtsc) +inline +cycle_type cycles() +{ return __rdtsc(); } +#elif defined(__INTEL_COMPILER) || defined(__ICC) || defined(_ECC) || defined(__ICL) +inline +cycle_type cycles() +{ return __rdtsc(); } +#elif defined(__GNUC__) || defined(__SUNPRO_C) +inline +cycle_type cycles() +{ + boost::uint32_t lo, hi; + + __asm__ __volatile__ ( + "xorl %%eax, %%eax\n" + "cpuid\n" + ::: "%rax", "%rbx", "%rcx", "%rdx" + ); + __asm__ __volatile__ ("rdtsc" : "=a" (lo), "=d" (hi) ); + __asm__ __volatile__ ( + "xorl %%eax, %%eax\n" + "cpuid\n" + ::: "%rax", "%rbx", "%rcx", "%rdx" + ); + + return ( cycle_type)hi << 32 | lo; +} +#else +# error "this compiler is not supported" +#endif + +struct cycle_overhead +{ + cycle_type operator()() + { + cycle_type start( cycles() ); + return cycles() - start; + } +}; + +inline +cycle_type overhead_cycle() +{ + std::size_t iterations( 10); + std::vector< cycle_type > overhead( iterations, 0); + for ( std::size_t i( 0); i < iterations; ++i) + std::generate( + overhead.begin(), overhead.end(), + cycle_overhead() ); + BOOST_ASSERT( overhead.begin() != overhead.end() ); + return std::accumulate( overhead.begin(), overhead.end(), 0) / iterations; +} + +#endif // CYCLE_X86_64_H diff --git a/src/boost/libs/coroutine2/performance/performance.cpp b/src/boost/libs/coroutine2/performance/performance.cpp new file mode 100644 index 000000000..dcc916d44 --- /dev/null +++ b/src/boost/libs/coroutine2/performance/performance.cpp @@ -0,0 +1,98 @@ + +// Copyright Oliver Kowalke 2014. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include <cstdlib> +#include <iostream> +#include <stdexcept> +#include <string> + +#include <boost/chrono.hpp> +#include <boost/coroutine2/all.hpp> +#include <boost/cstdint.hpp> +#include <boost/program_options.hpp> + +#include "bind_processor.hpp" +#include "clock.hpp" +#include "cycle.hpp" + +boost::uint64_t jobs = 1000; + +void fn( boost::coroutines2::coroutine< void >::push_type & c) { + while ( true) { + c(); + } +} + +duration_type measure_time_void( duration_type overhead) { + boost::coroutines2::coroutine< void >::pull_type c{ fn }; + time_point_type start( clock_type::now() ); + for ( std::size_t i = 0; i < jobs; ++i) { + c(); + } + duration_type total = clock_type::now() - start; + total -= overhead_clock(); // overhead of measurement + total /= jobs; // loops + total /= 2; // 2x jump_fcontext + + return total; +} + +# ifdef BOOST_CONTEXT_CYCLE +cycle_type measure_cycles_void( cycle_type overhead) { + boost::coroutines2::coroutine< void >::pull_type c{ fn }; + cycle_type start( cycles() ); + for ( std::size_t i = 0; i < jobs; ++i) { + c(); + } + cycle_type total = cycles() - start; + total -= overhead; // overhead of measurement + total /= jobs; // loops + total /= 2; // 2x jump_fcontext + + return total; +} +# endif + +int main( int argc, char * argv[]) { + try { + bool bind = false; + boost::program_options::options_description desc("allowed options"); + desc.add_options() + ("help", "help message") + ("bind,b", boost::program_options::value< bool >( & bind), "bind thread to CPU") + ("jobs,j", boost::program_options::value< boost::uint64_t >( & jobs), "jobs to run"); + boost::program_options::variables_map vm; + boost::program_options::store( + boost::program_options::parse_command_line( + argc, + argv, + desc), + vm); + boost::program_options::notify( vm); + if ( vm.count("help") ) { + std::cout << desc << std::endl; + return EXIT_SUCCESS; + } + if ( bind) { + bind_to_processor( 0); + } + duration_type overhead_c = overhead_clock(); + boost::uint64_t res = measure_time_void( overhead_c).count(); + std::cout << "average of " << res << " nano seconds" << std::endl; +#ifdef BOOST_CONTEXT_CYCLE + cycle_type overhead_y = overhead_cycle(); + res = measure_cycles_void( 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/coroutine2/performance/segmented/Jamfile.v2 b/src/boost/libs/coroutine2/performance/segmented/Jamfile.v2 new file mode 100644 index 000000000..d459354d2 --- /dev/null +++ b/src/boost/libs/coroutine2/performance/segmented/Jamfile.v2 @@ -0,0 +1,73 @@ + +# Copyright Oliver Kowalke 2014. +# Distributed under the Boost Software License, Version 1.0. +# (See accompanying file LICENSE_1_0.txt or copy at +# http://www.boost.org/LICENSE_1_0.txt) + +# For more information, see http://www.boost.org/ + +import common ; +import feature ; +import indirect ; +import modules ; +import os ; +import toolset ; + +project boost/coroutine2/performance/segmented_stack + : requirements + <library>/boost/chrono//boost_chrono + <library>/boost/coroutine//boost_coroutine + <library>/boost/program_options//boost_program_options + <segmented-stacks>on + <toolset>gcc,<segmented-stacks>on:<cxxflags>-fsplit-stack + <toolset>gcc,<segmented-stacks>on:<cxxflags>-DBOOST_USE_SEGMENTED_STACKS + <toolset>clang,<segmented-stacks>on:<cxxflags>-fsplit-stack + <toolset>clang,<segmented-stacks>on:<cxxflags>-DBOOST_USE_SEGMENTED_STACKS + <link>static + <threading>multi + <cxxflags>-DBOOST_DISABLE_ASSERTS + <optimization>speed + <variant>release + ; + +alias sources + : ../bind_processor_aix.cpp + : <target-os>aix + ; + +alias sources + : ../bind_processor_freebsd.cpp + : <target-os>freebsd + ; + +alias sources + : ../bind_processor_hpux.cpp + : <target-os>hpux + ; + +alias sources + : ../bind_processor_linux.cpp + : <target-os>linux + ; + +alias sources + : ../bind_processor_solaris.cpp + : <target-os>solaris + ; + +alias sources + : ../bind_processor_windows.cpp + : <target-os>windows + ; + +explicit sources ; + +exe performance_create_segmented + : sources + performance_create_segmented.cpp + ; + +exe performance_switch + : sources + performance_switch.cpp + ; diff --git a/src/boost/libs/coroutine2/performance/segmented/performance_create_segmented.cpp b/src/boost/libs/coroutine2/performance/segmented/performance_create_segmented.cpp new file mode 100644 index 000000000..d2313bfdf --- /dev/null +++ b/src/boost/libs/coroutine2/performance/segmented/performance_create_segmented.cpp @@ -0,0 +1,105 @@ + +// Copyright Oliver Kowalke 2014. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include <cstdlib> +#include <iostream> +#include <stdexcept> + +#include <boost/chrono.hpp> +#include <boost/coroutine2/all.hpp> +#include <boost/cstdint.hpp> +#include <boost/program_options.hpp> + +#include "../bind_processor.hpp" +#include "../clock.hpp" +#include "../cycle.hpp" + +typedef boost::coroutines2::segmented_stack stack_allocator; +typedef boost::coroutines2::coroutine< void > coro_type; + +boost::uint64_t jobs = 1000; + +void fn( coro_type::push_type & c) +{ while ( true) c(); } + +duration_type measure_time( duration_type overhead) +{ + stack_allocator stack_alloc; + + time_point_type start( clock_type::now() ); + for ( std::size_t i = 0; i < jobs; ++i) { + coro_type::pull_type c( stack_alloc, fn); + } + duration_type total = clock_type::now() - start; + total -= overhead_clock(); // overhead of measurement + total /= jobs; // loops + + return total; +} + +# ifdef BOOST_CONTEXT_CYCLE +cycle_type measure_cycles( cycle_type overhead) +{ + stack_allocator stack_alloc; + + cycle_type start( cycles() ); + for ( std::size_t i = 0; i < jobs; ++i) { + coro_type::pull_type c( stack_alloc, fn); + } + cycle_type total = cycles() - start; + total -= overhead; // overhead of measurement + total /= jobs; // loops + + return total; +} +# endif + +int main( int argc, char * argv[]) +{ + try + { + bool bind = false; + boost::program_options::options_description desc("allowed options"); + desc.add_options() + ("help", "help message") + ("bind,b", boost::program_options::value< bool >( & bind), "bind thread to CPU") + ("jobs,j", boost::program_options::value< boost::uint64_t >( & jobs), "jobs to run"); + + boost::program_options::variables_map vm; + boost::program_options::store( + boost::program_options::parse_command_line( + argc, + argv, + desc), + vm); + boost::program_options::notify( vm); + + if ( vm.count("help") ) { + std::cout << desc << std::endl; + return EXIT_SUCCESS; + } + + if ( bind) bind_to_processor( 0); + + duration_type overhead_c = overhead_clock(); + std::cout << "overhead " << overhead_c.count() << " nano seconds" << std::endl; + boost::uint64_t res = measure_time( overhead_c).count(); + std::cout << "average of " << res << " nano seconds" << std::endl; +#ifdef BOOST_CONTEXT_CYCLE + cycle_type overhead_y = overhead_cycle(); + std::cout << "overhead " << overhead_y << " cpu cycles" << std::endl; + res = measure_cycles( overhead_y); + std::cout << "average of " << res << " cpu cycles" << std::endl; +#endif + + return EXIT_SUCCESS; + } + catch ( std::exception const& e) + { std::cerr << "exception: " << e.what() << std::endl; } + catch (...) + { std::cerr << "unhandled exception" << std::endl; } + return EXIT_FAILURE; +} diff --git a/src/boost/libs/coroutine2/performance/segmented/performance_switch.cpp b/src/boost/libs/coroutine2/performance/segmented/performance_switch.cpp new file mode 100644 index 000000000..b23c1e2f8 --- /dev/null +++ b/src/boost/libs/coroutine2/performance/segmented/performance_switch.cpp @@ -0,0 +1,202 @@ + +// Copyright Oliver Kowalke 2014. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include <cstdlib> +#include <iostream> +#include <stdexcept> +#include <string> + +#include <boost/chrono.hpp> +#include <boost/coroutine2/all.hpp> +#include <boost/cstdint.hpp> +#include <boost/program_options.hpp> + +#include "../bind_processor.hpp" +#include "../clock.hpp" +#include "../cycle.hpp" + +boost::uint64_t jobs = 1000; + +struct X +{ + std::string str; + + X( std::string const& str_) : + str( str_) + {} +}; + +const X x("abc"); + +void fn_void( boost::coroutines2::coroutine< void >::push_type & c) +{ while ( true) c(); } + +void fn_int( boost::coroutines2::coroutine< int >::push_type & c) +{ while ( true) c( 7); } + +void fn_x( boost::coroutines2::coroutine< X >::push_type & c) +{ + while ( true) c( x); +} + +duration_type measure_time_void( duration_type overhead) +{ + boost::coroutines2::segmented_stack stack_alloc; + boost::coroutines2::coroutine< void >::pull_type c( stack_alloc, fn_void); + + time_point_type start( clock_type::now() ); + for ( std::size_t i = 0; i < jobs; ++i) { + c(); + } + duration_type total = clock_type::now() - start; + total -= overhead_clock(); // overhead of measurement + total /= jobs; // loops + total /= 2; // 2x jump_fcontext + + return total; +} + +duration_type measure_time_int( duration_type overhead) +{ + boost::coroutines2::segmented_stack stack_alloc; + boost::coroutines2::coroutine< int >::pull_type c( stack_alloc, fn_int); + + time_point_type start( clock_type::now() ); + for ( std::size_t i = 0; i < jobs; ++i) { + c(); + } + duration_type total = clock_type::now() - start; + total -= overhead_clock(); // overhead of measurement + total /= jobs; // loops + total /= 2; // 2x jump_fcontext + + return total; +} + +duration_type measure_time_x( duration_type overhead) +{ + boost::coroutines2::segmented_stack stack_alloc; + boost::coroutines2::coroutine< X >::pull_type c( stack_alloc, fn_x); + + time_point_type start( clock_type::now() ); + for ( std::size_t i = 0; i < jobs; ++i) { + c(); + } + duration_type total = clock_type::now() - start; + total -= overhead_clock(); // overhead of measurement + total /= jobs; // loops + total /= 2; // 2x jump_fcontext + + return total; +} + +# ifdef BOOST_CONTEXT_CYCLE +cycle_type measure_cycles_void( cycle_type overhead) +{ + boost::coroutines2::segmented_stack stack_alloc; + boost::coroutines2::coroutine< void >::pull_type c( stack_alloc, fn_void); + + cycle_type start( cycles() ); + for ( std::size_t i = 0; i < jobs; ++i) { + c(); + } + cycle_type total = cycles() - start; + total -= overhead; // overhead of measurement + total /= jobs; // loops + total /= 2; // 2x jump_fcontext + + return total; +} + +cycle_type measure_cycles_int( cycle_type overhead) +{ + boost::coroutines2::segmented_stack stack_alloc; + boost::coroutines2::coroutine< int >::pull_type c( stack_alloc, fn_int); + + cycle_type start( cycles() ); + for ( std::size_t i = 0; i < jobs; ++i) { + c(); + } + cycle_type total = cycles() - start; + total -= overhead; // overhead of measurement + total /= jobs; // loops + total /= 2; // 2x jump_fcontext + + return total; +} + +cycle_type measure_cycles_x( cycle_type overhead) +{ + boost::coroutines2::segmented_stack stack_alloc; + boost::coroutines2::coroutine< X >::pull_type c( stack_alloc, fn_x); + + cycle_type start( cycles() ); + for ( std::size_t i = 0; i < jobs; ++i) { + c(); + } + cycle_type total = cycles() - start; + total -= overhead; // overhead of measurement + total /= jobs; // loops + total /= 2; // 2x jump_fcontext + + return total; +} +# endif + +int main( int argc, char * argv[]) +{ + try + { + bool bind = false; + boost::program_options::options_description desc("allowed options"); + desc.add_options() + ("help", "help message") + ("bind,b", boost::program_options::value< bool >( & bind), "bind thread to CPU") + ("jobs,j", boost::program_options::value< boost::uint64_t >( & jobs), "jobs to run"); + + boost::program_options::variables_map vm; + boost::program_options::store( + boost::program_options::parse_command_line( + argc, + argv, + desc), + vm); + boost::program_options::notify( vm); + + if ( vm.count("help") ) { + std::cout << desc << std::endl; + return EXIT_SUCCESS; + } + + if ( bind) bind_to_processor( 0); + + duration_type overhead_c = overhead_clock(); + std::cout << "overhead " << overhead_c.count() << " nano seconds" << std::endl; + boost::uint64_t res = measure_time_void( overhead_c).count(); + std::cout << "void: average of " << res << " nano seconds" << std::endl; + res = measure_time_int( overhead_c).count(); + std::cout << "int: average of " << res << " nano seconds" << std::endl; + res = measure_time_x( overhead_c).count(); + std::cout << "X: average of " << res << " nano seconds" << std::endl; +#ifdef BOOST_CONTEXT_CYCLE + cycle_type overhead_y = overhead_cycle(); + std::cout << "overhead " << overhead_y << " cpu cycles" << std::endl; + res = measure_cycles_void( overhead_y); + std::cout << "void: average of " << res << " cpu cycles" << std::endl; + res = measure_cycles_int( overhead_y); + std::cout << "int: average of " << res << " cpu cycles" << std::endl; + res = measure_cycles_x( overhead_y); + std::cout << "X: average of " << res << " cpu cycles" << std::endl; +#endif + + return EXIT_SUCCESS; + } + catch ( std::exception const& e) + { std::cerr << "exception: " << e.what() << std::endl; } + catch (...) + { std::cerr << "unhandled exception" << std::endl; } + return EXIT_FAILURE; +} diff --git a/src/boost/libs/coroutine2/test/Jamfile.v2 b/src/boost/libs/coroutine2/test/Jamfile.v2 new file mode 100644 index 000000000..498a763d4 --- /dev/null +++ b/src/boost/libs/coroutine2/test/Jamfile.v2 @@ -0,0 +1,88 @@ + +# Copyright Oliver Kowalke 2014. +# Distributed under the Boost Software License, Version 1.0. +# (See accompanying file LICENSE_1_0.txt or copy at +# http://www.boost.org/LICENSE_1_0.txt) + +import common ; +import feature ; +import indirect ; +import modules ; +import os ; +import path ; +import testing ; +import toolset ; +import ../../config/checks/config : requires ; + +project boost/coroutine2/test + : requirements + <library>../../test/build//boost_unit_test_framework + <library>/boost/context//boost_context + <toolset>gcc,<segmented-stacks>on:<cxxflags>-fsplit-stack + <toolset>gcc,<segmented-stacks>on:<cxxflags>-DBOOST_USE_SEGMENTED_STACKS + <toolset>clang,<segmented-stacks>on:<cxxflags>-fsplit-stack + <toolset>clang,<segmented-stacks>on:<cxxflags>-DBOOST_USE_SEGMENTED_STACKS + <link>static + <threading>multi + <optimization>speed + <variant>release + ; + +rule native-impl ( properties * ) +{ + local result ; + if ( <target-os>darwin in $(properties) || <target-os>android in $(properties) ) + { + result = <build>no ; + } + else if ( ! ( <target-os>windows in $(properties) ) ) + { + result = <context-impl>ucontext ; + } + else + { + result = <context-impl>winfib ; + } + return $(result) ; +} + +test-suite minimal : +[ run test_coroutine.cpp : + : : + <context-impl>fcontext + [ requires cxx11_auto_declarations + cxx11_constexpr + cxx11_defaulted_functions + cxx11_final + cxx11_hdr_tuple + cxx11_lambdas + cxx11_noexcept + cxx11_nullptr + cxx11_rvalue_references + cxx11_template_aliases + cxx11_variadic_templates ] + : test_coroutine_asm ] + +[ run test_coroutine.cpp : + : : + <conditional>@native-impl + [ requires cxx11_auto_declarations + cxx11_constexpr + cxx11_defaulted_functions + cxx11_final + cxx11_hdr_tuple + cxx11_lambdas + cxx11_noexcept + cxx11_nullptr + cxx11_rvalue_references + cxx11_template_aliases + cxx11_variadic_templates ] + : test_coroutine_native ] ; + +test-suite full : + minimal ; + +test-suite extra ; + +explicit minimal ; +explicit extra ; diff --git a/src/boost/libs/coroutine2/test/test_coroutine.cpp b/src/boost/libs/coroutine2/test/test_coroutine.cpp new file mode 100644 index 000000000..0a9da6ac0 --- /dev/null +++ b/src/boost/libs/coroutine2/test/test_coroutine.cpp @@ -0,0 +1,679 @@ + +// Copyright Oliver Kowalke 2014. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include <algorithm> +#include <cstdio> +#include <iostream> +#include <sstream> +#include <stdexcept> +#include <string> +#include <tuple> +#include <vector> + +#include <boost/assert.hpp> +#include <boost/test/unit_test.hpp> + +#include <boost/coroutine2/coroutine.hpp> + +namespace coro = boost::coroutines2; + +int value1 = 0; +std::string value2 = ""; +bool value3 = false; +double value4 = .0; +int * value5 = nullptr; +int& value6 = value1; +int& value7 = value1; +int value8 = 0; +int value9 = 0; + +struct X +{ + X() { value1 = 7; } + ~X() { value1 = 0; } + + X( X const&) = delete; + X & operator=( X const&) = delete; +}; + +class copyable +{ +public: + bool state; + + copyable() : + state( false) + {} + + copyable( int) : + state( true) + {} + + void operator()( coro::coroutine< int >::push_type &) + { value3 = state; } +}; + +class moveable +{ +public: + bool state; + + moveable() : + state( false) + {} + + moveable( int) : + state( true) + {} + + moveable( moveable const&) = delete; + moveable & operator=( moveable const&) = delete; + + moveable( moveable && other) : + state( false) + { std::swap( state, other.state); } + + moveable & operator=( moveable && other) + { + if ( this != & other) { + state = other.state; + other.state = false; + } + return * this; + } + + void operator()( coro::coroutine< int >::push_type &) + { value3 = state; } +}; + +class movedata +{ +public: + int i; + + movedata( int i_) : + i( i_) + {} + + movedata( movedata const&) = delete; + movedata & operator=( movedata const&) = delete; + + movedata( movedata && other) : + i( 0) + { std::swap( i, other.i); } + + movedata & operator=( movedata && other) + { + if ( this != & other) { + i = other.i; + other.i = 0; + } + return * this; + } +}; + +struct my_exception {}; + +void f1( coro::coroutine< void >::push_type & c) +{ + while ( c) + c(); +} + +void f2( coro::coroutine< void >::push_type &) +{ ++value1; } + +void f3( coro::coroutine< void >::push_type & c) +{ + ++value1; + c(); + ++value1; +} + +void f4( coro::coroutine< int >::push_type & c) +{ + c( 3); + c( 7); +} + +void f5( coro::coroutine< std::string >::push_type & c) +{ + std::string res("abc"); + c( res); + res = "xyz"; + c( res); +} + +void f6( coro::coroutine< int >::pull_type & c) +{ value1 = c.get(); } + +void f7( coro::coroutine< std::string >::pull_type & c) +{ value2 = c.get(); } + +void f8( coro::coroutine< std::tuple< double, double > >::pull_type & c) +{ + double x = 0, y = 0; + std::tie( x, y) = c.get(); + value4 = x + y; + c(); + std::tie( x, y) = c.get(); + value4 = x + y; +} + +void f9( coro::coroutine< int * >::pull_type & c) +{ value5 = c.get(); } + +void f91( coro::coroutine< int const* >::pull_type & c) +{ value5 = const_cast< int * >( c.get() ); } + +void f10( coro::coroutine< int & >::pull_type & c) +{ + int & i = c.get(); + value5 = const_cast< int * >( & i); +} + +void f101( coro::coroutine< int const& >::pull_type & c) +{ + int const& i = c.get(); + value5 = const_cast< int * >( & i); +} + +void f11( coro::coroutine< std::tuple< int, int > >::pull_type & c) +{ + std::tie( value8, value9) = c.get(); +} + +void f12( coro::coroutine< void >::pull_type & c) +{ + value1 = 7; + X x_; + c(); + c(); +} + +void f16( coro::coroutine< int >::push_type & c) +{ + c( 1); + c( 2); + c( 3); + c( 4); + c( 5); +} + +void f17( coro::coroutine< int >::pull_type & c, std::vector< int > & vec) +{ + int x = c.get(); + while ( 5 > x) + { + vec.push_back( x); + x = c().get(); + } +} + +void f20( coro::coroutine< int >::push_type &) +{} + +void f21( coro::coroutine< int >::pull_type & c) +{ + while ( c) + { + value1 = c.get(); + c(); + } +} + +void f22( coro::coroutine< movedata >::pull_type & c) +{ + movedata mv( c.get() ); + value1 = mv.i; +} + +void test_move() +{ + { + coro::coroutine< int >::pull_type coro1( f20); + coro::coroutine< int >::pull_type coro2( f16); + BOOST_CHECK( ! coro1); + BOOST_CHECK( coro2); + BOOST_CHECK_EQUAL( 1, coro2.get() ); + coro2(); + BOOST_CHECK_EQUAL( 2, coro2.get() ); + coro1 = std::move( coro2); + BOOST_CHECK( coro1); + BOOST_CHECK( ! coro2); + coro1(); + BOOST_CHECK_EQUAL( 3, coro1.get() ); + BOOST_CHECK( ! coro2); + } + + { + value3 = false; + copyable cp( 3); + BOOST_CHECK( cp.state); + BOOST_CHECK( ! value3); + coro::coroutine< int >::pull_type coro( cp); + BOOST_CHECK( cp.state); + BOOST_CHECK( value3); + } + + { + value3 = false; + moveable mv( 7); + BOOST_CHECK( mv.state); + BOOST_CHECK( ! value3); + coro::coroutine< int >::pull_type coro( std::move( mv) ); + BOOST_CHECK( ! mv.state); + BOOST_CHECK( value3); + } + + { + value1 = 0; + movedata mv( 7); + BOOST_CHECK_EQUAL( 0, value1); + BOOST_CHECK_EQUAL( 7, mv.i); + coro::coroutine< movedata >::push_type coro( f22); + coro( std::move( mv) ); + BOOST_CHECK_EQUAL( 7, value1); + BOOST_CHECK_EQUAL( 0, mv.i); + } +} + +void test_complete() +{ + value1 = 0; + + coro::coroutine< void >::pull_type coro( f2); + BOOST_CHECK( ! coro); + BOOST_CHECK_EQUAL( ( int)1, value1); +} + +void test_bind() +{ + value1 = 0; + + coro::coroutine< void >::pull_type coro( std::bind( f2, std::placeholders::_1) ); + BOOST_CHECK( ! coro); + BOOST_CHECK_EQUAL( ( int)1, value1); +} + +void test_jump() +{ + value1 = 0; + + coro::coroutine< void >::pull_type coro( f3); + BOOST_CHECK( coro); + BOOST_CHECK_EQUAL( ( int)1, value1); + coro(); + BOOST_CHECK( ! coro); + BOOST_CHECK_EQUAL( ( int)2, value1); +} + +void test_result_int() +{ + coro::coroutine< int >::pull_type coro( f4); + BOOST_CHECK( coro); + int result = coro.get(); + BOOST_CHECK( coro); + BOOST_CHECK_EQUAL( 3, result); + result = coro().get(); + BOOST_CHECK( coro); + BOOST_CHECK_EQUAL( 7, result); + coro(); + BOOST_CHECK( ! coro); +} + +void test_result_string() +{ + coro::coroutine< std::string >::pull_type coro( f5); + BOOST_CHECK( coro); + std::string result = coro.get(); + BOOST_CHECK( coro); + BOOST_CHECK_EQUAL( std::string("abc"), result); + result = coro().get(); + BOOST_CHECK( coro); + BOOST_CHECK_EQUAL( std::string("xyz"), result); + coro(); + BOOST_CHECK( ! coro); +} + +void test_arg_int() +{ + value1 = 0; + + coro::coroutine< int >::push_type coro( f6); + BOOST_CHECK( coro); + coro( 3); + BOOST_CHECK( ! coro); + BOOST_CHECK_EQUAL( 3, value1); +} + +void test_arg_string() +{ + value2 = ""; + + coro::coroutine< std::string >::push_type coro( f7); + BOOST_CHECK( coro); + coro( std::string("abc") ); + BOOST_CHECK( ! coro); + BOOST_CHECK_EQUAL( std::string("abc"), value2); +} + +void test_fp() +{ + value4 = 0; + + coro::coroutine< std::tuple< double, double > >::push_type coro( f8); + BOOST_CHECK( coro); + coro( std::make_tuple( 7.35, 3.14) ); + BOOST_CHECK( coro); + BOOST_CHECK_EQUAL( ( double) 10.49, value4); + + value4 = 0; + coro( std::make_tuple( 1.15, 3.14) ); + BOOST_CHECK( ! coro); + BOOST_CHECK_EQUAL( ( double) 4.29, value4); +} + +void test_ptr() +{ + value5 = nullptr; + + int a = 3; + coro::coroutine< int * >::push_type coro( f9); + BOOST_CHECK( coro); + coro( & a); + BOOST_CHECK( ! coro); + BOOST_CHECK_EQUAL( & a, value5); +} + +void test_const_ptr() +{ + value5 = nullptr; + + int a = 3; + coro::coroutine< int const* >::push_type coro( f91); + BOOST_CHECK( coro); + coro( & a); + BOOST_CHECK( ! coro); + BOOST_CHECK_EQUAL( & a, value5); +} + +void test_ref() +{ + value5 = nullptr; + + int a_ = 3; + int & a = a_; + coro::coroutine< int & >::push_type coro( f10); + BOOST_CHECK( coro); + coro( std::ref( a) ); + BOOST_CHECK( ! coro); + BOOST_CHECK_EQUAL( & a, value5); +} + +void test_const_ref() +{ + value5 = nullptr; + + int a = 3; + coro::coroutine< int const& >::push_type coro( f101); + BOOST_CHECK( coro); + coro( a); + BOOST_CHECK( ! coro); + BOOST_CHECK_EQUAL( & a, value5); +} + +void test_no_result() +{ + coro::coroutine< int >::pull_type coro( f20); + BOOST_CHECK( ! coro); +} + +void test_tuple() +{ + value8 = 0; + value9 = 0; + + int a = 3, b = 7; + std::tuple< int, int > tpl( a, b); + BOOST_CHECK_EQUAL( a, std::get< 0 >( tpl) ); + BOOST_CHECK_EQUAL( b, std::get< 1 >( tpl) ); + coro::coroutine< std::tuple< int, int > >::push_type coro( f11); + BOOST_CHECK( coro); + coro( tpl); + BOOST_CHECK( ! coro); + BOOST_CHECK_EQUAL( a, value8); + BOOST_CHECK_EQUAL( b, value9); +} + +void test_unwind() +{ + value1 = 0; + { + coro::coroutine< void >::push_type coro( f12); + BOOST_CHECK( coro); + BOOST_CHECK_EQUAL( ( int) 0, value1); + coro(); + BOOST_CHECK( coro); + BOOST_CHECK_EQUAL( ( int) 7, value1); + coro(); + BOOST_CHECK_EQUAL( ( int) 7, value1); + } + BOOST_CHECK_EQUAL( ( int) 0, value1); + int i = 0; + { + coro::coroutine< void >::push_type coro( + [&i](coro::coroutine< void >::pull_type &) mutable { + i = 7; + }); + } + { + BOOST_CHECK_EQUAL( ( int) 0, value1); + auto * coro = new coro::coroutine< void >::pull_type( + [](coro::coroutine< void >::push_type & coro) mutable { + X x; + coro(); + }); + BOOST_CHECK_EQUAL( ( int) 7, value1); + delete coro; + BOOST_CHECK_EQUAL( ( int) 0, value1); + } + { + BOOST_CHECK_EQUAL( ( int) 0, value1); + auto * coro = new coro::coroutine< void >::push_type( + [](coro::coroutine< void >::pull_type & coro) mutable { + X x; + coro(); + }); + ( * coro)(); + BOOST_CHECK_EQUAL( ( int) 7, value1); + delete coro; + BOOST_CHECK_EQUAL( ( int) 0, value1); + } +} + +void test_exceptions() +{ + std::string msg("abc"), value; + std::runtime_error ex( msg); + try + { + coro::coroutine< void >::push_type coro( + [&msg]( coro::coroutine< void >::pull_type &) { + throw std::runtime_error( msg); + }); + BOOST_CHECK( coro); + coro(); + BOOST_CHECK( ! coro); + BOOST_CHECK( false); + } + catch ( std::runtime_error const& ex) + { value = ex.what(); } + BOOST_CHECK_EQUAL( value, msg); +} + +void test_input_iterator() +{ + { + using std::begin; + using std::end; + + std::vector< int > vec; + coro::coroutine< int >::pull_type coro( f16); + coro::coroutine< int >::pull_type::iterator e = end( coro); + for ( + coro::coroutine< int >::pull_type::iterator i = begin( coro); + i != e; ++i) + { vec.push_back( * i); } + BOOST_CHECK_EQUAL( ( std::size_t)5, vec.size() ); + BOOST_CHECK_EQUAL( ( int)1, vec[0] ); + BOOST_CHECK_EQUAL( ( int)2, vec[1] ); + BOOST_CHECK_EQUAL( ( int)3, vec[2] ); + BOOST_CHECK_EQUAL( ( int)4, vec[3] ); + BOOST_CHECK_EQUAL( ( int)5, vec[4] ); + } + { + std::vector< int > vec; + coro::coroutine< int >::pull_type coro( f16); + for ( auto i : coro) + { vec.push_back( i); } + BOOST_CHECK_EQUAL( ( std::size_t)5, vec.size() ); + BOOST_CHECK_EQUAL( ( int)1, vec[0] ); + BOOST_CHECK_EQUAL( ( int)2, vec[1] ); + BOOST_CHECK_EQUAL( ( int)3, vec[2] ); + BOOST_CHECK_EQUAL( ( int)4, vec[3] ); + BOOST_CHECK_EQUAL( ( int)5, vec[4] ); + } + { + int i1 = 1, i2 = 2, i3 = 3; + coro::coroutine< int& >::pull_type coro( + [&i1,&i2,&i3](coro::coroutine< int& >::push_type & c){ + c( i1); + c( i2); + c( i3); + }); + + int counter = 1; + for ( int & i : coro) { + switch ( counter) { + case 1: + BOOST_CHECK_EQUAL( & i1, & i); + break; + case 2: + BOOST_CHECK_EQUAL( & i2, & i); + break; + case 3: + BOOST_CHECK_EQUAL( & i3, & i); + break; + default: + BOOST_ASSERT( false); + } + ++counter; + } + } +} + +void test_output_iterator() +{ + using std::begin; + using std::end; + + int counter = 0; + std::vector< int > vec; + coro::coroutine< int >::push_type coro( + [&vec]( coro::coroutine< int >::pull_type & c) { + int x = c.get(); + while ( 5 > x) + { + vec.push_back( x); + x = c().get(); + } + }); + coro::coroutine< int >::push_type::iterator e( end( coro) ); + for ( coro::coroutine< int >::push_type::iterator i( begin( coro) ); + i != e; ++i) + { + i = ++counter; + } + BOOST_CHECK_EQUAL( ( std::size_t)4, vec.size() ); + BOOST_CHECK_EQUAL( ( int)1, vec[0] ); + BOOST_CHECK_EQUAL( ( int)2, vec[1] ); + BOOST_CHECK_EQUAL( ( int)3, vec[2] ); + BOOST_CHECK_EQUAL( ( int)4, vec[3] ); +} + +std::vector< int > vec; +coro::coroutine< void >::pull_type * child = nullptr; + +void start_child_coroutine() { + child = new coro::coroutine< void >::pull_type([](coro::coroutine< void >::push_type & yield) { + vec.push_back( 2); + yield(); + vec.push_back( 2); + yield(); + vec.push_back( 2); + yield(); + vec.push_back( 2); + yield(); + vec.push_back( 2); + yield(); + vec.push_back( 2); + }); +} + +coro::coroutine< void >::pull_type start_parent_coroutine() { + return coro::coroutine< void >::pull_type([=](coro::coroutine< void >::push_type & yield) { + vec.push_back( 1); + start_child_coroutine(); + yield(); + vec.push_back( 1); + }); +} + +void test_chaining() +{ + auto parent = start_parent_coroutine(); + while ( * child) { + ( * child)(); + } + BOOST_CHECK_EQUAL( 7, vec.size() ); + BOOST_CHECK_EQUAL( 1, vec[0]); + BOOST_CHECK_EQUAL( 2, vec[1]); + BOOST_CHECK_EQUAL( 2, vec[2]); + BOOST_CHECK_EQUAL( 2, vec[3]); + BOOST_CHECK_EQUAL( 2, vec[4]); + BOOST_CHECK_EQUAL( 2, vec[5]); + BOOST_CHECK_EQUAL( 2, vec[6]); +} + +boost::unit_test::test_suite * init_unit_test_suite( int, char* []) +{ + boost::unit_test::test_suite * test = + BOOST_TEST_SUITE("Boost.Coroutine2: coroutine test suite"); + + test->add( BOOST_TEST_CASE( & test_move) ); + test->add( BOOST_TEST_CASE( & test_complete) ); + test->add( BOOST_TEST_CASE( & test_bind) ); + test->add( BOOST_TEST_CASE( & test_jump) ); + test->add( BOOST_TEST_CASE( & test_result_int) ); + test->add( BOOST_TEST_CASE( & test_result_string) ); + test->add( BOOST_TEST_CASE( & test_arg_int) ); + test->add( BOOST_TEST_CASE( & test_arg_string) ); + test->add( BOOST_TEST_CASE( & test_fp) ); + test->add( BOOST_TEST_CASE( & test_ptr) ); + test->add( BOOST_TEST_CASE( & test_const_ptr) ); + test->add( BOOST_TEST_CASE( & test_no_result) ); + test->add( BOOST_TEST_CASE( & test_ref) ); + test->add( BOOST_TEST_CASE( & test_const_ref) ); + test->add( BOOST_TEST_CASE( & test_tuple) ); + test->add( BOOST_TEST_CASE( & test_unwind) ); + test->add( BOOST_TEST_CASE( & test_exceptions) ); + test->add( BOOST_TEST_CASE( & test_input_iterator) ); + test->add( BOOST_TEST_CASE( & test_output_iterator) ); + test->add( BOOST_TEST_CASE( & test_chaining) ); + + return test; +} |