From 3d578a95257a432a00d3ca899d75e3aa6c49c75e Mon Sep 17 00:00:00 2001 From: Oliver Kowalke Date: Sat, 24 Nov 2012 19:42:09 +0000 Subject: [PATCH] coroutine: initial commit [SVN r81511] --- doc/Jamfile.v2 | 25 + doc/acknowledgements.qbk | 17 + doc/attributes.qbk | 120 ++ doc/coro.qbk | 82 + doc/coroutine.qbk | 795 ++++++++++ doc/foo_bar.png | Bin 0 -> 10022 bytes doc/images/foo_bar.png | Bin 0 -> 12878 bytes doc/images/foo_bar_seq.png | Bin 0 -> 19656 bytes doc/intro.qbk | 335 ++++ doc/overview.qbk | 47 + doc/performance.qbk | 35 + doc/stack.qbk | 120 ++ example/Jamfile.v2 | 81 + example/asio/stream_client.cpp | 64 + example/asio/stream_server.cpp | 207 +++ example/echo.cpp | 46 + example/fibonacci.cpp | 43 + example/parallel.cpp | 51 + example/power.cpp | 50 + example/same_fringe.cpp | 81 + example/tree.h | 127 ++ example/unwind.cpp | 46 + include/boost/coroutine/all.hpp | 15 + include/boost/coroutine/attributes.hpp | 85 + include/boost/coroutine/coroutine.hpp | 1405 +++++++++++++++++ include/boost/coroutine/detail/arg.hpp | 61 + include/boost/coroutine/detail/config.hpp | 42 + .../boost/coroutine/detail/coroutine_base.hpp | 103 ++ .../detail/coroutine_base_resume.hpp | 237 +++ .../coroutine/detail/coroutine_caller.hpp | 57 + .../boost/coroutine/detail/coroutine_get.hpp | 54 + .../coroutine/detail/coroutine_object.hpp | 83 + .../detail/coroutine_object_result_0.ipp | 394 +++++ .../detail/coroutine_object_result_1.ipp | 565 +++++++ .../detail/coroutine_object_result_arity.ipp | 565 +++++++ .../detail/coroutine_object_void_0.ipp | 388 +++++ .../detail/coroutine_object_void_1.ipp | 546 +++++++ .../detail/coroutine_object_void_arity.ipp | 559 +++++++ .../boost/coroutine/detail/coroutine_op.hpp | 312 ++++ include/boost/coroutine/detail/exceptions.hpp | 28 + include/boost/coroutine/detail/flags.hpp | 34 + include/boost/coroutine/detail/holder.hpp | 88 ++ include/boost/coroutine/detail/param.hpp | 46 + .../detail/stack_allocator_posix.hpp | 165 ++ .../detail/stack_allocator_windows.hpp | 160 ++ include/boost/coroutine/flags.hpp | 27 + include/boost/coroutine/stack_allocator.hpp | 23 + index.html | 14 + performance/Jamfile.v2 | 64 + performance/bind_processor.hpp | 12 + performance/bind_processor_aix.cpp | 25 + performance/bind_processor_freebsd.cpp | 29 + performance/bind_processor_hpux.cpp | 31 + performance/bind_processor_linux.cpp | 30 + performance/bind_processor_solaris.cpp | 26 + performance/bind_processor_windows.cpp | 24 + performance/cycle.hpp | 26 + performance/cycle_i386.hpp | 83 + performance/cycle_x86-64.hpp | 79 + performance/performance.cpp | 119 ++ performance/simple_stack_allocator.hpp | 67 + performance/zeit.hpp | 53 + test/Jamfile.v2 | 25 + test/test_coroutine.cpp | 494 ++++++ 64 files changed, 9615 insertions(+) create mode 100644 doc/Jamfile.v2 create mode 100644 doc/acknowledgements.qbk create mode 100644 doc/attributes.qbk create mode 100644 doc/coro.qbk create mode 100644 doc/coroutine.qbk create mode 100644 doc/foo_bar.png create mode 100644 doc/images/foo_bar.png create mode 100644 doc/images/foo_bar_seq.png create mode 100644 doc/intro.qbk create mode 100644 doc/overview.qbk create mode 100644 doc/performance.qbk create mode 100644 doc/stack.qbk create mode 100644 example/Jamfile.v2 create mode 100644 example/asio/stream_client.cpp create mode 100644 example/asio/stream_server.cpp create mode 100644 example/echo.cpp create mode 100644 example/fibonacci.cpp create mode 100644 example/parallel.cpp create mode 100644 example/power.cpp create mode 100644 example/same_fringe.cpp create mode 100644 example/tree.h create mode 100644 example/unwind.cpp create mode 100644 include/boost/coroutine/all.hpp create mode 100644 include/boost/coroutine/attributes.hpp create mode 100644 include/boost/coroutine/coroutine.hpp create mode 100644 include/boost/coroutine/detail/arg.hpp create mode 100644 include/boost/coroutine/detail/config.hpp create mode 100644 include/boost/coroutine/detail/coroutine_base.hpp create mode 100644 include/boost/coroutine/detail/coroutine_base_resume.hpp create mode 100644 include/boost/coroutine/detail/coroutine_caller.hpp create mode 100644 include/boost/coroutine/detail/coroutine_get.hpp create mode 100644 include/boost/coroutine/detail/coroutine_object.hpp create mode 100644 include/boost/coroutine/detail/coroutine_object_result_0.ipp create mode 100644 include/boost/coroutine/detail/coroutine_object_result_1.ipp create mode 100644 include/boost/coroutine/detail/coroutine_object_result_arity.ipp create mode 100644 include/boost/coroutine/detail/coroutine_object_void_0.ipp create mode 100644 include/boost/coroutine/detail/coroutine_object_void_1.ipp create mode 100644 include/boost/coroutine/detail/coroutine_object_void_arity.ipp create mode 100644 include/boost/coroutine/detail/coroutine_op.hpp create mode 100644 include/boost/coroutine/detail/exceptions.hpp create mode 100644 include/boost/coroutine/detail/flags.hpp create mode 100644 include/boost/coroutine/detail/holder.hpp create mode 100644 include/boost/coroutine/detail/param.hpp create mode 100644 include/boost/coroutine/detail/stack_allocator_posix.hpp create mode 100644 include/boost/coroutine/detail/stack_allocator_windows.hpp create mode 100644 include/boost/coroutine/flags.hpp create mode 100644 include/boost/coroutine/stack_allocator.hpp create mode 100644 index.html create mode 100644 performance/Jamfile.v2 create mode 100644 performance/bind_processor.hpp create mode 100644 performance/bind_processor_aix.cpp create mode 100644 performance/bind_processor_freebsd.cpp create mode 100644 performance/bind_processor_hpux.cpp create mode 100644 performance/bind_processor_linux.cpp create mode 100644 performance/bind_processor_solaris.cpp create mode 100644 performance/bind_processor_windows.cpp create mode 100644 performance/cycle.hpp create mode 100644 performance/cycle_i386.hpp create mode 100644 performance/cycle_x86-64.hpp create mode 100644 performance/performance.cpp create mode 100644 performance/simple_stack_allocator.hpp create mode 100644 performance/zeit.hpp create mode 100644 test/Jamfile.v2 create mode 100644 test/test_coroutine.cpp diff --git a/doc/Jamfile.v2 b/doc/Jamfile.v2 new file mode 100644 index 0000000..d18dc6f --- /dev/null +++ b/doc/Jamfile.v2 @@ -0,0 +1,25 @@ +# (C) Copyright 2008 Anthony Williams +# +# Distributed under the Boost Software License, Version 1.0. (See accompanying +# file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +xml coro : coro.qbk ; + +boostbook standalone + : + coro + : + # HTML options first: + # How far down we chunk nested sections, basically all of them: + chunk.section.depth=3 + # Don't put the first section on the same page as the TOC: + chunk.first.sections=1 + # How far down sections get TOC's + toc.section.depth=10 + # Max depth in each TOC: + toc.max.depth=3 + # How far down we go with TOC's + generate.section.toc.level=10 + # Path for links to Boost: + boost.root=../../../.. + ; diff --git a/doc/acknowledgements.qbk b/doc/acknowledgements.qbk new file mode 100644 index 0000000..1387cc5 --- /dev/null +++ b/doc/acknowledgements.qbk @@ -0,0 +1,17 @@ +[/ + 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 +] + +[section:acknowledgements Acknowledgments] + +I'd like to thank Alex Hagen-Zanker, Christopher Kormanyos, Conrad Poelman, +Eugene Yakubovich, Giovanni Piero Deretta, Hartmut Kaiser, Jeffrey Lee Hellrung, +Nat Goodspeed, Robert Stewart, Vicente J. Botet Escriba and Yuriy Krasnoschek. + +Especially Eugene Yakubovich, Giovanni Piero Deretta and Vicente J. Botet +Escriba contributed many good ideas during the review. + +[endsect] diff --git a/doc/attributes.qbk b/doc/attributes.qbk new file mode 100644 index 0000000..c503b31 --- /dev/null +++ b/doc/attributes.qbk @@ -0,0 +1,120 @@ +[/ + 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 +] + +[section:attributes Attributes] + +Class `attributes` is used to transfers parameters required to setup a +coroutines's context. + + struct attributes + { + std::size_t size; + flag_unwind_t do_unwind; + bool preserve_fpu; + + attributes() BOOST_NOEXCEPT : + size( ctx::default_stacksize() ), + do_unwind( stack_unwind), + preserve_fpu( true) + {} + + explicit attributes( std::size_t size_) BOOST_NOEXCEPT : + size( size_), + do_unwind( stack_unwind), + preserve_fpu( true) + {} + + explicit attributes( flag_unwind_t do_unwind_) BOOST_NOEXCEPT : + size( ctx::default_stacksize() ), + do_unwind( do_unwind_), + preserve_fpu( true) + {} + + explicit attributes( bool preserve_fpu_) BOOST_NOEXCEPT : + size( ctx::default_stacksize() ), + do_unwind( stack_unwind), + preserve_fpu( preserve_fpu_) + {} + + explicit attributes( + std::size_t size_, + flag_unwind_t do_unwind_) BOOST_NOEXCEPT : + size( size_), + do_unwind( do_unwind_), + preserve_fpu( true) + {} + + explicit attributes( + std::size_t size_, + bool preserve_fpu_) BOOST_NOEXCEPT : + size( size_), + do_unwind( stack_unwind), + preserve_fpu( preserve_fpu_) + {} + + explicit attributes( + flag_unwind_t do_unwind_, + bool preserve_fpu_) BOOST_NOEXCEPT : + size( ctx::default_stacksize() ), + do_unwind( do_unwind_), + preserve_fpu( preserve_fpu_) + {} + }; + +[heading `attributes()`] +[variablelist +[[Effects:] [Default constructor using `ctx::default_stacksize()`, does unwind +the stack after coroutine/generator is complete and preserves FPU registers.]] +[[Throws:] [Nothing.]] +] + +[heading `attributes( std::size_t size)`] +[variablelist +[[Effects:] [Argument `size` defines stack size of the inner `context`. +Stack unwinding after termination and preserving FPU registers is set by +default.]] +[[Throws:] [Nothing.]] +] + +[heading `attributes( flag_unwind_t do_unwind)`] +[variablelist +[[Effects:] [Argument `do_unwind` determines if stack will be unwound after +termination or not. The default stacksize is used for the inner `context` +and FPU registers are preserved.]] +[[Throws:] [Nothing.]] +] + +[heading `attributes( bool preserve_fpu)`] +[variablelist +[[Effects:] [Argument `preserve_fpu` determines if FPU register have to be +preserved if a `context` switches. THe default stacksize is used for the +inner `context` and the stack will be unwound after termination.]] +[[Throws:] [Nothing.]] +] + +[heading `attributes( std::size_t size, flag_unwind_t do_unwind)`] +[variablelist +[[Effects:] [Arguments `size` and `do_unwind` are given by the user. +FPU registers preserved during each `context` switch.]] +[[Throws:] [Nothing.]] +] + +[heading `attributes( std::size_t size, bool preserve_fpu)`] +[variablelist +[[Effects:] [Arguments `size` and `preserve_fpu` are given by the user. +The stack is automatically unwound after coroutine/generator terminates.]] +[[Throws:] [Nothing.]] +] + +[heading `attributes( flag_unwind_t do_unwind, bool preserve_fpu)`] +[variablelist +[[Effects:] [Arguments `do_unwind` and `preserve_fpu` are given by the user. +The stack gets a default value of `ctx::default_stacksize()`.]] +[[Throws:] [Nothing.]] +] + +[endsect] diff --git a/doc/coro.qbk b/doc/coro.qbk new file mode 100644 index 0000000..9615952 --- /dev/null +++ b/doc/coro.qbk @@ -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 +] + +[article Coroutine + [quickbook 1.5] + [authors [Kowalke, Oliver]] + [copyright 2009 Oliver Kowalke] + [purpose C++ Library providing coroutine facility] + [category text] + [license + Distributed under the Boost Software License, Version 1.0. + (See accompanying file LICENSE_1_0.txt or copy at + [@http://www.boost.org/LICENSE_1_0.txt]) + ] +] + + +[def __boost_build__ [*Boost.Build]] +[def __boost_context__ [*Boost.Context]] +[def __boost_coroutine__ [*Boost.Coroutine]] +[def __boost_exception__ [*Boost.Exception]] +[def __boost_function_types__ [*Boost.FunctionTypes]] +[def __boost_move__ [*Boost.Move]] +[def __boost_mpl__ [*Boost.MPL]] +[def __boost_optional__ [*Boost.Optional]] +[def __boost_preprocessor__ [*Boost.Preprocessor]] +[def __boost_range__ [*Boost.Range]] +[def __boost_result_of__ [*Boost.ResultOf]] +[def __boost_smart_ptr__ [*Boost.SmartPtr]] +[def __boost_static_assert__ [*Boost.StaticAssert]] +[def __boost_tuple__ [*Boost.Tuple]] +[def __boost_type_traits__ [*Boost.TypeTraits]] +[def __boost_utility__ [*Boost.Utility]] +[def __boost_version__ [*Boost-1.52.0]] + +[def __ctx__ ['context]] +[def __coro__ ['coroutine]] +[def __coro_fn__ ['coroutine-function]] +[def __coros__ ['coroutines]] +[def __not_a_coro__ ['not-a-coroutine]] +[def __signature__ ['Signature]] +[def __stack_allocator__ ['stack-allocator]] +[def __stack_allocator_concept__ ['stack-allocator concept]] +[def __stack__ ['stack]] +[def __tls__ ['thread-local-storage]] + +[def __args__ ['boost::coroutines::coroutine<>::arguments]] +[def __attrs__ ['boost::coroutines::attributes]] +[def __begin__ ['boost::begin()]] +[def __bind__ ['boost::bind()]] +[def __coro_allocator__ ['boost::coroutines::stack_allocator]] +[def __coro_bool__ ['boost::coroutines::coroutine<>::operator bool]] +[def __coro_caller__ ['boost::coroutines::coroutine<>::caller_type]] +[def __coro_get__ ['boost::coroutines::coroutine<>::get()]] +[def __coro_ns__ ['boost::coroutines]] +[def __coro_op__ ['boost::coroutines::coroutine<>::operator()]] +[def __end__ ['boost::end()]] +[def __fcontext__ ['boost::contexts::fcontext_t]] +[def __fetch__ ['inbuf::fetch()]] +[def __forced_unwind__ ['boost::coroutines::detail::forced_unwind]] +[def __getline__ ['std::getline()]] +[def __handle_read__ ['session::handle_read()]] +[def __io_service__ ['boost::asio::io_sevice]] +[def __server__ ['server]] +[def __session__ ['session]] +[def __start__ ['session::start()]] +[def __thread__ ['boost::thread]] +[def __tie__ ['boost::tie]] +[def __tuple__ ['boost::tuple<>]] +[def __underflow__ ['stream_buf::underflow()]] + +[include overview.qbk] +[include intro.qbk] +[include coroutine.qbk] +[include attributes.qbk] +[include stack.qbk] +[include performance.qbk] +[include acknowledgements.qbk] diff --git a/doc/coroutine.qbk b/doc/coroutine.qbk new file mode 100644 index 0000000..972e45d --- /dev/null +++ b/doc/coroutine.qbk @@ -0,0 +1,795 @@ +[/ + 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 +] + +[section:coroutine Coroutine] + +Each instance of __coro__ has its own context of execution (CPU registers and +stack space) or represents __not_a_coro__ (similar to __thread__). +Objects of type __coro__ are moveable but not copyable and can be returned by a +function. + + boost::coroutines::coroutine< void() > f(); + + void g() + { + boost::coroutines::coroutine< void() > c( f() ); + c(); + } + +[note __boost_move__ is used to emulate rvalue references.] + + +[heading Creating a coroutine] + +A new __coro__ is created from a __coro_fn__ (function or functor) which will be +executed in a new __ctx__ (CPU registers and stack space). + +[note __coro_fn__ is required to return ['void] and accept a reference of type +__coro_caller__.] + +The template argument __signature__ determines the data-types transferred to +__coro_fn__ and from __coro_fn__ by calling __coro_op__ and __coro_get__. + + typedef boost::coroutines::coroutine< int( std::string const&) > coro_t; + + void f( coro_t::caller_type & ca) + { + ... + // access argument + std::string str( ca.get() ); + ... + ca( 7); + ... + } + + std::string str; + ... + coro_t c( f); + // pass argument + c( str); + // returned value + int res = c.get(); + + +The __coro_fn__ is started at __coro__ construction (similar to __thread__) +in a newly created __coro__ complete with registers, flags, stack and +instruction pointer. +If __coro_fn__ requires some arguments (types defined by __signature__) +on start-up those parameters must be applied to the __coro__ constructor. +A single arguments can be passed as it is: + + typedef boost::coroutines::coroutine< int( std::string const&) > coro_t; + + void f( coro_t::caller_type & ca); + + std::string str("abc"); + coro_t c( f, str); + + +For multiple arguments __args__ must be used (it is a typedef of __tuple__): + + typedef boost::coroutines::coroutine< int( int, std::string const&) > coro_t; + + void f( coro_t::caller_type & ca); + + std::string str("abc"); + coro_t c( f, coro_t::arguments( 7, str) ); + + +[note The maximum number of arguments is limited to 10 (limit defined by +__boost_tuple__).] + +[note Parameters bound with __bind__ to __coro_fn__ will not be part of the +__coro_op__ signature.] + +__attrs__, an additional constructor argument of __coro__, defines the stack +size, stack unwinding and floating-point preserving behaviour used for __ctx__ +construction. + +The __coro__ constructor uses the __stack_allocator_concept__ to allocate an +associated stack, and the destructor uses the same __stack_allocator_concept__ +to deallocate the stack. The default __stack_allocator_concept__ is +__stack_allocator__, but a custom stack-allocator can be passed to the +constructor. + + +[heading Calling a coroutine] + +The execution control is transferred to __coro__ at construction (__coro_fn__ +entered) - when control should be returned to the original calling routine, +invoke __coro_op__ on the first argument of type __coro_caller__ inside +__coro_fn__. __coro_caller__ is a typedef of __coro__ with an inverted +__signature__. Inverted __signature__ means that the return type becomes an +argument and vice versa. Multiple arguments are wrapped into __tuple__. + + void f( boost::coroutines::coroutine< std::string const&( int) & ca); + boost::coroutines::coroutine< int( std::string const&) > c1( f); + + void g( boost::coroutines::coroutine< boost::tuple< X, Y >( int) & ca); + boost::coroutines::coroutine< int( X, X) > c2( g); + + +The current coroutine information (registers, flags, and stack and instruction +pointer) is saved and the original context information is restored. Calling +__coro_op__ resumes execution in the coroutine after saving the new state of the +original routine. + + typedef boost::coroutines::coroutine< void() > coro_t; + + // void fn( boost::coroutines::coroutine< void() > & ca, int j) + void fn( coro_t::caller_type & ca, int j) + { + for( int i = 0; i < j; ++i) + { + std::cout << "fn(): local variable i == " << i << std::endl; + + // save current coroutine + // value of local variable is preserved + // transfer execution control back to main() + ca(); + + // coroutine<>::operator()() was called + // execution control transferred back from main() + } + } + + int main( int argc, char * argv[]) + { + // bind parameter '7' to coroutine-fn + coro_t c( boost::bind( fn, _1, 7) ); + + std::cout << "main() starts coroutine c" << std::endl; + + while ( c) + { + std::cout << "main() calls coroutine c" << std::endl; + // execution control is transferred to c + c(); + } + + std::cout << "Done" << std::endl; + + return EXIT_SUCCESS; + } + + output: + main() starts coroutine c + fn(): local variable i == 0 + main() calls coroutine c + fn(): local variable i == 1 + main() calls coroutine c + fn(): local variable i == 2 + main() calls coroutine c + fn(): local variable i == 3 + main() calls coroutine c + fn(): local variable i == 4 + main() calls coroutine c + fn(): local variable i == 5 + main() calls coroutine c + fn(): local variable i == 6 + main() calls coroutine c + Done + +[warning Calling __coro_op__ from inside the [_same] coroutine results in +undefined behaviour.] + + +[heading Transfer of data] + +__signature__, the template argument of __coro__, defines the types transferred +to and returned from the __coro_fn__, e.g. it determines the signature of +__coro_op__ and the return-type of __coro_get__. + +[note __coro_caller__ is not part of __signature__ and __coro_fn__ is required +to return void and accept __coro_caller__ as argument.] + +__coro_op__ accepts arguments as defined in __signature__ and returns a +reference to __coro__. The arguments passed to __coro_op__, in one coroutine, +is returned (as a __tuple__) by __coro_get__ in the other coroutine. +If __coro__ is constructed and arguments are passed to the constructor, the +__coro_fn__ will be entered and the arguments are accessed thorough __coro_get__ +in __coro_fn__ on entry. + +The value given to __coro_op__ of __coro_caller__, in one coroutine, is returned by +__coro_get__ in the other routine. + + typedef boost::coroutines::coroutine< int( int) > coro_t; + + void fn( coro_t::caller_type & ca) + { + // access the integer argument given to coroutine ctor + int i = ca.get(); + std::cout << "fn(): local variable i == " << i << std::endl; + + // save current coroutine context and + // transfer execution control back to caller + // pass content of variable 'i' to caller + // after execution control is returned back coroutine<>::operator() + // returns and the transferred integer s accessed via coroutine<>::get() + i = ca( i).get(); + + // i == 10 because c( 10) in main() + std::cout << "fn(): local variable i == " << i << std::endl; + ca( i); + } + + int main( int argc, char * argv[]) + { + std::cout << "main(): call coroutine c" << std::endl; + coro_t c( fn, 7); + + int x = c.get(); + std::cout << "main(): transferred value: " << x << std::endl; + + x = c( 10).get(); + std::cout << "main(): transferred value: " << x << std::endl; + + std::cout << "Done" << std::endl; + + return EXIT_SUCCESS; + } + + output: + main(): call coroutine c + fn(): local variable i == 7 + main(): transferred value: 7 + fn(): local variable i == 10 + main(): transferred value: 10 + Done + + +[heading __coro_fn__ with multiple arguments] + +If __coro_fn__ has more than one argument __coro_op__ has the same size of +arguments and __coro_get__ from __coro_caller__ returns a __tuple__ corresponding +to the arguments of __signature__. __tie__ helps to access the values stored in +the __tuple__ returned by __coro_get__. + + typedef boost::coroutines::coroutine< int(int,int) > coro_t; + + void fn( coro_t::caller_type & ca) + { + int a, b; + boost::tie( a, b) = ca.get(); + boost::tie( a, b) = ca( a + b).get(); + ca( a + b); + } + + int main( int argc, char * argv[]) + { + std::cout << "main(): call coroutine c" << std::endl; + coro_t coro( fn, coro_t::arguments( 3, 7) ); + + int res = coro.get(); + std::cout << "main(): 3 + 7 == " << res << std::endl; + + res = coro( 5, 7).get(); + std::cout << "main(): 5 + 7 == " << res << std::endl; + + std::cout << "Done" << std::endl; + + return EXIT_SUCCESS; + } + + output: + main(): call coroutine c + main(): 3 + 7 == 10 + main(): 5 + 7 == 12 + Done + + +[heading Transfer of pointers and references] + +You can transfer references and pointers from and to coroutines but as usual +you must take care (scope, no re-assignment of const references etc.). +In the following code `x` points to `local` which is allocated on stack of +`c`. When `c` goes out of scope the stack becomes deallocated. Using `x` +after `c` is gone will fail! + + struct X + { + void g(); + }; + + typedef boost::coroutines::coroutine< X*() > coro_t; + + void fn( coro_t::caller_t & ca) { + X local; + ca( & local); + } + + int main() { + X * x = 0; + { + coro_t c( fn); + x = c.get(); // let x point to X on stack owned by c + // stack gets unwound -> X will be destructed + } + x->g(); // segmentation fault! + return EXIT_SUCCESS; + } + + +[heading Range iterators] + +__boost_coroutine__ provides output- and input-iterators using __boost_range__. +`coroutine< T() >` can be used via output-iterators using __begin__ and __end__. + + typedef boost::coroutines::coroutine< int() > coro_t; + typedef boost::range_iterator< coro_t >::type iterator_t; + + void power( coro_t::caller_type & ca, int number, int exponent) + { + int counter = 0; + int result = 1; + while ( counter++ < exponent) + { + result = result * number; + ca( result); + } + } + + int main() + { + coro_t c( boost::bind( power, _1, 2, 8) ); + iterator_t e( boost::end( c) ); + for ( iterator_t i( boost::begin( c) ); i != e; ++i) + std::cout << * i << " "; + + std::cout << "\nDone" << std::endl; + + return EXIT_SUCCESS; + } + + output: + 2 4 8 16 32 64 128 256 + Done + +`BOOST_FOREACH` can be used to iterate over the coroutine range too. + + typedef boost::coroutines::coroutine< int() > coro_t; + typedef boost::range_iterator< coro_t >::type iterator_t; + + void power( coro_t::caller_type & ca, int number, int exponent) + { + int counter = 0; + int result = 1; + while ( counter++ < exponent) + { + result = result * number; + ca( result); + } + } + + int main() + { + coro_t c( boost::bind( power, _1, 2, 8) ); + BOOST_FOREACH( int i, c) + { std::cout << i << " "; } + + std::cout << "\nDone" << std::endl; + + return EXIT_SUCCESS; + } + + output: + 2 4 8 16 32 64 128 256 + Done + + +Input iterators are created from coroutines of type `coroutine< void( T) >`. + + + +[heading Exit a __coro_fn__] + +__coro_fn__ is exited with a simple return statement jumping back to the calling +routine. The __coro__ becomes complete, e.g. __coro_bool__ will return 'false'. + + typedef boost::coroutines::coroutine< int(int,int) > coro_t; + + void fn( coro_t::caller_type & ca) + { + int a, b; + boost::tie( a, b) = ca.get(); + boost::tie( a, b) = ca( a + b).get(); + ca( a + b); + } + + int main( int argc, char * argv[]) + { + std::cout << "main(): call coroutine c" << std::endl; + coro_t coro( fn, coro_t::arguments( 3, 7) ); + + BOOST_ASSERT( coro); + int res = coro.get(); + std::cout << "main(): 3 + 7 == " << res << std::endl; + + res = coro( 5, 7).get(); + BOOST_ASSERT( ! coro); + std::cout << "main(): 5 + 7 == " << res << std::endl; + + std::cout << "Done" << std::endl; + + return EXIT_SUCCESS; + } + + output: + main(): call coroutine c + main(): 3 + 7 == 10 + main(): 5 + 7 == 12 + Done + +[important After returning from __coro_fn__ the __coro__ is complete (can not +resumed with __coro_op__).] + + +[heading Exceptions in __coro_fn__] + +An exception thrown inside __coro_fn__ will transferred via exception-pointer +(see __boost_exception__ for details) and re-thrown by constructor or +__coro_op__. + + typedef boost::coroutines::coroutine< void() > coro_t; + + void fn( coro_t::caller_type & ca) + { + ca(); + throw std::runtime_error("abc"); + } + + int main( int argc, char * argv[]) + { + coro_t c( f); + try + { + c(); + } + catch ( std::exception const& e) + { + std::cout << "exception catched:" << e.what() << std::endl; + return EXIT_FAILURE; + } + + std::cout << "Done" << std::endl; + + return EXIT_SUCCESS; + } + + output: + exception catched: abc + +[important Code executed by coroutine must not prevent the propagation of the +__forced_unwind__ exception. Absorbing that exception will cause stack +unwinding to fail. Thus, any code that catches all exceptions must re-throw the +pending exception.] + + try + { + // code that might throw + } + catch( forced_unwind) + { + throw; + } + catch(...) + { + // possibly not re-throw pending exception + } + + +[heading Stack unwinding] + +Sometimes it is necessary to unwind the stack of an unfinished coroutine to +destroy local stack variables so they can release allocated resources (RAII +pattern). The third argument of the coroutine constructor, `do_unwind`, +indicates whether the destructor should unwind the stack (stack is unwound by +default). + +Stack unwinding assumes the following preconditions: + +* The coroutine is not __not_a_coro__ +* The coroutine is not complete +* The coroutine is not running +* The coroutine owns a stack + +After unwinding, a __coro__ is complete. + + + typedef boost::coroutines::coroutine< void() > coro_t; + + struct X + { + X() + { std::cout << "X()" << std::endl; } + + ~X() + { std::cout << "~X()" << std::endl; } + }; + + void fn( coro_t::caller_type & ca) + { + X x; + + for ( int = 0;; ++i) + { + std::cout << "fn(): " << i << std::endl; + // transfer execution control back to main() + ca(); + } + } + + int main( int argc, char * argv[]) + { + { + coro_t c( fn, + boost::coroutines::attributes( + boost::ctx::default_stacksize(), + boost::coroutines::stack_unwind) ); + + c(); + c(); + c(); + c(); + c(); + + std::cout << "c is complete: " << std::boolalpha << c.is_complete() << "\n"; + } + + std::cout << "Done" << std::endl; + + return EXIT_SUCCESS; + } + + output: + X() + fn(): 0 + fn(): 1 + fn(): 2 + fn(): 3 + fn(): 4 + fn(): 5 + c is complete: false + ~X() + Done + +[important You must not swallow __forced_unwind__ exceptions!] + + +[heading FPU preserving] + +Some applications do not use floating-point registers and can disable preserving +fpu registers for performance reasons. + +[note According to the calling convetion the FPU registers are preserved by default.] + + +[section:coroutine Class `coroutine`] + + #include + + template< typename Signature > + class coroutine; + + template< + typename R, + typename ArgTypes... + > + class coroutine< R ( ArgTypes...) > + { + public: + typedef unspec-type caller_type; + typedef unspec-type arguments; + + coroutine(); + + template< + typename Fn, + typename StackAllocator = stack_allocator, + typename Allocator = std::allocator< coroutine > + > + coroutine( Fn fn, attributes const& attr = attributes(), + StackAllocator const& stack_alloc = StackAllocator(), + Allocator const& alloc = Allocator() ); + + template< + typename Fn, + typename StackAllocator = stack_allocator, + typename Allocator = std::allocator< coroutine > + > + coroutine( Fn fn, arguments const& args, + attributes const& attr = attributes(), + StackAllocator const& stack_alloc = StackAllocator(), + Allocator const& alloc = Allocator() ); + + template< + typename Fn, + typename StackAllocator = stack_allocator, + typename Allocator = std::allocator< coroutine > + > + coroutine( Fn && fn, attributes const& attr = attributes(), + StackAllocator stack_alloc = StackAllocator(), + Allocator const& alloc = Allocator() ); + + template< + typename Fn, + typename StackAllocator = stack_allocator, + typename Allocator = std::allocator< coroutine > + > + coroutine( Fn && fn arguments const& args, + attributes const& attr = attributes(), + StackAllocator stack_alloc = StackAllocator(), + Allocator const& alloc = Allocator() ); + + coroutine( coroutine && other); + + coroutine & operator=( coroutine && other); + + operator unspecified-bool-type() const; + + bool operator!() const; + + void swap( coroutine & other); + + bool empty() const; + + coroutine & operator()(A0 a0, ..., A9 a9); + + R get() const; + }; + + template< typename Signature > + void swap( coroutine< Signature > & l, coroutine< Signature > & r); + + template< typename T > + range_iterator< coroutine< T() > >::type begin( coroutine< T() > &); + template< typename T > + range_iterator< coroutine< void(T) > >::type begin( coroutine< void(T) > &); + + template< typename T > + range_iterator< coroutine< T() > >::type end( coroutine< T() > &); + template< typename T > + range_iterator< coroutine< void(T) > >::type end( coroutine< void(T) > &); + +[heading `coroutine()`] +[variablelist +[[Effects:] [Creates a coroutine representing __not_a_coro__.]] +[[Throws:] [Nothing.]] +] + +[heading `template< typename Fn, typename StackAllocator, typename Allocator > + coroutine( Fn fn, attributes const& attr, StackAllocator const& stack_alloc, Allocator const& alloc)`] +[variablelist +[[Preconditions:] [`size` > minimum_stacksize(), `size` < maximum_stacksize() +when ! is_stack_unbound().]] +[[Effects:] [Creates a coroutine which will execute `fn`. Argument `attr` +determines stack clean-up and preserving floating-point registers. +For allocating/deallocating the stack `stack_alloc` is used and internal +data are allocated by Allocator.]] +] + +[heading `template< typename Fn, typename StackAllocator, typename Allocator > + coroutine( Fn && fn, attributes const& attr, StackAllocator const& stack_alloc, Allocator const& alloc)`] +[variablelist +[[Preconditions:] [`size` > minimum_stacksize(), `size` < maximum_stacksize() +when ! is_stack_unbound().]] +[[Effects:] [Creates a coroutine which will execute `fn`. Argument `attr` +determines stack clean-up and preserving floating-point registers. +For allocating/deallocating the stack `stack_alloc` is used and internal +data are allocated by Allocator.]] +] + +[heading `coroutine( coroutine && other)`] +[variablelist +[[Effects:] [Moves the internal data of `other` to `*this`. +`other` becomes __not_a_coro__.]] +[[Throws:] [Nothing.]] +] + +[heading `coroutine & operator=( coroutine && other)`] +[variablelist +[[Effects:] [Destroys the internal data of `*this` and moves the +internal data of `other` to `*this`. `other` becomes __not_a_coro__.]] +[[Throws:] [Nothing.]] +] + +[heading `operator unspecified-bool-type() const`] +[variablelist +[[Returns:] [If `*this` refers to __not_a_coro__ or the coroutine-function +has returned (completed), the function returns false. Otherwise true.]] +[[Throws:] [Nothing.]] +] + +[heading `bool operator!() const`] +[variablelist +[[Returns:] [If `*this` refers to __not_a_coro__ or the coroutine-function +has returned (completed), the function returns true. Otherwise false.]] +[[Throws:] [Nothing.]] +] + +[heading `bool empty()`] +[variablelist +[[Returns:] [If `*this` refers to __not_a_coro__, the function returns true. +Otherwise false.]] +[[Throws:] [Nothing.]] +] + +[heading `coroutine<> & operator()(A0 a0, A9 a9)`] +[variablelist +[[Preconditions:] [operator unspecified-bool-type() returns true for `*this`.] +[[Effects:] [Execution control is transferred to __coro_fn__ and the arguments +`a0`,..., are passed to the coroutine-function.]] +[[Throws:] [Exceptions thrown inside __coro_fn__.]] +] + +[heading `R get()()`] +[variablelist +[[Preconditions:] [`*this` is not a __not_a_coro__, `! is_complete()`.]] +[[Returns:] [Returns data transferred from coroutine-function via __coro_op__ +of __coro_caller__.]] +[[Throws:] [Nothing.]] +] + +[heading `void swap( coroutine & other)`] +[variablelist +[[Effects:] [Swaps the internal data from `*this` with the values +of `other`.]] +[[Throws:] [Nothing.]] +] + +[heading `T caller_type::operator()( R)`] +[variablelist +[[Effects:] [Gives execution control back to calling context by returning +a value of type R. The return type of this function is a __tuple__ containing +the arguments passed to __coro_op__.]] +[[Throws:] [Nothing.]] +] + +[heading Non-member function `swap()`] + + template< typename Signature > + void swap( coroutine< Signature > & l, coroutine< Signature > & r); + +[variablelist +[[Effects:] [As if 'l.swap( r)'.]] +] + +[heading Non-member function `begin( coroutine< T() > &)`] + template< typename T > + range_iterator< coroutine< T() > >::type begin( coroutine< T() > &); + +[variablelist +[[Returns:] [Returns a range-iterator (input-iterator).]] +] + +[heading Non-member function `begin( coroutine< void(T) > &)`] + template< typename T > + range_iterator< coroutine< void(T) > >::type begin( coroutine< void(T) > &); + +[variablelist +[[Returns:] [Returns a range-iterator (output-iterator).]] +] + +[heading Non-member function `end( coroutine< T() > &)`] + template< typename T > + range_iterator< coroutine< T() > >::type end( coroutine< T() > &); + +[variablelist +[[Returns:] [Returns a end range-iterator (input-iterator).]] +] + +[heading Non-member function `end( coroutine< void(T) > &)`] + template< typename T > + range_iterator< coroutine< void(T) > >::type end( coroutine< void(T) > &); + +[variablelist +[[Returns:] [Returns a end range-iterator (output-iterator).]] +] + +[endsect] + +[endsect] diff --git a/doc/foo_bar.png b/doc/foo_bar.png new file mode 100644 index 0000000000000000000000000000000000000000..9fa59c516000574ebaec2726e09712b6883ba204 GIT binary patch literal 10022 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 literal 0 HcmV?d00001 diff --git a/doc/images/foo_bar.png b/doc/images/foo_bar.png new file mode 100644 index 0000000000000000000000000000000000000000..d01a9c078d61b5ce56e792652e83934415c9766d GIT binary patch literal 12878 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 + { + private: + boost::asio::io_service & io_service_; + boost::asio::ip::tcp::acceptor acceptor_; + + void handle_accept_( session * new_session, boost::system::error_code const& error) + { + if ( ! error) + { + // start asynchronous read + new_session->start(); + + // start asynchronous accept + start(); + } + } + + server( boost::asio::io_service & io_service, short port) : + io_service_( io_service), + acceptor_( + io_service_, + boost::asio::ip::tcp::endpoint( boost::asio::ip::tcp::v4(), port) ) + {} + + public: + typedef boost::shared_ptr< server > ptr_t; + + static ptr_t create( boost::asio::io_service & io_service, short port) + { return ptr_t( new server( io_service, port) ); } + + void start() + { + // create new session which gets started if asynchronous + // accept completes + session * new_session( new session( io_service_) ); + acceptor_.async_accept( + new_session->socket(), + boost::bind( & server::handle_accept_, this->shared_from_this(), + new_session, boost::asio::placeholders::error) ); + } + }; + + +Each __session__ communicates with the connected client and handles the +requests. +The application protocol in this example uses TCP-sockets as channel and +'newline' to separate the messages in the byte stream. +An application protocol is a set of rules for the order in which messages are +exchanged. +['std::istream] is used to extract the messages from the character stream . +Message 'exit' terminates the session. + + class session : private boost::noncopyable + { + private: + void handle_read_( coro_t::caller_type & self) + { + // create stream-buffer reading from socket + inbuf buf( socket_); + std::istream s( & buf); + + // messages are separated by 'newline' + std::string msg; + std::getline( s, msg); + std::cout << msg << std::endl; + + // terminate session for message 'exit' + // else do asynchronous read + if ( "exit" == msg) + io_service_.post( + boost::bind( + & session::destroy_, this) ); + else + start(); + } + + void destroy_() + { delete this; } + + boost::asio::io_service & io_service_; + boost::asio::ip::tcp::socket socket_; + + public: + session( boost::asio::io_service & io_service) : + io_service_( io_service), + socket_( io_service_) + { std::cout << "service(): " << socket_.remote_endpoint() << std::endl; } + + ~session() + { std::cout << "~service(): " << socket_.remote_endpoint() << std::endl; } + + boost::asio::ip::tcp::socket & socket() + { return socket_; } + + void start() + { + // register on io_service for asynchronous read + io_service_.async_read( + socket_, + boost::bind( + & session::handle_read_, this->shared_from_this(), _1, _2) ); + } + }; + + +Function __getline__ returns only if a 'newline' was read from the socket. +Therefore the application will block until 'newline' is received by the socket. +The stream-buffer used by the stream maintains an internal buffer which gets +(re-)filled by its function __underflow__. __underflow__ does the +read-operation on the socket. The C++ IO-streams framework does not provide an +easy way to create an continuation which represents reading bytes from the socket. + + +Coroutines help in this case to make the application non-blocking even if no +'newline' was received. +Class ['session] creates a coroutine which uses __handle_read__ as +__coro_fn__. On a new created ['session] ['start()] called starting the +coroutine. In the __coro_fn__ __handle_read__ the messages are received +via __getline__ in a loop until 'exit' is delivered. + + class session : private boost::noncopyable + { + private: + void handle_read_( coro_t::caller_type & ca) + { + // create stream-buffer with coroutine + inbuf buf( socket_, coro_, ca); + std::istream s( & buf); + + std::string msg; + do + { + // read message + // we not block if no newline was received yet + std::getline( s, msg); + std::cout << msg << std::endl; + } while ( msg != "exit"); + io_service_.post( + boost::bind( + & session::destroy_, this) ); + } + + void destroy_() + { delete this; } + + coro_t coro_; + boost::asio::io_service & io_service_; + boost::asio::ip::tcp::socket socket_; + + public: + session( boost::asio::io_service & io_service) : + coro_(), + io_service_( io_service), + socket_( io_service_) + { std::cout << "service(): " << socket_.remote_endpoint() << std::endl; } + + ~session() + { std::cout << "~service(): " << socket_.remote_endpoint() << std::endl; } + + boost::asio::ip::tcp::socket & socket() + { return socket_; } + + void start() + { + // create and start a coroutine + // handle_read_() is used as coroutine-function + coro_ = coro_t( boost::bind( & session::handle_read_, this, _1) ); + } + }; + + +The stream-buffer is created with __coro_caller__ and handles suspend/resume of this +code path depending on if bytes can be read from the socket. + + class inbuf : public std::streambuf, + private boost::noncopyable + { + private: + static const std::streamsize pb_size; + + enum + { bf_size = 16 }; + + int fetch_() + { + std::streamsize num = std::min( + static_cast< std::streamsize >( gptr() - eback() ), pb_size); + + std::memmove( + buffer_ + ( pb_size - num), + gptr() - num, num); + + // read bytes from the socket into internal buffer 'buffer_' + // make coro_t::operator() as callback, invoked if some + // bytes are read into 'buffer_' + s_.async_read_some( + boost::asio::buffer( buffer_ + pb_size, bf_size - pb_size), + boost::bind( & coro_t::operator(), & coro_, _1, _2) ); + // suspend this coroutine + ca_(); + + // coroutine was resumed by boost::asio::io_sevice + boost::system::error_code ec; + std::size_t n = 0; + + // check arguments + boost::tie( ec, n) = ca_.get(); + + // check if an error occurred + if ( ec) + { + setg( 0, 0, 0); + return -1; + } + + setg( buffer_ + pb_size - num, buffer_ + pb_size, buffer_ + pb_size + n); + return n; + } + + boost::asio::ip::tcp::socket & s_; + coro_t & coro_; + coro_t::caller_type & ca_; + char buffer_[bf_size]; + + protected: + virtual int underflow() + { + if ( gptr() < egptr() ) + return traits_type::to_int_type( * gptr() ); + + if ( 0 > fetch_() ) + return traits_type::eof(); + else + return traits_type::to_int_type( * gptr() ); + } + + public: + inbuf( + boost::asio::ip::tcp::socket & s, + coro_t & coro, + coro_t::caller_type & ca) : + s_( s), coro_( coro), ca_( ca), buffer_() + { setg( buffer_ + 4, buffer_ + 4, buffer_ + 4); } + }; + const std::streamsize inbuf::pb_size = 4; + + +__fetch__ uses __coro_op__ as callback for the asynchronous read-operation on the +socket and suspends itself (['ca_()] jumps back to __start__). If some bytes are +available in the socket receive buffer __io_service__ copies the bytes to the +internal buffer ['buffer_] and invokes the callback which resumes the coroutine +(['ca_()] returns). + + +[endsect] diff --git a/doc/overview.qbk b/doc/overview.qbk new file mode 100644 index 0000000..a8c2162 --- /dev/null +++ b/doc/overview.qbk @@ -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 +] + +[section:overview Overview] + +__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, __coro__ switches are +cooperative (programmer controls when a switch will happen). The kernel is not +involved in the coroutine switches. + +The implementation uses __boost_context__ for context switching. + +This library is a follow-up on +[@http://www.crystalclearsoftware.com/soc/coroutine/ Boost.Coroutine] +by Giovanni P. Deretta. + +In order to use the classes and functions described here, you can either include +the specific headers specified by the descriptions of each class or function, or +include the master library header: + + #include + +which includes all the other headers in turn. + +All functions and classes are contained in the namespace __coro_ns__. + +__boost_coroutine__ depends on __boost_context__, __boost_exception__, +__boost_function_types__, __boost_move__, __boost_mpl__, __boost_optional__, +__boost_preprocessor__, __boost_range__, __boost_result_of__, +__boost_smart_ptr__, __boost_static_assert__, __boost_tuple__, +__boost_type_traits__ as well as __boost_utility__ and requires +__boost_version__. + + +[endsect] diff --git a/doc/performance.qbk b/doc/performance.qbk new file mode 100644 index 0000000..6cea3f2 --- /dev/null +++ b/doc/performance.qbk @@ -0,0 +1,35 @@ +[/ + 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 +] + +[section:performance Performance] + +Performance of __boost_coroutine__ was measured on the platforms shown in the +following table. Performance measurements were taken using `rdtsc` and +`::clock_gettime()`, with overhead corrections, on x86 platforms. In each case, +stack protection was active, cache warm-up was accounted for, and the one +running thread was pinned to a single CPU. The code was compiled using the +build options, 'variant = release cxxflags = -DBOOST_DISABLE_ASSERTS'. + +The numbers in the table are the number of cycles per iteration, based upon an +average computed over 10 iterations. + +[table Perfomance of coroutine switch + [[Platform] [CPU cycles] [nanoseconds]] + [ + [AMD Athlon 64 DualCore 4400+ (32bit Linux)] + [58] + [65] + ] + [ + [Intel Core2 Q6700 (64bit Linux)] + [80] + [28] + ] +] + + +[endsect] diff --git a/doc/stack.qbk b/doc/stack.qbk new file mode 100644 index 0000000..f063ec0 --- /dev/null +++ b/doc/stack.qbk @@ -0,0 +1,120 @@ +[/ + 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 +] + +[section:stack Stack allocation] + +A __coro__ uses internally a __ctx__ which manages a set of registers and a stack. +The memory used by the stack is allocated/deallocated via a __stack_allocator__ +which is required to model a __stack_allocator_concept__. + + +[heading __stack_allocator_concept__] +A __stack_allocator__ must satisfy the __stack_allocator_concept__ requirements +shown in the following table, in which `a` is an object of a +__stack_allocator__ type, `p` is a `void *`, and `s` is a `std::size_t`: + +[table + [[expression][return type][notes]] + [ + [`a.allocate( s)`] + [`void *`] + [returns a pointer to `s` bytes allocated from the stack] + ] + [ + [`a.deallocate( p, s)`] + [`void`] + [deallocates `s` bytes of memory beginning at `p`, + a pointer previously returned by `a.allocate()`] + ] +] + +[important The implementation of `allocate()` might include logic to protect +against exceeding the context's available stack size rather than leaving it as +undefined behaviour.] + +[important Calling `deallocate()` with a pointer not returned by `allocate()` +results in undefined behaviour.] + +[note The stack is not required to be aligned; alignment takes place inside +__coro__.] + +[note Depending on the architecture `allocate()` returns an address from the +top of the stack (growing downwards) or the bottom of the stack (growing +upwards).] + + +[section:stack_allocator Class ['stack_allocator]] + +__boost_coroutine__ provides the class __coro_allocator__ which models +the __stack_allocator_concept__. +It appends a guard page at the end of each stack to protect against exceeding +the stack. If the guard page is accessed (read or write operation) a +segmentation fault/access violation is generated by the operating system. + +[note The appended `guard page` is [*not] mapped to physical memory, only +virtual addresses are used.] + + class stack_allocator + { + static bool is_stack_unbound(); + + static std::size_t maximum_stacksize(); + + static std::size_t default_stacksize(); + + static std::size_t minimum_stacksize(); + + void * allocate( std::size_t size); + + void deallocate( void * sp, std::size_t size); + } + +[heading `static bool is_stack_unbound()`] +[variablelist +[[Returns:] [Returns `true` if the environment defines no limit for the size of a stack.]] +] + +[heading `static std::size_t maximum_stacksize()`] +[variablelist +[[Preconditions:] [`is_stack_unbound()` returns `false`.]] +[[Returns:] [Returns the maximum size in bytes of stack defined by the environment.]] +] + +[heading `static std::size_t default_stacksize()`] +[variablelist +[[Returns:] [Returns a default stack size, which may be platform specific. +If the stack is unbound then the present implementation returns the maximum of +`64 kB` and `minimum_stacksize()`.]] +] + +[heading `static std::size_t minimum_stacksize()`] +[variablelist +[[Returns:] [Returns the minimum size in bytes of stack defined by the +environment (Win32 4kB/Win64 8kB, defined by rlimit on POSIX).]] +] + +[heading `void * allocate( std::size_t size)`] +[variablelist +[[Preconditions:] [`minimum_stacksize() > size` and +`! is_stack_unbound() && ( maximum_stacksize() < size)`.]] +[[Effects:] [Allocates memory of `size` Bytes and appends one guard page at the +end of the allocated memory.]] +[[Returns:] [Returns pointer to the start address of the new stack. Depending +on the architecture the stack grows downwards/upwards the returned address is +the highest/lowest address of the stack.]] +] + +[heading `void deallocate( void * sp, std::size_t size)`] +[variablelist +[[Preconditions:] [`sp` is valid, `minimum_stacksize() > size` and +`! is_stack_unbound() && ( maximum_stacksize() < size)`.]] +[[Effects:] [Deallocates the stack space.]] +] + +[endsect] + +[endsect] diff --git a/example/Jamfile.v2 b/example/Jamfile.v2 new file mode 100644 index 0000000..60e975c --- /dev/null +++ b/example/Jamfile.v2 @@ -0,0 +1,81 @@ +# 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 ; + +if [ os.name ] = SOLARIS +{ + lib socket ; + lib nsl ; +} +else if [ os.name ] = NT +{ + lib ws2_32 ; + lib mswsock ; +} +else if [ os.name ] = HPUX +{ + lib ipv6 ; +} + +project boost/coroutine/example + : requirements + /boost/context//boost_context + /boost/program_options//boost_program_options + /boost/system//boost_system + /boost/thread//boost_thread + BOOST_ALL_NO_LIB=1 + multi + SOLARIS:socket + SOLARIS:nsl + NT:_WIN32_WINNT=0x0501 + NT,gcc:ws2_32 + NT,gcc:mswsock + NT,gcc-cygwin:__USE_W32_SOCKETS + HPUX,gcc:_XOPEN_SOURCE_EXTENDED + HPUX:ipv6 + static + ; + +exe fibonacci + : fibonacci.cpp + ; + +exe echo + : echo.cpp + ; + +exe power + : power.cpp + ; + +exe parallel + : parallel.cpp + ; + +exe unwind + : unwind.cpp + ; + +exe same_fringe + : same_fringe.cpp + ; + +exe asio/stream_client + : asio/stream_client.cpp + ; + +exe asio/stream_server + : asio/stream_server.cpp + ; diff --git a/example/asio/stream_client.cpp b/example/asio/stream_client.cpp new file mode 100644 index 0000000..a20d5d3 --- /dev/null +++ b/example/asio/stream_client.cpp @@ -0,0 +1,64 @@ +#include +#include +#include + +#include +#include +#include +#include + +int main( int argc, char* argv[]) +{ + try + { + std::string host, msg, port; + int timeout = 1; + boost::program_options::options_description desc("allowed options"); + desc.add_options() + ("help,h", "help message") + ("host,a", boost::program_options::value< std::string >( & host), "host running the service") + ("port,p", boost::program_options::value< std::string >( & port), "port service is listening") + ("message,m", boost::program_options::value< std::string >( & msg), "message to send") + ("timeout,t", boost::program_options::value< int >( & timeout), "timeout between message 'exit' message in seconds"); + + 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; + } + + boost::asio::io_service io_service; + + boost::asio::ip::tcp::resolver resolver( io_service); + boost::asio::ip::tcp::resolver::query query( boost::asio::ip::tcp::v4(), host, port); + boost::asio::ip::tcp::resolver::iterator iterator( resolver.resolve( query) ); + + boost::asio::ip::tcp::socket s( io_service); + s.connect( * iterator); + + msg.append("\n"); // each message is terminated by newline + boost::asio::write( s, boost::asio::buffer( msg, msg.size() ) ); + std::cout << msg << " sendet" << std::endl; + boost::this_thread::sleep( boost::posix_time::seconds( timeout) ); + std::string exit("exit\n"); // newline + boost::asio::write( s, boost::asio::buffer( exit, exit.size() ) ); + std::cout << exit << " sendet" << std::endl; + + std::cout << "Done" << std::endl; + return EXIT_SUCCESS; + } + catch ( std::exception const& e) + { + std::cerr << "Exception: " << e.what() << "\n"; + } + + return EXIT_FAILURE; +} diff --git a/example/asio/stream_server.cpp b/example/asio/stream_server.cpp new file mode 100644 index 0000000..bb77906 --- /dev/null +++ b/example/asio/stream_server.cpp @@ -0,0 +1,207 @@ +#define NOMINMAX + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +typedef boost::tuple< boost::system::error_code, std::size_t > tuple_t; +typedef boost::coroutines::coroutine< void( boost::system::error_code, std::size_t) > coro_t; + +class inbuf : public std::streambuf, + private boost::noncopyable +{ +private: + static const std::streamsize pb_size; + + enum + { bf_size = 16 }; + + int fetch_() + { + std::streamsize num = std::min( + static_cast< std::streamsize >( gptr() - eback() ), pb_size); + + std::memmove( + buffer_ + ( pb_size - num), + gptr() - num, num); + + s_.async_read_some( + boost::asio::buffer( buffer_ + pb_size, bf_size - pb_size), + boost::bind( & coro_t::operator(), & coro_, _1, _2) ); + ca_(); + + boost::system::error_code ec; + std::size_t n = 0; + + boost::tie( ec, n) = ca_.get(); + if ( ec) + { + setg( 0, 0, 0); + return -1; + } + + setg( buffer_ + pb_size - num, buffer_ + pb_size, buffer_ + pb_size + n); + return n; + } + + boost::asio::ip::tcp::socket & s_; + coro_t & coro_; + coro_t::caller_type & ca_; + char buffer_[bf_size]; + +protected: + virtual int underflow() + { + if ( gptr() < egptr() ) + return traits_type::to_int_type( * gptr() ); + + if ( 0 > fetch_() ) + return traits_type::eof(); + else + return traits_type::to_int_type( * gptr() ); + } + +public: + inbuf( + boost::asio::ip::tcp::socket & s, + coro_t & coro, + coro_t::caller_type & ca) : + s_( s), coro_( coro), ca_( ca), buffer_() + { setg( buffer_ + 4, buffer_ + 4, buffer_ + 4); } +}; +const std::streamsize inbuf::pb_size = 4; + +class session : private boost::noncopyable +{ +private: + void handle_read_( coro_t::caller_type & ca) + { + inbuf buf( socket_, coro_, ca); + std::istream s( & buf); + + std::string msg; + do + { + std::getline( s, msg); + std::cout << msg << std::endl; + } while ( "exit" != msg); + io_service_.post( + boost::bind( + & session::destroy_, this) ); + } + + void destroy_() + { delete this; } + + coro_t coro_; + boost::asio::io_service & io_service_; + boost::asio::ip::tcp::socket socket_; + +public: + session( boost::asio::io_service & io_service) : + coro_(), + io_service_( io_service), + socket_( io_service_) + {} + + boost::asio::ip::tcp::socket & socket() + { return socket_; } + + void start() + { coro_ = coro_t( boost::bind( & session::handle_read_, this, _1) ); } +}; + +class server : public boost::enable_shared_from_this< server > +{ +private: + boost::asio::io_service & io_service_; + boost::asio::ip::tcp::acceptor acceptor_; + + void handle_accept_( session * new_session, boost::system::error_code const& error) + { + if ( ! error) + { + new_session->start(); + start(); + } + } + + server( boost::asio::io_service & io_service, short port) : + io_service_( io_service), + acceptor_( + io_service_, + boost::asio::ip::tcp::endpoint( boost::asio::ip::tcp::v4(), port) ) + {} + +public: + typedef boost::shared_ptr< server > ptr_t; + + static ptr_t create( boost::asio::io_service & io_service, short port) + { return ptr_t( new server( io_service, port) ); } + + void start() + { + session * new_session( new session( io_service_) ); + acceptor_.async_accept( + new_session->socket(), + boost::bind( & server::handle_accept_, this->shared_from_this(), + new_session, boost::asio::placeholders::error) ); + } +}; + +int main( int argc, char * argv[]) +{ + try + { + int port = 0; + boost::program_options::options_description desc("allowed options"); + desc.add_options() + ("help,h", "help message") + ("port,p", boost::program_options::value< int >( & port), "port service is listening"); + + 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; + } + { + boost::asio::io_service io_service; + io_service.post( + boost::bind( + & server::start, + server::create( + io_service, port) ) ); + io_service.run(); + } + std::cout << "Done" << std::endl; + + return EXIT_SUCCESS; + } + catch ( std::exception const& e) + { std::cerr << "Exception: " << e.what() << std::endl; } + + return EXIT_FAILURE; +} + diff --git a/example/echo.cpp b/example/echo.cpp new file mode 100644 index 0000000..92b7b5e --- /dev/null +++ b/example/echo.cpp @@ -0,0 +1,46 @@ + +// 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 +#include + +#include +#include + +typedef boost::coroutines::coroutine< void() > coro_t; + +void echo( coro_t & ca, int i) +{ + std::cout << i; + ca(); +} + +void runit( coro_t & ca) +{ + std::cout << "started! "; + for ( int i = 0; i < 10; ++i) + { + coro_t c( boost::bind( echo, _1, i) ); + while ( c) + c(); + ca(); + } +} + +int main( int argc, char * argv[]) +{ + { + coro_t c( runit); + while ( c) { + std::cout << "-"; + c(); + } + } + + std::cout << "\nDone" << std::endl; + + return EXIT_SUCCESS; +} diff --git a/example/fibonacci.cpp b/example/fibonacci.cpp new file mode 100644 index 0000000..dae7863 --- /dev/null +++ b/example/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 +#include + +#include +#include +#include + +typedef boost::coroutines::coroutine< int() > coro_t; +typedef boost::range_iterator< coro_t >::type iterator_t; + +void fibonacci( coro_t::caller_type & c) +{ + int first = 1, second = 1; + while ( true) + { + int third = first + second; + first = second; + second = third; + c( third); + } +} + +int main() +{ + coro_t c( fibonacci); + iterator_t it( boost::begin( c) ); + BOOST_ASSERT( boost::end( c) != it); + for ( int i = 0; i < 10; ++i) + { + std::cout << * it << " "; + ++it; + } + + std::cout << "\nDone" << std::endl; + + return EXIT_SUCCESS; +} diff --git a/example/parallel.cpp b/example/parallel.cpp new file mode 100644 index 0000000..1c1f683 --- /dev/null +++ b/example/parallel.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 +#include + +#include +#include + +typedef boost::coroutines::coroutine< void() > coroutine_t; + +void first( coroutine_t::caller_type & self) +{ + std::cout << "started first! "; + for ( int i = 0; i < 10; ++i) + { + self(); + std::cout << "a" << i; + } +} + +void second( coroutine_t::caller_type & self) +{ + std::cout << "started second! "; + for ( int i = 0; i < 10; ++i) + { + self(); + std::cout << "b" << i; + } +} + +int main( int argc, char * argv[]) +{ + { + coroutine_t c1( boost::bind( first, _1) ); + coroutine_t c2( boost::bind( second, _1) ); + while ( c1 && c2) { + c1(); + std::cout << " "; + c2(); + std::cout << " "; + } + } + + std::cout << "\nDone" << std::endl; + + return EXIT_SUCCESS; +} diff --git a/example/power.cpp b/example/power.cpp new file mode 100644 index 0000000..b00ca11 --- /dev/null +++ b/example/power.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 +#include + +#include +#include +#include +#include + +typedef boost::coroutines::coroutine< int() > coro1_t; +typedef boost::coroutines::coroutine< void( int) > coro2_t; +typedef boost::range_iterator< coro1_t >::type iterator_t; + +void power( coro2_t & c, int number, int exponent) +{ + int counter = 0; + int result = 1; + while ( counter++ < exponent) + { + result = result * number; + c( result); + } +} + +int main() +{ + { + std::cout << "using range functions" << std::endl; + coro1_t c( boost::bind( power, _1, 2, 8) ); + iterator_t e( boost::end( c) ); + for ( iterator_t i( boost::begin( c) ); i != e; ++i) + std::cout << * i << " "; + } + + { + std::cout << "\nusing BOOST_FOREACH" << std::endl; + coro1_t c( boost::bind( power, _1, 2, 8) ); + BOOST_FOREACH( int i, c) + { std::cout << i << " "; } + } + + std::cout << "\nDone" << std::endl; + + return EXIT_SUCCESS; +} diff --git a/example/same_fringe.cpp b/example/same_fringe.cpp new file mode 100644 index 0000000..99172d4 --- /dev/null +++ b/example/same_fringe.cpp @@ -0,0 +1,81 @@ + +// 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 +#include +#include +#include +#include + +#include +#include + +#include "tree.h" + +bool match_trees( coro_t & c1, coro_t & c2) +{ + typedef boost::range_iterator< coro_t >::type iterator_t; + iterator_t i1( boost::begin( c1) ); + iterator_t e1( boost::end( c1) ); + iterator_t i2( boost::begin( c2) ); + return std::equal( i1, e1, i2); +} + +std::pair< node::ptr_t, node::ptr_t > create_eq_trees() +{ + branch::ptr_t tree1 = branch::create( + leaf::create( "A"), + branch::create( + leaf::create( "B"), + leaf::create( "C") ) ); + + branch::ptr_t tree2 = branch::create( + branch::create( + leaf::create( "A"), + leaf::create( "B") ), + leaf::create( "C") ); + + return std::make_pair( tree1, tree2); +} + +std::pair< node::ptr_t, node::ptr_t > create_diff_trees() +{ + branch::ptr_t tree1 = branch::create( + leaf::create( "A"), + branch::create( + leaf::create( "B"), + leaf::create( "C") ) ); + + branch::ptr_t tree2 = branch::create( + branch::create( + leaf::create( "A"), + leaf::create( "X") ), + leaf::create( "C") ); + + return std::make_pair( tree1, tree2); +} + +int main() +{ + { + std::pair< node::ptr_t, node::ptr_t > pt = create_eq_trees(); + coro_t te1( boost::bind( enumerate_leafs, _1, pt.first) ); + coro_t te2( boost::bind( enumerate_leafs, _1, pt.second) ); + bool result = match_trees( te1, te2); + std::cout << std::boolalpha << "eq. trees matched == " << result << std::endl; + } + { + std::pair< node::ptr_t, node::ptr_t > pt = create_diff_trees(); + coro_t te1( boost::bind( enumerate_leafs, _1, pt.first) ); + coro_t te2( boost::bind( enumerate_leafs, _1, pt.second) ); + bool result = match_trees( te1, te2); + std::cout << std::boolalpha << "diff. trees matched == " << result << std::endl; + } + + std::cout << "Done" << std::endl; + + return EXIT_SUCCESS; +} diff --git a/example/tree.h b/example/tree.h new file mode 100644 index 0000000..f0c2187 --- /dev/null +++ b/example/tree.h @@ -0,0 +1,127 @@ + +// 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 +#include + +#include +#include +#include +#include + +# if defined(BOOST_MSVC) +# 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; } + +typedef boost::coroutines::coroutine< leaf&() > coro_t; + +class tree_visitor : public visitor +{ +private: + coro_t::caller_type & c_; + +public: + tree_visitor( coro_t::caller_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( coro_t::caller_type & c, node::ptr_t root) +{ + tree_visitor v( c); + root->accept( v); +} + +# if defined(BOOST_MSVC) +# pragma warning(pop) +# endif + + +#endif // TREE_H diff --git a/example/unwind.cpp b/example/unwind.cpp new file mode 100644 index 0000000..32df2ab --- /dev/null +++ b/example/unwind.cpp @@ -0,0 +1,46 @@ + +// 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 +#include + +#include +#include + +typedef boost::coroutines::coroutine< void() > coro_t; + +struct X : private boost::noncopyable +{ + X() { std::cout << "X()" << std::endl; } + ~X() { std::cout << "~X()" << std::endl; } +}; + +void fn( coro_t & ca) +{ + X x; + int i = 0; + while ( true) + { + std::cout << "fn() : " << ++i << std::endl; + ca(); + } +} + +int main( int argc, char * argv[]) +{ + { + coro_t c( fn); + for ( int k = 0; k < 3; ++k) + { + c(); + } + std::cout << "destroying coroutine and unwinding stack" << std::endl; + } + + std::cout << "\nDone" << std::endl; + + return EXIT_SUCCESS; +} diff --git a/include/boost/coroutine/all.hpp b/include/boost/coroutine/all.hpp new file mode 100644 index 0000000..d80ca20 --- /dev/null +++ b/include/boost/coroutine/all.hpp @@ -0,0 +1,15 @@ + +// 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_COROUTINES_ALL_H +#define BOOST_COROUTINES_ALL_H + +#include +#include +#include +#include + +#endif // BOOST_COROUTINES_ALL_H diff --git a/include/boost/coroutine/attributes.hpp b/include/boost/coroutine/attributes.hpp new file mode 100644 index 0000000..2855082 --- /dev/null +++ b/include/boost/coroutine/attributes.hpp @@ -0,0 +1,85 @@ + +// 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_COROUTINES_ATTRIBUTES_H +#define BOOST_COROUTINES_ATTRIBUTES_H + +#include + +#include + +#include +#include + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_PREFIX +#endif + +namespace boost { +namespace coroutines { + +struct attributes +{ + std::size_t size; + flag_unwind_t do_unwind; + flag_fpu_t preserve_fpu; + + attributes() BOOST_NOEXCEPT : + size( stack_allocator::default_stacksize() ), + do_unwind( stack_unwind), + preserve_fpu( fpu_preserved) + {} + + explicit attributes( std::size_t size_) BOOST_NOEXCEPT : + size( size_), + do_unwind( stack_unwind), + preserve_fpu( fpu_preserved) + {} + + explicit attributes( flag_unwind_t do_unwind_) BOOST_NOEXCEPT : + size( stack_allocator::default_stacksize() ), + do_unwind( do_unwind_), + preserve_fpu( fpu_preserved) + {} + + explicit attributes( flag_fpu_t preserve_fpu_) BOOST_NOEXCEPT : + size( stack_allocator::default_stacksize() ), + do_unwind( stack_unwind), + preserve_fpu( preserve_fpu_) + {} + + explicit attributes( + std::size_t size_, + flag_unwind_t do_unwind_) BOOST_NOEXCEPT : + size( size_), + do_unwind( do_unwind_), + preserve_fpu( fpu_preserved) + {} + + explicit attributes( + std::size_t size_, + flag_fpu_t preserve_fpu_) BOOST_NOEXCEPT : + size( size_), + do_unwind( stack_unwind), + preserve_fpu( preserve_fpu_) + {} + + explicit attributes( + flag_unwind_t do_unwind_, + flag_fpu_t preserve_fpu_) BOOST_NOEXCEPT : + size( stack_allocator::default_stacksize() ), + do_unwind( do_unwind_), + preserve_fpu( preserve_fpu_) + {} +}; + +}} + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_SUFFIX +#endif + +#endif // BOOST_COROUTINES_ATTRIBUTES_H diff --git a/include/boost/coroutine/coroutine.hpp b/include/boost/coroutine/coroutine.hpp new file mode 100644 index 0000000..5bd9c8f --- /dev/null +++ b/include/boost/coroutine/coroutine.hpp @@ -0,0 +1,1405 @@ + +// 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_COROUTINES_COROUTINE_H +#define BOOST_COROUTINES_COROUTINE_H + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_PREFIX +#endif + +namespace boost { +namespace coroutines { +namespace detail { + +template< + typename Signature, + template< class, int > class C, + typename Result = typename function_traits< Signature >::result_type, + int arity = function_traits< Signature >::arity +> +struct caller; + +template< + typename Signature, + template< class, int > class C +> +struct caller< Signature, C, void, 0 > +{ typedef C< void(), 0 > type; }; + +template< + typename Signature, + template< class, int > class C, + typename Result +> +struct caller< Signature, C, Result, 0 > +{ typedef C< void( Result), 1 > type; }; + +template< + typename Signature, + template< class, int > class C, + int arity +> +struct caller< Signature, C, void, arity > +{ typedef C< typename detail::arg< Signature >::type(), 0 > type; }; + +template< + typename Signature, + template< class, int > class C, + typename Result, int arity +> +struct caller +{ typedef C< typename detail::arg< Signature >::type( Result), 1 > type; }; + +} + +template< typename Signature, int arity = function_traits< Signature >::arity > +class coroutine; + +template< typename Signature > +class coroutine< Signature, 0 > : public detail::coroutine_op< + Signature, + coroutine< Signature >, + typename function_traits< Signature >::result_type, + function_traits< Signature >::arity + >, + public detail::coroutine_get< + coroutine< Signature >, + typename function_traits< Signature >::result_type, + function_traits< Signature >::arity + > +{ +private: + typedef detail::coroutine_base< Signature > base_t; + typedef typename base_t::ptr_t ptr_t; + + template< typename X, typename Y, int > + friend struct detail::coroutine_get; + template< typename X, typename Y, typename Z, int > + friend struct detail::coroutine_op; + template< typename X, typename Y, typename Z, typename A, typename B, typename C, int > + friend class detail::coroutine_object; + + struct dummy + { void nonnull() {} }; + + typedef void ( dummy::*safe_bool)(); + + ptr_t impl_; + + BOOST_MOVABLE_BUT_NOT_COPYABLE( coroutine); + + template< typename Allocator > + coroutine( context::fcontext_t * callee, + bool unwind, bool preserve_fpu, + Allocator const& alloc) : + detail::coroutine_op< + Signature, + coroutine< Signature >, + typename function_traits< Signature >::result_type, + function_traits< Signature >::arity + >(), + detail::coroutine_get< + coroutine< Signature >, + typename function_traits< Signature >::result_type, + function_traits< Signature >::arity + + >(), + impl_() + { + typedef detail::coroutine_caller< + Signature, Allocator + > caller_t; + typename caller_t::allocator_t a( alloc); + impl_ = ptr_t( + // placement new + ::new( a.allocate( 1) ) caller_t( + callee, unwind, preserve_fpu, a) ); + } + +public: + typedef typename detail::caller< + Signature, + boost::coroutines::coroutine + >::type caller_type; + + coroutine() BOOST_NOEXCEPT : + detail::coroutine_op< + Signature, + coroutine< Signature >, + typename function_traits< Signature >::result_type, + function_traits< Signature >::arity + >(), + detail::coroutine_get< + coroutine< Signature >, + typename function_traits< Signature >::result_type, + function_traits< Signature >::arity + >(), + impl_() + {} + +#ifndef BOOST_NO_RVALUE_REFERENCES +#ifdef BOOST_MSVC + typedef void ( * coroutine_fn) ( caller_type &); + + explicit coroutine( coroutine_fn fn, attributes const& attr = attributes(), + stack_allocator const& stack_alloc = + stack_allocator(), + std::allocator< coroutine > const& alloc = + std::allocator< coroutine >() ) : + detail::coroutine_op< + Signature, + coroutine< Signature >, + typename function_traits< Signature >::result_type, + function_traits< Signature >::arity + >(), + detail::coroutine_get< + coroutine< Signature >, + typename function_traits< Signature >::result_type, + function_traits< Signature >::arity + >(), + impl_() + { + typedef detail::coroutine_object< + Signature, + coroutine_fn, stack_allocator, std::allocator< coroutine >, + caller_type, + typename function_traits< Signature >::result_type, + function_traits< Signature >::arity + > object_t; + typename object_t::allocator_t a( alloc); + impl_ = ptr_t( + // placement new + ::new( a.allocate( 1) ) object_t( forward< coroutine_fn >( fn), attr, stack_alloc, a) ); + } + + template< typename StackAllocator > + explicit coroutine( coroutine_fn fn, attributes const& attr, + StackAllocator const& stack_alloc, + std::allocator< coroutine > const& alloc = + std::allocator< coroutine >() ) : + detail::coroutine_op< + Signature, + coroutine< Signature >, + typename function_traits< Signature >::result_type, + function_traits< Signature >::arity + >(), + detail::coroutine_get< + coroutine< Signature >, + typename function_traits< Signature >::result_type, + function_traits< Signature >::arity + >(), + impl_() + { + typedef detail::coroutine_object< + Signature, + coroutine_fn, StackAllocator, std::allocator< coroutine >, + caller_type, + typename function_traits< Signature >::result_type, + function_traits< Signature >::arity + > object_t; + typename object_t::allocator_t a( alloc); + impl_ = ptr_t( + // placement new + ::new( a.allocate( 1) ) object_t( forward< coroutine_fn >( fn), attr, stack_alloc, a) ); + } + + template< typename StackAllocator, typename Allocator > + explicit coroutine( coroutine_fn fn, attributes const& attr, + StackAllocator const& stack_alloc, + Allocator const& alloc) : + detail::coroutine_op< + Signature, + coroutine< Signature >, + typename function_traits< Signature >::result_type, + function_traits< Signature >::arity + >(), + detail::coroutine_get< + coroutine< Signature >, + typename function_traits< Signature >::result_type, + function_traits< Signature >::arity + >(), + impl_() + { + typedef detail::coroutine_object< + Signature, + coroutine_fn, StackAllocator, Allocator, + caller_type, + typename function_traits< Signature >::result_type, + function_traits< Signature >::arity + > object_t; + typename object_t::allocator_t a( alloc); + impl_ = ptr_t( + // placement new + ::new( a.allocate( 1) ) object_t( forward< coroutine_fn >( fn), attr, stack_alloc, a) ); + } +#endif + template< typename Fn > + explicit coroutine( BOOST_RV_REF( Fn) fn, attributes const& attr = attributes(), + stack_allocator const& stack_alloc = + stack_allocator(), + std::allocator< coroutine > const& alloc = + std::allocator< coroutine >(), + typename disable_if< + is_same< typename decay< Fn >::type, coroutine >, + dummy * + >::type = 0) : + detail::coroutine_op< + Signature, + coroutine< Signature >, + typename function_traits< Signature >::result_type, + function_traits< Signature >::arity + >(), + detail::coroutine_get< + coroutine< Signature >, + typename function_traits< Signature >::result_type, + function_traits< Signature >::arity + >(), + impl_() + { + BOOST_STATIC_ASSERT(( + is_same< void, typename result_of< Fn() >::type >::value)); + typedef detail::coroutine_object< + Signature, + Fn, stack_allocator, std::allocator< coroutine >, + caller_type, + typename function_traits< Signature >::result_type, + function_traits< Signature >::arity + > object_t; + typename object_t::allocator_t a( alloc); + impl_ = ptr_t( + // placement new + ::new( a.allocate( 1) ) object_t( forward< Fn >( fn), attr, stack_alloc, a) ); + } + + template< typename Fn, typename StackAllocator > + explicit coroutine( BOOST_RV_REF( Fn) fn, attributes const& attr, + StackAllocator const& stack_alloc, + std::allocator< coroutine > const& alloc = + std::allocator< coroutine >(), + typename disable_if< + is_same< typename decay< Fn >::type, coroutine >, + dummy * + >::type = 0) : + detail::coroutine_op< + Signature, + coroutine< Signature >, + typename function_traits< Signature >::result_type, + function_traits< Signature >::arity + >(), + detail::coroutine_get< + coroutine< Signature >, + typename function_traits< Signature >::result_type, + function_traits< Signature >::arity + >(), + impl_() + { + BOOST_STATIC_ASSERT(( + is_same< void, typename result_of< Fn() >::type >::value)); + typedef detail::coroutine_object< + Signature, + Fn, StackAllocator, std::allocator< coroutine >, + caller_type, + typename function_traits< Signature >::result_type, + function_traits< Signature >::arity + > object_t; + typename object_t::allocator_t a( alloc); + impl_ = ptr_t( + // placement new + ::new( a.allocate( 1) ) object_t( forward< Fn >( fn), attr, stack_alloc, a) ); + } + + template< typename Fn, typename StackAllocator, typename Allocator > + explicit coroutine( BOOST_RV_REF( Fn) fn, attributes const& attr, + StackAllocator const& stack_alloc, + Allocator const& alloc, + typename disable_if< + is_same< typename decay< Fn >::type, coroutine >, + dummy * + >::type = 0) : + detail::coroutine_op< + Signature, + coroutine< Signature >, + typename function_traits< Signature >::result_type, + function_traits< Signature >::arity + >(), + detail::coroutine_get< + coroutine< Signature >, + typename function_traits< Signature >::result_type, + function_traits< Signature >::arity + >(), + impl_() + { + BOOST_STATIC_ASSERT(( + is_same< void, typename result_of< Fn() >::type >::value)); + typedef detail::coroutine_object< + Signature, + Fn, StackAllocator, Allocator, + caller_type, + typename function_traits< Signature >::result_type, + function_traits< Signature >::arity + > object_t; + typename object_t::allocator_t a( alloc); + impl_ = ptr_t( + // placement new + ::new( a.allocate( 1) ) object_t( forward< Fn >( fn), attr, stack_alloc, a) ); + } +#else + template< typename Fn > + explicit coroutine( Fn fn, attributes const& attr = attributes(), + stack_allocator const& stack_alloc = + stack_allocator(), + std::allocator< coroutine > const& alloc = + std::allocator< coroutine >(), + typename disable_if< + is_convertible< Fn &, BOOST_RV_REF( Fn) >, + dummy * + >::type = 0) : + detail::coroutine_op< + Signature, + coroutine< Signature >, + typename function_traits< Signature >::result_type, + function_traits< Signature >::arity + >(), + detail::coroutine_get< + coroutine< Signature >, + typename function_traits< Signature >::result_type, + function_traits< Signature >::arity + >(), + impl_() + { + BOOST_STATIC_ASSERT(( + is_same< void, typename result_of< Fn() >::type >::value)); + typedef detail::coroutine_object< + Signature, + Fn, stack_allocator, std::allocator< coroutine >, + caller_type, + typename function_traits< Signature >::result_type, + function_traits< Signature >::arity + > object_t; + typename object_t::allocator_t a( alloc); + impl_ = ptr_t( + // placement new + ::new( a.allocate( 1) ) object_t( fn, attr, stack_alloc, a) ); + } + + template< typename Fn, typename StackAllocator > + explicit coroutine( Fn fn, attributes const& attr, + StackAllocator const& stack_alloc, + std::allocator< coroutine > const& alloc = + std::allocator< coroutine >(), + typename disable_if< + is_convertible< Fn &, BOOST_RV_REF( Fn) >, + dummy * + >::type = 0) : + detail::coroutine_op< + Signature, + coroutine< Signature >, + typename function_traits< Signature >::result_type, + function_traits< Signature >::arity + >(), + detail::coroutine_get< + coroutine< Signature >, + typename function_traits< Signature >::result_type, + function_traits< Signature >::arity + >(), + impl_() + { + BOOST_STATIC_ASSERT(( + is_same< void, typename result_of< Fn() >::type >::value)); + typedef detail::coroutine_object< + Signature, + Fn, StackAllocator, std::allocator< coroutine >, + caller_type, + typename function_traits< Signature >::result_type, + function_traits< Signature >::arity + > object_t; + typename object_t::allocator_t a( alloc); + impl_ = ptr_t( + // placement new + ::new( a.allocate( 1) ) object_t( fn, attr, stack_alloc, a) ); + } + + template< typename Fn, typename StackAllocator, typename Allocator > + explicit coroutine( Fn fn, attributes const& attr, + StackAllocator const& stack_alloc, + Allocator const& alloc, + typename disable_if< + is_convertible< Fn &, BOOST_RV_REF( Fn) >, + dummy * + >::type = 0) : + detail::coroutine_op< + Signature, + coroutine< Signature >, + typename function_traits< Signature >::result_type, + function_traits< Signature >::arity + >(), + detail::coroutine_get< + coroutine< Signature >, + typename function_traits< Signature >::result_type, + function_traits< Signature >::arity + >(), + impl_() + { + BOOST_STATIC_ASSERT(( + is_same< void, typename result_of< Fn() >::type >::value)); + typedef detail::coroutine_object< + Signature, + Fn, StackAllocator, Allocator, + caller_type, + typename function_traits< Signature >::result_type, + function_traits< Signature >::arity + > object_t; + typename object_t::allocator_t a( alloc); + impl_ = ptr_t( + // placement new + ::new( a.allocate( 1) ) object_t( fn, attr, stack_alloc, a) ); + } + + template< typename Fn > + explicit coroutine( BOOST_RV_REF( Fn) fn, attributes const& attr = attributes(), + stack_allocator const& stack_alloc = + stack_allocator(), + std::allocator< coroutine > const& alloc = + std::allocator< coroutine >(), + typename disable_if< + is_same< typename decay< Fn >::type, coroutine >, + dummy * + >::type = 0) : + detail::coroutine_op< + Signature, + coroutine< Signature >, + typename function_traits< Signature >::result_type, + function_traits< Signature >::arity + >(), + detail::coroutine_get< + coroutine< Signature >, + typename function_traits< Signature >::result_type, + function_traits< Signature >::arity + >(), + impl_() + { + BOOST_STATIC_ASSERT(( + is_same< void, typename result_of< Fn() >::type >::value)); + typedef detail::coroutine_object< + Signature, + Fn, stack_allocator, std::allocator< coroutine >, + caller_type, + typename function_traits< Signature >::result_type, + function_traits< Signature >::arity + > object_t; + typename object_t::allocator_t a( alloc); + impl_ = ptr_t( + // placement new + ::new( a.allocate( 1) ) object_t( fn, attr, stack_alloc, a) ); + } + + template< typename Fn, typename StackAllocator > + explicit coroutine( BOOST_RV_REF( Fn) fn, attributes const& attr, + StackAllocator const& stack_alloc, + std::allocator< coroutine > const& alloc = + std::allocator< coroutine >(), + typename disable_if< + is_same< typename decay< Fn >::type, coroutine >, + dummy * + >::type = 0) : + detail::coroutine_op< + Signature, + coroutine< Signature >, + typename function_traits< Signature >::result_type, + function_traits< Signature >::arity + >(), + detail::coroutine_get< + coroutine< Signature >, + typename function_traits< Signature >::result_type, + function_traits< Signature >::arity + >(), + impl_() + { + BOOST_STATIC_ASSERT(( + is_same< void, typename result_of< Fn() >::type >::value)); + typedef detail::coroutine_object< + Signature, + Fn, StackAllocator, std::allocator< coroutine >, + caller_type, + typename function_traits< Signature >::result_type, + function_traits< Signature >::arity + > object_t; + typename object_t::allocator_t a( alloc); + impl_ = ptr_t( + // placement new + ::new( a.allocate( 1) ) object_t( fn, attr, stack_alloc, a) ); + } + + template< typename Fn, typename StackAllocator, typename Allocator > + explicit coroutine( BOOST_RV_REF( Fn) fn, attributes const& attr, + StackAllocator const& stack_alloc, + Allocator const& alloc, + typename disable_if< + is_same< typename decay< Fn >::type, coroutine >, + dummy * + >::type = 0) : + detail::coroutine_op< + Signature, + coroutine< Signature >, + typename function_traits< Signature >::result_type, + function_traits< Signature >::arity + >(), + detail::coroutine_get< + coroutine< Signature >, + typename function_traits< Signature >::result_type, + function_traits< Signature >::arity + >(), + impl_() + { + BOOST_STATIC_ASSERT(( + is_same< void, typename result_of< Fn() >::type >::value)); + typedef detail::coroutine_object< + Signature, + Fn, StackAllocator, Allocator, + caller_type, + typename function_traits< Signature >::result_type, + function_traits< Signature >::arity + > object_t; + typename object_t::allocator_t a( alloc); + impl_ = ptr_t( + // placement new + ::new( a.allocate( 1) ) object_t( fn, attr, stack_alloc, a) ); + } +#endif + + coroutine( BOOST_RV_REF( coroutine) other) BOOST_NOEXCEPT : + detail::coroutine_op< + Signature, + coroutine< Signature >, + typename function_traits< Signature >::result_type, + function_traits< Signature >::arity + >(), + detail::coroutine_get< + coroutine< Signature >, + typename function_traits< Signature >::result_type, + function_traits< Signature >::arity + >(), + impl_() + { swap( other); } + + coroutine & operator=( BOOST_RV_REF( coroutine) other) BOOST_NOEXCEPT + { + coroutine tmp( boost::move( other) ); + swap( tmp); + return * this; + } + + bool empty() const BOOST_NOEXCEPT + { return ! impl_; } + + operator safe_bool() const BOOST_NOEXCEPT + { return ( empty() || impl_->is_complete() ) ? 0 : & dummy::nonnull; } + + bool operator!() const BOOST_NOEXCEPT + { return empty() || impl_->is_complete(); } + + void swap( coroutine & other) BOOST_NOEXCEPT + { impl_.swap( other.impl_); } +}; + +template< typename Signature, int arity > +class coroutine : public detail::coroutine_op< + Signature, + coroutine< Signature >, + typename function_traits< Signature >::result_type, + function_traits< Signature >::arity + >, + public detail::coroutine_get< + coroutine< Signature >, + typename function_traits< Signature >::result_type, + function_traits< Signature >::arity + > +{ +private: + typedef detail::coroutine_base< Signature > base_t; + typedef typename base_t::ptr_t ptr_t; + + template< typename X, typename Y, int > + friend struct detail::coroutine_get; + template< typename X, typename Y, typename Z, int > + friend struct detail::coroutine_op; + template< typename X, typename Y, typename Z, typename A, typename B, typename C, int > + friend class detail::coroutine_object; + + struct dummy + { void nonnull() {} }; + + typedef void ( dummy::*safe_bool)(); + + ptr_t impl_; + + BOOST_MOVABLE_BUT_NOT_COPYABLE( coroutine); + + template< typename Allocator > + coroutine( context::fcontext_t * callee, + bool unwind, bool preserve_fpu, + Allocator const& alloc) : + detail::coroutine_op< + Signature, + coroutine< Signature >, + typename function_traits< Signature >::result_type, + function_traits< Signature >::arity + >(), + detail::coroutine_get< + coroutine< Signature >, + typename function_traits< Signature >::result_type, + function_traits< Signature >::arity + >(), + impl_() + { + typedef detail::coroutine_caller< + Signature, Allocator + > caller_t; + typename caller_t::allocator_t a( alloc); + impl_ = ptr_t( + // placement new + ::new( a.allocate( 1) ) caller_t( + callee, unwind, preserve_fpu, a) ); + } + +public: + typedef typename detail::caller< + Signature, + boost::coroutines::coroutine + >::type caller_type; + typedef typename detail::arg< Signature >::type arguments; + + coroutine() BOOST_NOEXCEPT : + detail::coroutine_op< + Signature, + coroutine< Signature >, + typename function_traits< Signature >::result_type, + function_traits< Signature >::arity + >(), + detail::coroutine_get< + coroutine< Signature >, + typename function_traits< Signature >::result_type, + function_traits< Signature >::arity + >(), + impl_() + {} + +#ifndef BOOST_NO_RVALUE_REFERENCES +#ifdef BOOST_MSVC + typedef void ( * coroutine_fn) ( caller_type &); + + explicit coroutine( coroutine_fn fn, attributes const& attr = attributes(), + stack_allocator const& stack_alloc = + stack_allocator(), + std::allocator< coroutine > const& alloc = + std::allocator< coroutine >() ) : + detail::coroutine_op< + Signature, + coroutine< Signature >, + typename function_traits< Signature >::result_type, + function_traits< Signature >::arity + >(), + detail::coroutine_get< + coroutine< Signature >, + typename function_traits< Signature >::result_type, + function_traits< Signature >::arity + >(), + impl_() + { + typedef detail::coroutine_object< + Signature, + coroutine_fn, stack_allocator, std::allocator< coroutine >, + caller_type, + typename function_traits< Signature >::result_type, + function_traits< Signature >::arity + > object_t; + typename object_t::allocator_t a( alloc); + impl_ = ptr_t( + // placement new + ::new( a.allocate( 1) ) object_t( forward< coroutine_fn >( fn), attr, stack_alloc, a) ); + } + + explicit coroutine( coroutine_fn fn, arguments arg, attributes const& attr = attributes(), + stack_allocator const& stack_alloc = + stack_allocator(), + std::allocator< coroutine > const& alloc = + std::allocator< coroutine >() ) : + detail::coroutine_op< + Signature, + coroutine< Signature >, + typename function_traits< Signature >::result_type, + function_traits< Signature >::arity + >(), + detail::coroutine_get< + coroutine< Signature >, + typename function_traits< Signature >::result_type, + function_traits< Signature >::arity + >(), + impl_() + { + typedef detail::coroutine_object< + Signature, + coroutine_fn, stack_allocator, std::allocator< coroutine >, + caller_type, + typename function_traits< Signature >::result_type, + function_traits< Signature >::arity + > object_t; + typename object_t::allocator_t a( alloc); + impl_ = ptr_t( + // placement new + ::new( a.allocate( 1) ) object_t( forward< coroutine_fn >( fn), arg, attr, stack_alloc, a) ); + } + + template< typename StackAllocator > + explicit coroutine( coroutine_fn fn, attributes const& attr, + StackAllocator const& stack_alloc, + std::allocator< coroutine > const& alloc = + std::allocator< coroutine >() ) : + detail::coroutine_op< + Signature, + coroutine< Signature >, + typename function_traits< Signature >::result_type, + function_traits< Signature >::arity + >(), + detail::coroutine_get< + coroutine< Signature >, + typename function_traits< Signature >::result_type, + function_traits< Signature >::arity + >(), + impl_() + { + typedef detail::coroutine_object< + Signature, + coroutine_fn, StackAllocator, std::allocator< coroutine >, + caller_type, + typename function_traits< Signature >::result_type, + function_traits< Signature >::arity + > object_t; + typename object_t::allocator_t a( alloc); + impl_ = ptr_t( + // placement new + ::new( a.allocate( 1) ) object_t( forward< coroutine_fn >( fn), attr, stack_alloc, a) ); + } + + template< typename StackAllocator, typename Allocator > + explicit coroutine( coroutine_fn fn, attributes const& attr, + StackAllocator const& stack_alloc, + Allocator const& alloc) : + detail::coroutine_op< + Signature, + coroutine< Signature >, + typename function_traits< Signature >::result_type, + function_traits< Signature >::arity + >(), + detail::coroutine_get< + coroutine< Signature >, + typename function_traits< Signature >::result_type, + function_traits< Signature >::arity + >(), + impl_() + { + typedef detail::coroutine_object< + Signature, + coroutine_fn, StackAllocator, Allocator, + caller_type, + typename function_traits< Signature >::result_type, + function_traits< Signature >::arity + > object_t; + typename object_t::allocator_t a( alloc); + impl_ = ptr_t( + // placement new + ::new( a.allocate( 1) ) object_t( forward< coroutine_fn >( fn), attr, stack_alloc, a) ); + } +#endif + template< typename Fn > + explicit coroutine( BOOST_RV_REF( Fn) fn, attributes const& attr = attributes(), + stack_allocator const& stack_alloc = + stack_allocator(), + std::allocator< coroutine > const& alloc = + std::allocator< coroutine >(), + typename disable_if< + is_same< typename decay< Fn >::type, coroutine >, + dummy * + >::type = 0) : + detail::coroutine_op< + Signature, + coroutine< Signature >, + typename function_traits< Signature >::result_type, + function_traits< Signature >::arity + >(), + detail::coroutine_get< + coroutine< Signature >, + typename function_traits< Signature >::result_type, + function_traits< Signature >::arity + >(), + impl_() + { + BOOST_STATIC_ASSERT(( + is_same< void, typename result_of< Fn() >::type >::value)); + typedef detail::coroutine_object< + Signature, + Fn, stack_allocator, std::allocator< coroutine >, + caller_type, + typename function_traits< Signature >::result_type, + function_traits< Signature >::arity + > object_t; + typename object_t::allocator_t a( alloc); + impl_ = ptr_t( + // placement new + ::new( a.allocate( 1) ) object_t( forward< Fn >( fn), attr, stack_alloc, a) ); + } + + template< typename Fn > + explicit coroutine( BOOST_RV_REF( Fn) fn, arguments arg, attributes const& attr = attributes(), + stack_allocator const& stack_alloc = + stack_allocator(), + std::allocator< coroutine > const& alloc = + std::allocator< coroutine >(), + typename disable_if< + is_same< typename decay< Fn >::type, coroutine >, + dummy * + >::type = 0) : + detail::coroutine_op< + Signature, + coroutine< Signature >, + typename function_traits< Signature >::result_type, + function_traits< Signature >::arity + >(), + detail::coroutine_get< + coroutine< Signature >, + typename function_traits< Signature >::result_type, + function_traits< Signature >::arity + >(), + impl_() + { + BOOST_STATIC_ASSERT(( + is_same< void, typename result_of< Fn() >::type >::value)); + typedef detail::coroutine_object< + Signature, + Fn, stack_allocator, std::allocator< coroutine >, + caller_type, + typename function_traits< Signature >::result_type, + function_traits< Signature >::arity + > object_t; + typename object_t::allocator_t a( alloc); + impl_ = ptr_t( + // placement new + ::new( a.allocate( 1) ) object_t( forward< Fn >( fn), arg, attr, stack_alloc, a) ); + } + + template< typename Fn, typename StackAllocator > + explicit coroutine( BOOST_RV_REF( Fn) fn, attributes const& attr, + StackAllocator const& stack_alloc, + std::allocator< coroutine > const& alloc = + std::allocator< coroutine >(), + typename disable_if< + is_same< typename decay< Fn >::type, coroutine >, + dummy * + >::type = 0) : + detail::coroutine_op< + Signature, + coroutine< Signature >, + typename function_traits< Signature >::result_type, + function_traits< Signature >::arity + >(), + detail::coroutine_get< + coroutine< Signature >, + typename function_traits< Signature >::result_type, + function_traits< Signature >::arity + >(), + impl_() + { + BOOST_STATIC_ASSERT(( + is_same< void, typename result_of< Fn() >::type >::value)); + typedef detail::coroutine_object< + Signature, + Fn, StackAllocator, std::allocator< coroutine >, + caller_type, + typename function_traits< Signature >::result_type, + function_traits< Signature >::arity + > object_t; + typename object_t::allocator_t a( alloc); + impl_ = ptr_t( + // placement new + ::new( a.allocate( 1) ) object_t( forward< Fn >( fn), attr, stack_alloc, a) ); + } + + template< typename Fn, typename StackAllocator > + explicit coroutine( BOOST_RV_REF( Fn) fn, arguments arg, attributes const& attr, + StackAllocator const& stack_alloc, + std::allocator< coroutine > const& alloc = + std::allocator< coroutine >(), + typename disable_if< + is_same< typename decay< Fn >::type, coroutine >, + dummy * + >::type = 0) : + detail::coroutine_op< + Signature, + coroutine< Signature >, + typename function_traits< Signature >::result_type, + function_traits< Signature >::arity + >(), + detail::coroutine_get< + coroutine< Signature >, + typename function_traits< Signature >::result_type, + function_traits< Signature >::arity + >(), + impl_() + { + BOOST_STATIC_ASSERT(( + is_same< void, typename result_of< Fn() >::type >::value)); + typedef detail::coroutine_object< + Signature, + Fn, StackAllocator, std::allocator< coroutine >, + caller_type, + typename function_traits< Signature >::result_type, + function_traits< Signature >::arity + > object_t; + typename object_t::allocator_t a( alloc); + impl_ = ptr_t( + // placement new + ::new( a.allocate( 1) ) object_t( forward< Fn >( fn), arg, attr, stack_alloc, a) ); + } + + template< typename Fn, typename StackAllocator, typename Allocator > + explicit coroutine( BOOST_RV_REF( Fn) fn, attributes const& attr, + StackAllocator const& stack_alloc, + Allocator const& alloc, + typename disable_if< + is_same< typename decay< Fn >::type, coroutine >, + dummy * + >::type = 0) : + detail::coroutine_op< + Signature, + coroutine< Signature >, + typename function_traits< Signature >::result_type, + function_traits< Signature >::arity + >(), + detail::coroutine_get< + coroutine< Signature >, + typename function_traits< Signature >::result_type, + function_traits< Signature >::arity + >(), + impl_() + { + BOOST_STATIC_ASSERT(( + is_same< void, typename result_of< Fn() >::type >::value)); + typedef detail::coroutine_object< + Signature, + Fn, StackAllocator, Allocator, + caller_type, + typename function_traits< Signature >::result_type, + function_traits< Signature >::arity + > object_t; + typename object_t::allocator_t a( alloc); + impl_ = ptr_t( + // placement new + ::new( a.allocate( 1) ) object_t( forward< Fn >( fn), attr, stack_alloc, a) ); + } + + template< typename Fn, typename StackAllocator, typename Allocator > + explicit coroutine( BOOST_RV_REF( Fn) fn, arguments arg, attributes const& attr, + StackAllocator const& stack_alloc, + Allocator const& alloc, + typename disable_if< + is_same< typename decay< Fn >::type, coroutine >, + dummy * + >::type = 0) : + detail::coroutine_op< + Signature, + coroutine< Signature >, + typename function_traits< Signature >::result_type, + function_traits< Signature >::arity + >(), + detail::coroutine_get< + coroutine< Signature >, + typename function_traits< Signature >::result_type, + function_traits< Signature >::arity + >(), + impl_() + { + BOOST_STATIC_ASSERT(( + is_same< void, typename result_of< Fn() >::type >::value)); + typedef detail::coroutine_object< + Signature, + Fn, StackAllocator, Allocator, + caller_type, + typename function_traits< Signature >::result_type, + function_traits< Signature >::arity + > object_t; + typename object_t::allocator_t a( alloc); + impl_ = ptr_t( + // placement new + ::new( a.allocate( 1) ) object_t( forward< Fn >( fn), arg, attr, stack_alloc, a) ); + } +#else + template< typename Fn > + explicit coroutine( Fn fn, attributes const& attr = attributes(), + stack_allocator const& stack_alloc = + stack_allocator(), + std::allocator< coroutine > const& alloc = + std::allocator< coroutine >(), + typename disable_if< + is_convertible< Fn &, BOOST_RV_REF( Fn) >, + dummy * + >::type = 0) : + detail::coroutine_op< + Signature, + coroutine< Signature >, + typename function_traits< Signature >::result_type, + function_traits< Signature >::arity + >(), + detail::coroutine_get< + coroutine< Signature >, + typename function_traits< Signature >::result_type, + function_traits< Signature >::arity + >(), + impl_() + { + BOOST_STATIC_ASSERT(( + is_same< void, typename result_of< Fn() >::type >::value)); + typedef detail::coroutine_object< + Signature, + Fn, stack_allocator, std::allocator< coroutine >, + caller_type, + typename function_traits< Signature >::result_type, + function_traits< Signature >::arity + > object_t; + typename object_t::allocator_t a( alloc); + impl_ = ptr_t( + // placement new + ::new( a.allocate( 1) ) object_t( fn, attr, stack_alloc, a) ); + } + + template< typename Fn > + explicit coroutine( Fn fn, arguments arg, attributes const& attr = attributes(), + stack_allocator const& stack_alloc = + stack_allocator(), + std::allocator< coroutine > const& alloc = + std::allocator< coroutine >(), + typename disable_if< + is_convertible< Fn &, BOOST_RV_REF( Fn) >, + dummy * + >::type = 0) : + detail::coroutine_op< + Signature, + coroutine< Signature >, + typename function_traits< Signature >::result_type, + function_traits< Signature >::arity + >(), + detail::coroutine_get< + coroutine< Signature >, + typename function_traits< Signature >::result_type, + function_traits< Signature >::arity + >(), + impl_() + { + BOOST_STATIC_ASSERT(( + is_same< void, typename result_of< Fn() >::type >::value)); + typedef detail::coroutine_object< + Signature, + Fn, stack_allocator, std::allocator< coroutine >, + caller_type, + typename function_traits< Signature >::result_type, + function_traits< Signature >::arity + > object_t; + typename object_t::allocator_t a( alloc); + impl_ = ptr_t( + // placement new + ::new( a.allocate( 1) ) object_t( fn, arg, attr, stack_alloc, a) ); + } + + template< typename Fn, typename StackAllocator > + explicit coroutine( Fn fn, attributes const& attr, + StackAllocator const& stack_alloc, + std::allocator< coroutine > const& alloc = + std::allocator< coroutine >(), + typename disable_if< + is_convertible< Fn &, BOOST_RV_REF( Fn) >, + dummy * + >::type = 0) : + detail::coroutine_op< + Signature, + coroutine< Signature >, + typename function_traits< Signature >::result_type, + function_traits< Signature >::arity + >(), + detail::coroutine_get< + coroutine< Signature >, + typename function_traits< Signature >::result_type, + function_traits< Signature >::arity + >(), + impl_() + { + BOOST_STATIC_ASSERT(( + is_same< void, typename result_of< Fn() >::type >::value)); + typedef detail::coroutine_object< + Signature, + Fn, StackAllocator, std::allocator< coroutine >, + caller_type, + typename function_traits< Signature >::result_type, + function_traits< Signature >::arity + > object_t; + typename object_t::allocator_t a( alloc); + impl_ = ptr_t( + // placement new + ::new( a.allocate( 1) ) object_t( fn, attr, stack_alloc, a) ); + } + + template< typename Fn, typename StackAllocator, typename Allocator > + explicit coroutine( Fn fn, attributes const& attr, + StackAllocator const& stack_alloc, + Allocator const& alloc, + typename disable_if< + is_convertible< Fn &, BOOST_RV_REF( Fn) >, + dummy * + >::type = 0) : + detail::coroutine_op< + Signature, + coroutine< Signature >, + typename function_traits< Signature >::result_type, + function_traits< Signature >::arity + >(), + detail::coroutine_get< + coroutine< Signature >, + typename function_traits< Signature >::result_type, + function_traits< Signature >::arity + >(), + impl_() + { + BOOST_STATIC_ASSERT(( + is_same< void, typename result_of< Fn() >::type >::value)); + typedef detail::coroutine_object< + Signature, + Fn, StackAllocator, Allocator, + caller_type, + typename function_traits< Signature >::result_type, + function_traits< Signature >::arity + > object_t; + typename object_t::allocator_t a( alloc); + impl_ = ptr_t( + // placement new + ::new( a.allocate( 1) ) object_t( fn, attr, stack_alloc, a) ); + } + + template< typename Fn > + explicit coroutine( BOOST_RV_REF( Fn) fn, attributes const& attr = attributes(), + stack_allocator const& stack_alloc = + stack_allocator(), + std::allocator< coroutine > const& alloc = + std::allocator< coroutine >(), + typename disable_if< + is_same< typename decay< Fn >::type, coroutine >, + dummy * + >::type = 0) : + detail::coroutine_op< + Signature, + coroutine< Signature >, + typename function_traits< Signature >::result_type, + function_traits< Signature >::arity + >(), + detail::coroutine_get< + coroutine< Signature >, + typename function_traits< Signature >::result_type, + function_traits< Signature >::arity + >(), + impl_() + { + BOOST_STATIC_ASSERT(( + is_same< void, typename result_of< Fn() >::type >::value)); + typedef detail::coroutine_object< + Signature, + Fn, stack_allocator, std::allocator< coroutine >, + caller_type, + typename function_traits< Signature >::result_type, + function_traits< Signature >::arity + > object_t; + typename object_t::allocator_t a( alloc); + impl_ = ptr_t( + // placement new + ::new( a.allocate( 1) ) object_t( fn, attr, stack_alloc, a) ); + } + + template< typename Fn, typename StackAllocator > + explicit coroutine( BOOST_RV_REF( Fn) fn, attributes const& attr, + StackAllocator const& stack_alloc, + std::allocator< coroutine > const& alloc = + std::allocator< coroutine >(), + typename disable_if< + is_same< typename decay< Fn >::type, coroutine >, + dummy * + >::type = 0) : + detail::coroutine_op< + Signature, + coroutine< Signature >, + typename function_traits< Signature >::result_type, + function_traits< Signature >::arity + >(), + detail::coroutine_get< + coroutine< Signature >, + typename function_traits< Signature >::result_type, + function_traits< Signature >::arity + >(), + impl_() + { + BOOST_STATIC_ASSERT(( + is_same< void, typename result_of< Fn() >::type >::value)); + typedef detail::coroutine_object< + Signature, + Fn, StackAllocator, std::allocator< coroutine >, + caller_type, + typename function_traits< Signature >::result_type, + function_traits< Signature >::arity + > object_t; + typename object_t::allocator_t a( alloc); + impl_ = ptr_t( + // placement new + ::new( a.allocate( 1) ) object_t( fn, attr, stack_alloc, a) ); + } + + template< typename Fn, typename StackAllocator, typename Allocator > + explicit coroutine( BOOST_RV_REF( Fn) fn, attributes const& attr, + StackAllocator const& stack_alloc, + Allocator const& alloc, + typename disable_if< + is_same< typename decay< Fn >::type, coroutine >, + dummy * + >::type = 0) : + detail::coroutine_op< + Signature, + coroutine< Signature >, + typename function_traits< Signature >::result_type, + function_traits< Signature >::arity + >(), + detail::coroutine_get< + coroutine< Signature >, + typename function_traits< Signature >::result_type, + function_traits< Signature >::arity + >(), + impl_() + { + BOOST_STATIC_ASSERT(( + is_same< void, typename result_of< Fn() >::type >::value)); + typedef detail::coroutine_object< + Signature, + Fn, StackAllocator, Allocator, + caller_type, + typename function_traits< Signature >::result_type, + function_traits< Signature >::arity + > object_t; + typename object_t::allocator_t a( alloc); + impl_ = ptr_t( + // placement new + ::new( a.allocate( 1) ) object_t( fn, attr, stack_alloc, a) ); + } +#endif + + coroutine( BOOST_RV_REF( coroutine) other) BOOST_NOEXCEPT : + detail::coroutine_op< + Signature, + coroutine< Signature >, + typename function_traits< Signature >::result_type, + function_traits< Signature >::arity + >(), + detail::coroutine_get< + coroutine< Signature >, + typename function_traits< Signature >::result_type, + function_traits< Signature >::arity + >(), + impl_() + { swap( other); } + + coroutine & operator=( BOOST_RV_REF( coroutine) other) BOOST_NOEXCEPT + { + coroutine tmp( boost::move( other) ); + swap( tmp); + return * this; + } + + bool empty() const BOOST_NOEXCEPT + { return ! impl_; } + + operator safe_bool() const BOOST_NOEXCEPT + { return ( empty() || impl_->is_complete() ) ? 0 : & dummy::nonnull; } + + bool operator!() const BOOST_NOEXCEPT + { return empty() || impl_->is_complete(); } + + void swap( coroutine & other) BOOST_NOEXCEPT + { impl_.swap( other.impl_); } +}; + +template< typename Signature > +void swap( coroutine< Signature > & l, coroutine< Signature > & r) BOOST_NOEXCEPT +{ l.swap( r); } + +template< typename Signature > +inline +typename coroutine< Signature >::iterator +range_begin( coroutine< Signature > & c) +{ return typename coroutine< Signature >::iterator( & c); } + +template< typename Signature > +inline +typename coroutine< Signature >::const_iterator +range_begin( coroutine< Signature > const& c) +{ return typename coroutine< Signature >::const_iterator( & c); } + +template< typename Signature > +inline +typename coroutine< Signature >::iterator +range_end( coroutine< Signature > & c) +{ return typename coroutine< Signature >::iterator(); } + +template< typename Signature > +inline +typename coroutine< Signature >::const_iterator +range_end( coroutine< Signature > const& c) +{ return typename coroutine< Signature >::const_iterator(); } + +} + +template< typename Signature > +struct range_mutable_iterator< coroutines::coroutine< Signature > > +{ typedef typename coroutines::coroutine< Signature >::iterator type; }; + +template< typename Signature > +struct range_const_iterator< coroutines::coroutine< Signature > > +{ typedef typename coroutines::coroutine< Signature >::const_iterator type; }; + +} + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_SUFFIX +#endif + +#endif // BOOST_COROUTINES_COROUTINE_H diff --git a/include/boost/coroutine/detail/arg.hpp b/include/boost/coroutine/detail/arg.hpp new file mode 100644 index 0000000..f3d35c5 --- /dev/null +++ b/include/boost/coroutine/detail/arg.hpp @@ -0,0 +1,61 @@ + +// 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_COROUTINES_DETAIL_ARG_H +#define BOOST_COROUTINES_DETAIL_ARG_H + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_PREFIX +#endif + +namespace boost { +namespace coroutines { +namespace detail { + +template< + typename Signature, + int arity = function_traits< Signature >::arity > +struct arg; + +template< typename Signature > +struct arg< Signature, 1 > +{ + typedef typename function_traits< Signature >::arg1_type type; +}; + +#define BOOST_CONTEXT_TUPLE_COMMA(n) BOOST_PP_COMMA_IF(BOOST_PP_SUB(n,1)) +#define BOOST_CONTEXT_TUPLE_TYPE(z,n,unused) \ + BOOST_CONTEXT_TUPLE_COMMA(n) typename function_traits< Signature >::BOOST_PP_CAT(BOOST_PP_CAT(arg,n),_type) +#define BOOST_CONTEXT_TUPLE_TYPES(n) BOOST_PP_REPEAT_FROM_TO(1,BOOST_PP_ADD(n,1),BOOST_CONTEXT_TUPLE_TYPE,~) +#define BOOST_CONTEXT_TUPLE(z,n,unused) \ +template< typename Signature > \ +struct arg< Signature, n > \ +{ \ + typedef tuple< BOOST_CONTEXT_TUPLE_TYPES(n) > type; \ +}; +BOOST_PP_REPEAT_FROM_TO(2,11,BOOST_CONTEXT_TUPLE,~) +#undef BOOST_CONTEXT_TUPLE +#undef BOOST_CONTEXT_TUPLE_TYPES +#undef BOOST_CONTEXT_TUPLE_TYPE +#undef BOOST_CONTEXT_TUPLE_COMMA + +}}} + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_SUFFIX +#endif + +#endif // BOOST_COROUTINES_DETAIL_ARG_H diff --git a/include/boost/coroutine/detail/config.hpp b/include/boost/coroutine/detail/config.hpp new file mode 100644 index 0000000..c08a9a3 --- /dev/null +++ b/include/boost/coroutine/detail/config.hpp @@ -0,0 +1,42 @@ + +// 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_COROUTINES_DETAIL_CONFIG_H +#define BOOST_COROUTINES_DETAIL_CONFIG_H + +#include +#include + +#ifdef BOOST_COROUTINES_DECL +# undef BOOST_COROUTINES_DECL +#endif + +#if defined(BOOST_HAS_DECLSPEC) +# if defined(BOOST_ALL_DYN_LINK) || defined(BOOST_COROUTINES_DYN_LINK) +# if ! defined(BOOST_DYN_LINK) +# define BOOST_DYN_LINK +# endif +# if defined(BOOST_COROUTINES_SOURCE) +# define BOOST_COROUTINES_DECL BOOST_SYMBOL_EXPORT +# else +# define BOOST_COROUTINES_DECL BOOST_SYMBOL_IMPORT +# endif +# endif +#endif + +#if ! defined(BOOST_COROUTINES_DECL) +# define BOOST_COROUTINES_DECL +#endif + +#if ! defined(BOOST_COROUTINES_SOURCE) && ! defined(BOOST_ALL_NO_LIB) && ! defined(BOOST_COROUTINES_NO_LIB) +# define BOOST_LIB_NAME boost_context +# if defined(BOOST_ALL_DYN_LINK) || defined(BOOST_COROUTINES_DYN_LINK) +# define BOOST_DYN_LINK +# endif +# include +#endif + +#endif // BOOST_COROUTINES_DETAIL_CONFIG_H diff --git a/include/boost/coroutine/detail/coroutine_base.hpp b/include/boost/coroutine/detail/coroutine_base.hpp new file mode 100644 index 0000000..8f649c5 --- /dev/null +++ b/include/boost/coroutine/detail/coroutine_base.hpp @@ -0,0 +1,103 @@ + +// 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_COROUTINES_DETAIL_COROUTINE_BASE_H +#define BOOST_COROUTINES_DETAIL_COROUTINE_BASE_H + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_PREFIX +#endif + +namespace boost { +namespace coroutines { +namespace detail { + +template< typename Signature > +class coroutine_base : private noncopyable, + public coroutine_base_resume< + Signature, + coroutine_base< Signature >, + typename function_traits< Signature >::result_type, + function_traits< Signature >::arity + > +{ +public: + typedef intrusive_ptr< coroutine_base > ptr_t; + +private: + template< typename X, typename Y, typename Z, int > + friend class coroutine_base_resume; + template< typename X, typename Y, typename Z, typename A, typename B, typename C, int > + friend class coroutine_object; + + unsigned int use_count_; + context::fcontext_t caller_; + context::fcontext_t * callee_; + int flags_; + exception_ptr except_; + +protected: + virtual void deallocate_object() = 0; + +public: + coroutine_base( context::fcontext_t * callee, bool unwind, bool preserve_fpu) : + coroutine_base_resume< + Signature, + coroutine_base< Signature >, + typename function_traits< Signature >::result_type, + function_traits< Signature >::arity + >(), + use_count_( 0), + caller_(), + callee_( callee), + flags_( 0), + except_() + { + if ( unwind) flags_ |= flag_force_unwind; + if ( preserve_fpu) flags_ |= flag_preserve_fpu; + } + + virtual ~coroutine_base() + {} + + bool force_unwind() const BOOST_NOEXCEPT + { return 0 != ( flags_ & flag_force_unwind); } + + bool unwind_requested() const BOOST_NOEXCEPT + { return 0 != ( flags_ & flag_unwind_stack); } + + bool preserve_fpu() const BOOST_NOEXCEPT + { return 0 != ( flags_ & flag_preserve_fpu); } + + bool is_complete() const BOOST_NOEXCEPT + { return 0 != ( flags_ & flag_complete); } + + friend inline void intrusive_ptr_add_ref( coroutine_base * p) BOOST_NOEXCEPT + { ++p->use_count_; } + + friend inline void intrusive_ptr_release( coroutine_base * p) BOOST_NOEXCEPT + { if ( --p->use_count_ == 0) p->deallocate_object(); } +}; + +}}} + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_SUFFIX +#endif + +#endif // BOOST_COROUTINES_DETAIL_COROUTINE_BASE_H diff --git a/include/boost/coroutine/detail/coroutine_base_resume.hpp b/include/boost/coroutine/detail/coroutine_base_resume.hpp new file mode 100644 index 0000000..83a3988 --- /dev/null +++ b/include/boost/coroutine/detail/coroutine_base_resume.hpp @@ -0,0 +1,237 @@ + +// 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_COROUTINES_DETAIL_COROUTINE_BASE_RESUME_H +#define BOOST_COROUTINES_DETAIL_COROUTINE_BASE_RESUME_H + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_PREFIX +#endif + +namespace boost { +namespace coroutines { +namespace detail { + +template< typename Signature, typename D, typename Result, int arity > +class coroutine_base_resume; + +template< typename Signature, typename D > +class coroutine_base_resume< Signature, D, void, 0 > +{ +public: + void resume() + { + BOOST_ASSERT( static_cast< D * >( this)->callee_); + + holder< void > hldr_to( & static_cast< D * >( this)->caller_); + holder< void > * hldr_from( + reinterpret_cast< holder< void > * >( context::jump_fcontext( + hldr_to.ctx, + static_cast< D * >( this)->callee_, + reinterpret_cast< intptr_t >( & hldr_to), + static_cast< D * >( this)->preserve_fpu() ) ) ); + static_cast< D * >( this)->callee_ = hldr_from->ctx; + if ( hldr_from->force_unwind) throw forced_unwind(); + if ( static_cast< D * >( this)->except_) + rethrow_exception( static_cast< D * >( this)->except_); + } +}; + +template< typename Signature, typename D, typename Result > +class coroutine_base_resume< Signature, D, Result, 0 > +{ +public: + void resume() + { + BOOST_ASSERT( static_cast< D * >( this)); + BOOST_ASSERT( ! static_cast< D * >( this)->is_complete() ); + BOOST_ASSERT( static_cast< D * >( this)->callee_); + + holder< void > hldr_to( & static_cast< D * >( this)->caller_); + holder< Result > * hldr_from( + reinterpret_cast< holder< Result > * >( context::jump_fcontext( + hldr_to.ctx, + static_cast< D * >( this)->callee_, + reinterpret_cast< intptr_t >( & hldr_to), + static_cast< D * >( this)->preserve_fpu() ) ) ); + static_cast< D * >( this)->callee_ = hldr_from->ctx; + result_ = hldr_from->data; + if ( hldr_from->force_unwind) throw forced_unwind(); + if ( static_cast< D * >( this)->except_) + rethrow_exception( static_cast< D * >( this)->except_); + } + +protected: + template< typename X, typename Y, int > + friend struct coroutine_get; + + optional< Result > result_; +}; + +template< typename Signature, typename D > +class coroutine_base_resume< Signature, D, void, 1 > +{ +public: + typedef typename arg< Signature >::type arg_type; + + void resume( arg_type a1) + { + BOOST_ASSERT( static_cast< D * >( this)); + BOOST_ASSERT( ! static_cast< D * >( this)->is_complete() ); + BOOST_ASSERT( static_cast< D * >( this)->callee_); + + holder< arg_type > hldr_to( & static_cast< D * >( this)->caller_, a1); + holder< void > * hldr_from( + reinterpret_cast< holder< void > * >( context::jump_fcontext( + hldr_to.ctx, + static_cast< D * >( this)->callee_, + reinterpret_cast< intptr_t >( & hldr_to), + static_cast< D * >( this)->preserve_fpu() ) ) ); + static_cast< D * >( this)->callee_ = hldr_from->ctx; + if ( hldr_from->force_unwind) throw forced_unwind(); + if ( static_cast< D * >( this)->except_) + rethrow_exception( static_cast< D * >( this)->except_); + } +}; + +template< typename Signature, typename D, typename Result > +class coroutine_base_resume< Signature, D, Result, 1 > +{ +public: + typedef typename arg< Signature >::type arg_type; + + void resume( arg_type a1) + { + BOOST_ASSERT( static_cast< D * >( this)); + BOOST_ASSERT( ! static_cast< D * >( this)->is_complete() ); + BOOST_ASSERT( static_cast< D * >( this)->callee_); + + context::fcontext_t caller; + holder< arg_type > hldr_to( & static_cast< D * >( this)->caller_, a1); + holder< Result > * hldr_from( + reinterpret_cast< holder< Result > * >( context::jump_fcontext( + hldr_to.ctx, + static_cast< D * >( this)->callee_, + reinterpret_cast< intptr_t >( & hldr_to), + static_cast< D * >( this)->preserve_fpu() ) ) ); + static_cast< D * >( this)->callee_ = hldr_from->ctx; + result_ = hldr_from->data; + if ( hldr_from->force_unwind) throw forced_unwind(); + if ( static_cast< D * >( this)->except_) + rethrow_exception( static_cast< D * >( this)->except_); + } + +protected: + template< typename X, typename Y, int > + friend struct coroutine_get; + + optional< Result > result_; +}; + +#define BOOST_COROUTINE_BASE_RESUME_COMMA(n) BOOST_PP_COMMA_IF(BOOST_PP_SUB(n,1)) +#define BOOST_COROUTINE_BASE_RESUME_VAL(z,n,unused) BOOST_COROUTINE_BASE_RESUME_COMMA(n) BOOST_PP_CAT(a,n) +#define BOOST_COROUTINE_BASE_RESUME_VALS(n) BOOST_PP_REPEAT_FROM_TO(1,BOOST_PP_ADD(n,1),BOOST_COROUTINE_BASE_RESUME_VAL,~) +#define BOOST_COROUTINE_BASE_RESUME_ARG_TYPE(n) \ + typename function_traits< Signature >::BOOST_PP_CAT(BOOST_PP_CAT(arg,n),_type) +#define BOOST_COROUTINE_BASE_RESUME_ARG(z,n,unused) BOOST_COROUTINE_BASE_RESUME_COMMA(n) BOOST_COROUTINE_BASE_RESUME_ARG_TYPE(n) BOOST_PP_CAT(a,n) +#define BOOST_COROUTINE_BASE_RESUME_ARGS(n) BOOST_PP_REPEAT_FROM_TO(1,BOOST_PP_ADD(n,1),BOOST_COROUTINE_BASE_RESUME_ARG,~) +#define BOOST_COROUTINE_BASE_RESUME(z,n,unused) \ +template< typename Signature, typename D > \ +class coroutine_base_resume< Signature, D, void, n > \ +{ \ +public: \ + typedef typename arg< Signature >::type arg_type; \ +\ + void resume( BOOST_COROUTINE_BASE_RESUME_ARGS(n)) \ + { \ + BOOST_ASSERT( static_cast< D * >( this)); \ + BOOST_ASSERT( ! static_cast< D * >( this)->is_complete() ); \ + BOOST_ASSERT( static_cast< D * >( this)->callee_); \ +\ + holder< arg_type > hldr_to( \ + & static_cast< D * >( this)->caller_, \ + arg_type(BOOST_COROUTINE_BASE_RESUME_VALS(n) ) ); \ + holder< void > * hldr_from( \ + reinterpret_cast< holder< void > * >( context::jump_fcontext( \ + hldr_to.ctx, \ + static_cast< D * >( this)->callee_, \ + reinterpret_cast< intptr_t >( & hldr_to), \ + static_cast< D * >( this)->preserve_fpu() ) ) ); \ + static_cast< D * >( this)->callee_ = hldr_from->ctx; \ + if ( hldr_from->force_unwind) throw forced_unwind(); \ + if ( static_cast< D * >( this)->except_) \ + rethrow_exception( static_cast< D * >( this)->except_); \ + } \ +}; \ +\ +template< typename Signature, typename D, typename Result > \ +class coroutine_base_resume< Signature, D, Result, n > \ +{ \ +public: \ + typedef typename arg< Signature >::type arg_type; \ +\ + void resume( BOOST_COROUTINE_BASE_RESUME_ARGS(n)) \ + { \ + BOOST_ASSERT( static_cast< D * >( this)); \ + BOOST_ASSERT( ! static_cast< D * >( this)->is_complete() ); \ + BOOST_ASSERT( static_cast< D * >( this)->callee_); \ +\ + holder< arg_type > hldr_to( \ + & static_cast< D * >( this)->caller_, \ + arg_type(BOOST_COROUTINE_BASE_RESUME_VALS(n) ) ); \ + holder< Result > * hldr_from( \ + reinterpret_cast< holder< Result > * >( context::jump_fcontext( \ + hldr_to.ctx, \ + static_cast< D * >( this)->callee_, \ + reinterpret_cast< intptr_t >( & hldr_to), \ + static_cast< D * >( this)->preserve_fpu() ) ) ); \ + static_cast< D * >( this)->callee_ = hldr_from->ctx; \ + result_ = hldr_from->data; \ + if ( hldr_from->force_unwind) throw forced_unwind(); \ + if ( static_cast< D * >( this)->except_) \ + rethrow_exception( static_cast< D * >( this)->except_); \ + } \ +\ +protected: \ + template< typename X, typename Y, int > \ + friend struct coroutine_get; \ +\ + optional< Result > result_; \ +}; +BOOST_PP_REPEAT_FROM_TO(2,11,BOOST_COROUTINE_BASE_RESUME,~) +#undef BOOST_COROUTINE_BASE_RESUME +#undef BOOST_COROUTINE_BASE_RESUME_ARGS +#undef BOOST_COROUTINE_BASE_RESUME_ARG +#undef BOOST_COROUTINE_BASE_RESUME_ARG_TYPE +#undef BOOST_COROUTINE_BASE_RESUME_VALS +#undef BOOST_COROUTINE_BASE_RESUME_VAL +#undef BOOST_COROUTINE_BASE_RESUME_COMMA + +}}} + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_SUFFIX +#endif + +#endif // BOOST_COROUTINES_DETAIL_coroutine_base_resume_H diff --git a/include/boost/coroutine/detail/coroutine_caller.hpp b/include/boost/coroutine/detail/coroutine_caller.hpp new file mode 100644 index 0000000..067aff8 --- /dev/null +++ b/include/boost/coroutine/detail/coroutine_caller.hpp @@ -0,0 +1,57 @@ + +// 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_COROUTINES_DETAIL_COROUTINE_CALLER_H +#define BOOST_COROUTINES_DETAIL_COROUTINE_CALLER_H + +#include +#include + +#include +#include + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_PREFIX +#endif + +namespace boost { +namespace coroutines { +namespace detail { + +template< typename Signature, typename Allocator > +class coroutine_caller : public coroutine_base< Signature > +{ +public: + typedef typename Allocator::template rebind< + coroutine_caller< Signature, Allocator > + >::other allocator_t; + + coroutine_caller( context::fcontext_t * callee, bool unwind, bool preserve_fpu, + allocator_t const& alloc) BOOST_NOEXCEPT : + coroutine_base< Signature >( callee, unwind, preserve_fpu), + alloc_( alloc) + {} + + void deallocate_object() + { destroy_( alloc_, this); } + +private: + allocator_t alloc_; + + static void destroy_( allocator_t & alloc, coroutine_caller * p) + { + alloc.destroy( p); + alloc.deallocate( p, 1); + } +}; + +}}} + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_SUFFIX +#endif + +#endif // BOOST_COROUTINES_DETAIL_COROUTINE_CALLER_H diff --git a/include/boost/coroutine/detail/coroutine_get.hpp b/include/boost/coroutine/detail/coroutine_get.hpp new file mode 100644 index 0000000..c2a3339 --- /dev/null +++ b/include/boost/coroutine/detail/coroutine_get.hpp @@ -0,0 +1,54 @@ + +// 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_COROUTINES_DETAIL_COROUTINE_GET_H +#define BOOST_COROUTINES_DETAIL_COROUTINE_GET_H + +#include +#include +#include + +#include +#include + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_PREFIX +#endif + +namespace boost { +namespace coroutines { +namespace detail { + +template< + typename D, + typename Result, int arity +> +struct coroutine_get; + +template< typename D, int arity > +struct coroutine_get< D, void, arity > +{}; + +template< typename D, typename Result, int arity > +struct coroutine_get +{ + bool has_result() const + { return static_cast< D const* >( this)->impl_->result_; } + + typename param< Result >::type get() const + { + BOOST_ASSERT( static_cast< D const* >( this)->impl_->result_); + return static_cast< D const* >( this)->impl_->result_.get(); + } +}; + +}}} + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_SUFFIX +#endif + +#endif // BOOST_COROUTINES_DETAIL_COROUTINE_GET_H diff --git a/include/boost/coroutine/detail/coroutine_object.hpp b/include/boost/coroutine/detail/coroutine_object.hpp new file mode 100644 index 0000000..defb86c --- /dev/null +++ b/include/boost/coroutine/detail/coroutine_object.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 BOOST_COROUTINES_DETAIL_COROUTINE_OBJECT_H +#define BOOST_COROUTINES_DETAIL_COROUTINE_OBJECT_H + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_PREFIX +#endif + +namespace boost { +namespace coroutines { +namespace detail { + +template< typename Context > +void trampoline1( intptr_t vp) +{ + BOOST_ASSERT( vp); + + holder< Context * > * hldr( + reinterpret_cast< holder< Context * > * >( vp) ); + Context * ctx( hldr->data.get() ); + + ctx->run( hldr->ctx); +} + +template< typename Context, typename Arg > +void trampoline2( intptr_t vp) +{ + BOOST_ASSERT( vp); + + holder< tuple< Context *, Arg > > * hldr( + reinterpret_cast< holder< tuple< Context *, Arg > > * >( vp) ); + Context * ctx( hldr->data.get().get< 0 >() ); + Arg arg( hldr->data.get().get< 1 >() ); + + ctx->run( hldr->ctx, arg); +} + +template< + typename Signature, + typename Fn, typename StackAllocator, typename Allocator, + typename Caller, + typename Result, int arity +> +class coroutine_object; + +#include +#include +#include +#include +#include +#include + +}}} + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_SUFFIX +#endif + +#endif // BOOST_COROUTINES_DETAIL_COROUTINE_OBJECT_H diff --git a/include/boost/coroutine/detail/coroutine_object_result_0.ipp b/include/boost/coroutine/detail/coroutine_object_result_0.ipp new file mode 100644 index 0000000..71e155d --- /dev/null +++ b/include/boost/coroutine/detail/coroutine_object_result_0.ipp @@ -0,0 +1,394 @@ + +// 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) + +template< + typename Signature, + typename Fn, typename StackAllocator, typename Allocator, + typename Caller, + typename Result +> +class coroutine_object< Signature, Fn, StackAllocator, Allocator, Caller, Result, 0 > : + public coroutine_base< Signature > +{ +public: + typedef typename Allocator::template rebind< + coroutine_object< + Signature, Fn, StackAllocator, Allocator, Caller, Result, 0 + > + >::other allocator_t; + +private: + typedef coroutine_base< Signature > base_type; + + Fn fn_; + context::stack_t stack_; + StackAllocator stack_alloc_; + allocator_t alloc_; + + static void destroy_( allocator_t & alloc, coroutine_object * p) + { + alloc.destroy( p); + alloc.deallocate( p, 1); + } + + coroutine_object( coroutine_object const&); + coroutine_object & operator=( coroutine_object const&); + + void enter_() + { + holder< coroutine_object * > hldr_to( & this->caller_, this); + holder< Result > * hldr_from( + reinterpret_cast< holder< Result > * >( context::jump_fcontext( + hldr_to.ctx, this->callee_, + reinterpret_cast< intptr_t >( & hldr_to), + this->preserve_fpu() ) ) ); + this->callee_ = hldr_from->ctx; + this->result_ = hldr_from->data; + if ( this->except_) rethrow_exception( this->except_); + } + + void unwind_stack_() BOOST_NOEXCEPT + { + BOOST_ASSERT( ! this->is_complete() ); + + this->flags_ |= flag_unwind_stack; + holder< void > hldr( & this->caller_, true); + context::jump_fcontext( + hldr.ctx, this->callee_, + reinterpret_cast< intptr_t >( & hldr), + this->preserve_fpu() ); + this->flags_ &= ~flag_unwind_stack; + + BOOST_ASSERT( this->is_complete() ); + } + +public: +#ifndef BOOST_NO_RVALUE_REFERENCES + coroutine_object( BOOST_RV_REF( Fn) fn, attributes const& attr, + StackAllocator const& stack_alloc, + allocator_t const& alloc) : + base_type( + context::make_fcontext( + stack_alloc.allocate( attr.size), attr.size, + trampoline1< coroutine_object >), + stack_unwind == attr.do_unwind, + fpu_preserved == attr.preserve_fpu), + fn_( forward< Fn >( fn) ), + stack_( base_type::callee_->fc_stack), + stack_alloc_( stack_alloc), + alloc_( alloc) + { enter_(); } +#else + coroutine_object( Fn fn, attributes const& attr, + StackAllocator const& stack_alloc, + allocator_t const& alloc) : + base_type( + context::make_fcontext( + stack_alloc.allocate( attr.size), attr.size, + trampoline1< coroutine_object >), + stack_unwind == attr.do_unwind, + fpu_preserved == attr.preserve_fpu), + fn_( fn), + stack_( base_type::callee_->fc_stack), + stack_alloc_( stack_alloc), + alloc_( alloc) + { enter_(); } + + coroutine_object( BOOST_RV_REF( Fn) fn, attributes const& attr, + StackAllocator const& stack_alloc, + allocator_t const& alloc) : + base_type( + context::make_fcontext( + stack_alloc.allocate( attr.size), attr.size, + trampoline1< coroutine_object >), + stack_unwind == attr.do_unwind, + fpu_preserved == attr.preserve_fpu), + fn_( fn), + stack_( base_type::callee_->fc_stack), + stack_alloc_( stack_alloc), + alloc_( alloc) + { enter_(); } +#endif + + ~coroutine_object() + { + if ( ! this->is_complete() && this->force_unwind() ) unwind_stack_(); + stack_alloc_.deallocate( stack_.sp, stack_.size); + } + + void run( context::fcontext_t * callee) + { + Caller c( callee, false, this->preserve_fpu(), alloc_); + context::fcontext_t caller; + try + { + fn_( c); + holder< Result > hldr_to( & caller); + this->flags_ |= flag_complete; + callee = c.impl_->callee_; + BOOST_ASSERT( callee); + context::jump_fcontext( + hldr_to.ctx, callee, + reinterpret_cast< intptr_t >( & hldr_to), + this->preserve_fpu() ); + BOOST_ASSERT_MSG( false, "coroutine is complete"); + } + catch ( forced_unwind const&) + {} + catch (...) + { this->except_ = current_exception(); } + + this->flags_ |= flag_complete; + callee = c.impl_->callee_; + BOOST_ASSERT( callee); + context::jump_fcontext( + & caller, callee, + reinterpret_cast< intptr_t >( & caller), + this->preserve_fpu() ); + BOOST_ASSERT_MSG( false, "coroutine is complete"); + } + + void deallocate_object() + { destroy_( alloc_, this); } +}; + +template< + typename Signature, + typename Fn, typename StackAllocator, typename Allocator, + typename Caller, + typename Result +> +class coroutine_object< Signature, reference_wrapper< Fn >, StackAllocator, Allocator, Caller, Result, 0 > : + public coroutine_base< Signature > +{ +public: + typedef typename Allocator::template rebind< + coroutine_object< + Signature, Fn, StackAllocator, Allocator, Caller, Result, 0 + > + >::other allocator_t; + +private: + typedef coroutine_base< Signature > base_type; + + Fn fn_; + context::stack_t stack_; + StackAllocator stack_alloc_; + allocator_t alloc_; + + static void destroy_( allocator_t & alloc, coroutine_object * p) + { + alloc.destroy( p); + alloc.deallocate( p, 1); + } + + coroutine_object( coroutine_object const&); + coroutine_object & operator=( coroutine_object const&); + + void enter_() + { + holder< coroutine_object * > hldr_to( & this->caller_, this); + holder< Result > * hldr_from( + reinterpret_cast< holder< Result > * >( context::jump_fcontext( + hldr_to.ctx, this->callee_, + reinterpret_cast< intptr_t >( & hldr_to), + this->preserve_fpu() ) ) ); + this->callee_ = hldr_from->ctx; + this->result_ = hldr_from->data; + if ( this->except_) rethrow_exception( this->except_); + } + + void unwind_stack_() BOOST_NOEXCEPT + { + BOOST_ASSERT( ! this->is_complete() ); + + this->flags_ |= flag_unwind_stack; + holder< void > hldr( & this->caller_, true); + context::jump_fcontext( + hldr.ctx, this->callee_, + reinterpret_cast< intptr_t >( & hldr), + this->preserve_fpu() ); + this->flags_ &= ~flag_unwind_stack; + + BOOST_ASSERT( this->is_complete() ); + } + +public: + coroutine_object( reference_wrapper< Fn > fn, attributes const& attr, + StackAllocator const& stack_alloc, + allocator_t const& alloc) : + base_type( + context::make_fcontext( + stack_alloc.allocate( attr.size), attr.size, + trampoline1< coroutine_object >), + stack_unwind == attr.do_unwind, + fpu_preserved == attr.preserve_fpu), + fn_( fn), + stack_( base_type::callee_->fc_stack), + stack_alloc_( stack_alloc), + alloc_( alloc) + { enter_(); } + + ~coroutine_object() + { + if ( ! this->is_complete() && this->force_unwind() ) unwind_stack_(); + stack_alloc_.deallocate( stack_.sp, stack_.size); + } + + void run( context::fcontext_t * callee) + { + Caller c( callee, false, this->preserve_fpu(), alloc_); + context::fcontext_t caller; + try + { + fn_( c); + holder< Result > hldr_to( & caller); + this->flags_ |= flag_complete; + callee = c.impl_->callee_; + BOOST_ASSERT( callee); + context::jump_fcontext( + hldr_to.ctx, callee, + reinterpret_cast< intptr_t >( & hldr_to), + this->preserve_fpu() ); + BOOST_ASSERT_MSG( false, "coroutine is complete"); + } + catch ( forced_unwind const&) + {} + catch (...) + { this->except_ = current_exception(); } + + this->flags_ |= flag_complete; + callee = c.impl_->callee_; + BOOST_ASSERT( callee); + context::jump_fcontext( + & caller, callee, + reinterpret_cast< intptr_t >( & caller), + this->preserve_fpu() ); + BOOST_ASSERT_MSG( false, "coroutine is complete"); + } + + void deallocate_object() + { destroy_( alloc_, this); } +}; + +template< + typename Signature, + typename Fn, typename StackAllocator, typename Allocator, + typename Caller, + typename Result +> +class coroutine_object< Signature, const reference_wrapper< Fn >, StackAllocator, Allocator, Caller, Result, 0 > : + public coroutine_base< Signature > +{ +public: + typedef typename Allocator::template rebind< + coroutine_object< + Signature, Fn, StackAllocator, Allocator, Caller, Result, 0 + > + >::other allocator_t; + +private: + typedef coroutine_base< Signature > base_type; + + Fn fn_; + context::stack_t stack_; + StackAllocator stack_alloc_; + allocator_t alloc_; + + static void destroy_( allocator_t & alloc, coroutine_object * p) + { + alloc.destroy( p); + alloc.deallocate( p, 1); + } + + coroutine_object( coroutine_object const&); + coroutine_object & operator=( coroutine_object const&); + + void enter_() + { + holder< coroutine_object * > hldr_to( & this->caller_, this); + holder< Result > * hldr_from( + reinterpret_cast< holder< Result > * >( context::jump_fcontext( + hldr_to.ctx, this->callee_, + reinterpret_cast< intptr_t >( & hldr_to), + this->preserve_fpu() ) ) ); + this->callee_ = hldr_from->ctx; + this->result_ = hldr_from->data; + if ( this->except_) rethrow_exception( this->except_); + } + + void unwind_stack_() BOOST_NOEXCEPT + { + BOOST_ASSERT( ! this->is_complete() ); + + this->flags_ |= flag_unwind_stack; + holder< void > hldr( & this->caller_, true); + context::jump_fcontext( + hldr.ctx, this->callee_, + reinterpret_cast< intptr_t >( & hldr), + this->preserve_fpu() ); + this->flags_ &= ~flag_unwind_stack; + + BOOST_ASSERT( this->is_complete() ); + } + +public: + coroutine_object( const reference_wrapper< Fn > fn, attributes const& attr, + StackAllocator const& stack_alloc, + allocator_t const& alloc) : + base_type( + context::make_fcontext( + stack_alloc.allocate( attr.size), attr.size, + trampoline1< coroutine_object >), + stack_unwind == attr.do_unwind, + fpu_preserved == attr.preserve_fpu), + fn_( fn), + stack_( base_type::callee_->fc_stack), + stack_alloc_( stack_alloc), + alloc_( alloc) + { enter_(); } + + ~coroutine_object() + { + if ( ! this->is_complete() && this->force_unwind() ) unwind_stack_(); + stack_alloc_.deallocate( stack_.sp, stack_.size); + } + + void run( context::fcontext_t * callee) + { + Caller c( callee, false, this->preserve_fpu(), alloc_); + context::fcontext_t caller; + try + { + fn_( c); + holder< Result > hldr_to( & caller); + this->flags_ |= flag_complete; + callee = c.impl_->callee_; + BOOST_ASSERT( callee); + context::jump_fcontext( + hldr_to.ctx, callee, + reinterpret_cast< intptr_t >( & hldr_to), + this->preserve_fpu() ); + BOOST_ASSERT_MSG( false, "coroutine is complete"); + } + catch ( forced_unwind const&) + {} + catch (...) + { this->except_ = current_exception(); } + + this->flags_ |= flag_complete; + callee = c.impl_->callee_; + BOOST_ASSERT( callee); + context::jump_fcontext( + & caller, callee, + reinterpret_cast< intptr_t >( & caller), + this->preserve_fpu() ); + BOOST_ASSERT_MSG( false, "coroutine is complete"); + } + + void deallocate_object() + { destroy_( alloc_, this); } +}; diff --git a/include/boost/coroutine/detail/coroutine_object_result_1.ipp b/include/boost/coroutine/detail/coroutine_object_result_1.ipp new file mode 100644 index 0000000..71e24c4 --- /dev/null +++ b/include/boost/coroutine/detail/coroutine_object_result_1.ipp @@ -0,0 +1,565 @@ + +// 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) + +template< + typename Signature, + typename Fn, typename StackAllocator, typename Allocator, + typename Caller, + typename Result +> +class coroutine_object< Signature, Fn, StackAllocator, Allocator, Caller, Result, 1 > : + public coroutine_base< Signature > +{ +public: + typedef typename Allocator::template rebind< + coroutine_object< + Signature, Fn, StackAllocator, Allocator, Caller, Result, 1 + > + >::other allocator_t; + typedef typename arg< Signature >::type arg_type; + +private: + typedef coroutine_base< Signature > base_type; + + Fn fn_; + context::stack_t stack_; + StackAllocator stack_alloc_; + allocator_t alloc_; + + static void destroy_( allocator_t & alloc, coroutine_object * p) + { + alloc.destroy( p); + alloc.deallocate( p, 1); + } + + coroutine_object( coroutine_object &); + coroutine_object & operator=( coroutine_object const&); + + void enter_() + { + holder< coroutine_object * > hldr_to( & this->caller_, this); + holder< Result > * hldr_from( + reinterpret_cast< holder< Result > * >( context::jump_fcontext( + hldr_to.ctx, this->callee_, + reinterpret_cast< intptr_t >( & hldr_to), + this->preserve_fpu() ) ) ); + this->callee_ = hldr_from->ctx; + this->result_ = hldr_from->data; + if ( this->except_) rethrow_exception( this->except_); + } + + void enter_( typename detail::param< arg_type >::type arg) + { + holder< + tuple< coroutine_object *, + typename detail::param< arg_type >::type > + > hldr_to( + & this->caller_, tuple< coroutine_object *, + typename detail::param< arg_type >::type >( this, arg) ); + holder< Result > * hldr_from( + reinterpret_cast< holder< Result > * >( context::jump_fcontext( + hldr_to.ctx, this->callee_, + reinterpret_cast< intptr_t >( & hldr_to), + this->preserve_fpu() ) ) ); + this->callee_ = hldr_from->ctx; + this->result_ = hldr_from->data; + if ( this->except_) rethrow_exception( this->except_); + } + + void run_( Caller & c) + { + context::fcontext_t * callee( 0); + context::fcontext_t caller; + try + { + fn_( c); + this->flags_ |= flag_complete; + callee = c.impl_->callee_; + BOOST_ASSERT( callee); + holder< Result > hldr_to( & caller); + context::jump_fcontext( + hldr_to.ctx, callee, + reinterpret_cast< intptr_t >( & hldr_to), + this->preserve_fpu() ); + BOOST_ASSERT_MSG( false, "coroutine is complete"); + } + catch ( forced_unwind const&) + {} + catch (...) + { this->except_ = current_exception(); } + + this->flags_ |= flag_complete; + callee = c.impl_->callee_; + BOOST_ASSERT( callee); + context::jump_fcontext( + & caller, callee, + reinterpret_cast< intptr_t >( & caller), + this->preserve_fpu() ); + BOOST_ASSERT_MSG( false, "coroutine is complete"); + } + + void unwind_stack_() BOOST_NOEXCEPT + { + BOOST_ASSERT( ! this->is_complete() ); + + this->flags_ |= flag_unwind_stack; + holder< arg_type > hldr( & this->caller_, true); + context::jump_fcontext( + hldr.ctx, this->callee_, + reinterpret_cast< intptr_t >( & hldr), + this->preserve_fpu() ); + this->flags_ &= ~flag_unwind_stack; + + BOOST_ASSERT( this->is_complete() ); + } + +public: +#ifndef BOOST_NO_RVALUE_REFERENCES + coroutine_object( BOOST_RV_REF( Fn) fn, attributes const& attr, + StackAllocator const& stack_alloc, + allocator_t const& alloc) : + base_type( + context::make_fcontext( + stack_alloc.allocate( attr.size), attr.size, + trampoline1< coroutine_object >), + stack_unwind == attr.do_unwind, + fpu_preserved == attr.preserve_fpu), + fn_( forward< Fn >( fn) ), + stack_( base_type::callee_->fc_stack), + stack_alloc_( stack_alloc), + alloc_( alloc) + { enter_(); } + + coroutine_object( BOOST_RV_REF( Fn) fn, typename detail::param< arg_type >::type arg, attributes const& attr, + StackAllocator const& stack_alloc, + allocator_t const& alloc) : + base_type( + context::make_fcontext( + stack_alloc.allocate( attr.size), attr.size, + trampoline2< coroutine_object, typename detail::param< arg_type >::type >), + stack_unwind == attr.do_unwind, + fpu_preserved == attr.preserve_fpu), + fn_( forward< Fn >( fn) ), + stack_( base_type::callee_->fc_stack), + stack_alloc_( stack_alloc), + alloc_( alloc) + { enter_( arg); } +#else + coroutine_object( Fn fn, attributes const& attr, + StackAllocator const& stack_alloc, + allocator_t const& alloc) : + base_type( + context::make_fcontext( + stack_alloc.allocate( attr.size), attr.size, + trampoline1< coroutine_object >), + stack_unwind == attr.do_unwind, + fpu_preserved == attr.preserve_fpu), + fn_( fn), + stack_( base_type::callee_->fc_stack), + stack_alloc_( stack_alloc), + alloc_( alloc) + { enter_(); } + + coroutine_object( Fn fn, typename detail::param< arg_type >::type arg, attributes const& attr, + StackAllocator const& stack_alloc, + allocator_t const& alloc) : + base_type( + context::make_fcontext( + stack_alloc.allocate( attr.size), attr.size, + trampoline2< coroutine_object, typename detail::param< arg_type >::type >), + stack_unwind == attr.do_unwind, + fpu_preserved == attr.preserve_fpu), + fn_( fn), + stack_( base_type::callee_->fc_stack), + stack_alloc_( stack_alloc), + alloc_( alloc) + { enter_( arg); } + + coroutine_object( BOOST_RV_REF( Fn) fn, attributes const& attr, + StackAllocator const& stack_alloc, + allocator_t const& alloc) : + base_type( + context::make_fcontext( + stack_alloc.allocate( attr.size), attr.size, + trampoline1< coroutine_object >), + stack_unwind == attr.do_unwind, + fpu_preserved == attr.preserve_fpu), + fn_( fn), + stack_( base_type::callee_->fc_stack), + stack_alloc_( stack_alloc), + alloc_( alloc) + { enter_(); } + + coroutine_object( BOOST_RV_REF( Fn) fn, typename detail::param< arg_type >::type arg, attributes const& attr, + StackAllocator const& stack_alloc, + allocator_t const& alloc) : + base_type( + context::make_fcontext( + stack_alloc.allocate( attr.size), attr.size, + trampoline2< coroutine_object, typename detail::param< arg_type >::type >), + stack_unwind == attr.do_unwind, + fpu_preserved == attr.preserve_fpu), + fn_( fn), + stack_( base_type::callee_->fc_stack), + stack_alloc_( stack_alloc), + alloc_( alloc) + { enter_( arg); } +#endif + + ~coroutine_object() + { + if ( ! this->is_complete() && this->force_unwind() ) unwind_stack_(); + stack_alloc_.deallocate( stack_.sp, stack_.size); + } + + void run( context::fcontext_t * callee) + { + Caller c( callee, false, this->preserve_fpu(), alloc_); + run_( c); + } + + void run( context::fcontext_t * callee, typename detail::param< arg_type >::type arg) + { + Caller c( callee, false, this->preserve_fpu(), alloc_); + c.impl_->result_ = arg; + run_( c); + } + + void deallocate_object() + { destroy_( alloc_, this); } +}; + +template< + typename Signature, + typename Fn, typename StackAllocator, typename Allocator, + typename Caller, + typename Result +> +class coroutine_object< Signature, reference_wrapper< Fn >, StackAllocator, Allocator, Caller, Result, 1 > : + public coroutine_base< Signature > +{ +public: + typedef typename Allocator::template rebind< + coroutine_object< + Signature, Fn, StackAllocator, Allocator, Caller, Result, 1 + > + >::other allocator_t; + typedef typename arg< Signature >::type arg_type; + +private: + typedef coroutine_base< Signature > base_type; + + Fn fn_; + context::stack_t stack_; + StackAllocator stack_alloc_; + allocator_t alloc_; + + static void destroy_( allocator_t & alloc, coroutine_object * p) + { + alloc.destroy( p); + alloc.deallocate( p, 1); + } + + coroutine_object( coroutine_object &); + coroutine_object & operator=( coroutine_object const&); + + void enter_() + { + holder< coroutine_object * > hldr_to( & this->caller_, this); + holder< Result > * hldr_from( + reinterpret_cast< holder< Result > * >( context::jump_fcontext( + hldr_to.ctx, this->callee_, + reinterpret_cast< intptr_t >( & hldr_to), + this->preserve_fpu() ) ) ); + this->callee_ = hldr_from->ctx; + this->result_ = hldr_from->data; + if ( this->except_) rethrow_exception( this->except_); + } + + void enter_( typename detail::param< arg_type >::type arg) + { + holder< + tuple< coroutine_object *, + typename detail::param< arg_type >::type > + > hldr_to( + & this->caller_, tuple< coroutine_object *, + typename detail::param< arg_type >::type >( this, arg) ); + holder< Result > * hldr_from( + reinterpret_cast< holder< Result > * >( context::jump_fcontext( + hldr_to.ctx, this->callee_, + reinterpret_cast< intptr_t >( & hldr_to), + this->preserve_fpu() ) ) ); + this->callee_ = hldr_from->ctx; + this->result_ = hldr_from->data; + if ( this->except_) rethrow_exception( this->except_); + } + + void run_( Caller & c) + { + context::fcontext_t * callee( 0); + context::fcontext_t caller; + try + { + fn_( c); + this->flags_ |= flag_complete; + callee = c.impl_->callee_; + BOOST_ASSERT( callee); + holder< Result > hldr_to( & caller); + context::jump_fcontext( + hldr_to.ctx, callee, + reinterpret_cast< intptr_t >( & hldr_to), + this->preserve_fpu() ); + BOOST_ASSERT_MSG( false, "coroutine is complete"); + } + catch ( forced_unwind const&) + {} + catch (...) + { this->except_ = current_exception(); } + + this->flags_ |= flag_complete; + callee = c.impl_->callee_; + BOOST_ASSERT( callee); + context::jump_fcontext( + & caller, callee, + reinterpret_cast< intptr_t >( & caller), + this->preserve_fpu() ); + BOOST_ASSERT_MSG( false, "coroutine is complete"); + } + + void unwind_stack_() BOOST_NOEXCEPT + { + BOOST_ASSERT( ! this->is_complete() ); + + this->flags_ |= flag_unwind_stack; + holder< arg_type > hldr( & this->caller_, true); + context::jump_fcontext( + hldr.ctx, this->callee_, + reinterpret_cast< intptr_t >( & hldr), + this->preserve_fpu() ); + this->flags_ &= ~flag_unwind_stack; + + BOOST_ASSERT( this->is_complete() ); + } + +public: + coroutine_object( reference_wrapper< Fn > fn, attributes const& attr, + StackAllocator const& stack_alloc, + allocator_t const& alloc) : + base_type( + context::make_fcontext( + stack_alloc.allocate( attr.size), attr.size, + trampoline1< coroutine_object >), + stack_unwind == attr.do_unwind, + fpu_preserved == attr.preserve_fpu), + fn_( fn), + stack_( base_type::callee_->fc_stack), + stack_alloc_( stack_alloc), + alloc_( alloc) + { enter_(); } + + coroutine_object( reference_wrapper< Fn > fn, typename detail::param< arg_type >::type arg, attributes const& attr, + StackAllocator const& stack_alloc, + allocator_t const& alloc) : + base_type( + context::make_fcontext( + stack_alloc.allocate( attr.size), attr.size, + trampoline2< coroutine_object, typename detail::param< arg_type >::type >), + stack_unwind == attr.do_unwind, + fpu_preserved == attr.preserve_fpu), + fn_( fn), + stack_( base_type::callee_->fc_stack), + stack_alloc_( stack_alloc), + alloc_( alloc) + { enter_( arg); } + + ~coroutine_object() + { + if ( ! this->is_complete() && this->force_unwind() ) unwind_stack_(); + stack_alloc_.deallocate( stack_.sp, stack_.size); + } + + void run( context::fcontext_t * callee) + { + Caller c( callee, false, this->preserve_fpu(), alloc_); + run_( c); + } + + void run( context::fcontext_t * callee, typename detail::param< arg_type >::type arg) + { + Caller c( callee, false, this->preserve_fpu(), alloc_); + c.impl_->result_ = arg; + run_( c); + } + + void deallocate_object() + { destroy_( alloc_, this); } +}; + +template< + typename Signature, + typename Fn, typename StackAllocator, typename Allocator, + typename Caller, + typename Result +> +class coroutine_object< Signature, const reference_wrapper< Fn >, StackAllocator, Allocator, Caller, Result, 1 > : + public coroutine_base< Signature > +{ +public: + typedef typename Allocator::template rebind< + coroutine_object< + Signature, Fn, StackAllocator, Allocator, Caller, Result, 1 + > + >::other allocator_t; + typedef typename arg< Signature >::type arg_type; + +private: + typedef coroutine_base< Signature > base_type; + + Fn fn_; + context::stack_t stack_; + StackAllocator stack_alloc_; + allocator_t alloc_; + + static void destroy_( allocator_t & alloc, coroutine_object * p) + { + alloc.destroy( p); + alloc.deallocate( p, 1); + } + + coroutine_object( coroutine_object &); + coroutine_object & operator=( coroutine_object const&); + + void enter_() + { + holder< coroutine_object * > hldr_to( & this->caller_, this); + holder< Result > * hldr_from( + reinterpret_cast< holder< Result > * >( context::jump_fcontext( + hldr_to.ctx, this->callee_, + reinterpret_cast< intptr_t >( & hldr_to), + this->preserve_fpu() ) ) ); + this->callee_ = hldr_from->ctx; + this->result_ = hldr_from->data; + if ( this->except_) rethrow_exception( this->except_); + } + + void enter_( typename detail::param< arg_type >::type arg) + { + holder< + tuple< coroutine_object *, + typename detail::param< arg_type >::type > + > hldr_to( + & this->caller_, tuple< coroutine_object *, + typename detail::param< arg_type >::type >( this, arg) ); + holder< Result > * hldr_from( + reinterpret_cast< holder< Result > * >( context::jump_fcontext( + hldr_to.ctx, this->callee_, + reinterpret_cast< intptr_t >( & hldr_to), + this->preserve_fpu() ) ) ); + this->callee_ = hldr_from->ctx; + this->result_ = hldr_from->data; + if ( this->except_) rethrow_exception( this->except_); + } + + void run_( Caller & c) + { + context::fcontext_t * callee( 0); + context::fcontext_t caller; + try + { + fn_( c); + this->flags_ |= flag_complete; + callee = c.impl_->callee_; + BOOST_ASSERT( callee); + holder< Result > hldr_to( & caller); + context::jump_fcontext( + hldr_to.ctx, callee, + reinterpret_cast< intptr_t >( & hldr_to), + this->preserve_fpu() ); + BOOST_ASSERT_MSG( false, "coroutine is complete"); + } + catch ( forced_unwind const&) + {} + catch (...) + { this->except_ = current_exception(); } + + this->flags_ |= flag_complete; + callee = c.impl_->callee_; + BOOST_ASSERT( callee); + context::jump_fcontext( + & caller, callee, + reinterpret_cast< intptr_t >( & caller), + this->preserve_fpu() ); + BOOST_ASSERT_MSG( false, "coroutine is complete"); + } + + void unwind_stack_() BOOST_NOEXCEPT + { + BOOST_ASSERT( ! this->is_complete() ); + + this->flags_ |= flag_unwind_stack; + holder< arg_type > hldr( & this->caller_, true); + context::jump_fcontext( + hldr.ctx, this->callee_, + reinterpret_cast< intptr_t >( & hldr), + this->preserve_fpu() ); + this->flags_ &= ~flag_unwind_stack; + + BOOST_ASSERT( this->is_complete() ); + } + +public: + coroutine_object( const reference_wrapper< Fn > fn, attributes const& attr, + StackAllocator const& stack_alloc, + allocator_t const& alloc) : + base_type( + context::make_fcontext( + stack_alloc.allocate( attr.size), attr.size, + trampoline1< coroutine_object >), + stack_unwind == attr.do_unwind, + fpu_preserved == attr.preserve_fpu), + fn_( fn), + stack_( base_type::callee_->fc_stack), + stack_alloc_( stack_alloc), + alloc_( alloc) + { enter_(); } + + coroutine_object( const reference_wrapper< Fn > fn, typename detail::param< arg_type >::type arg, attributes const& attr, + StackAllocator const& stack_alloc, + allocator_t const& alloc) : + base_type( + context::make_fcontext( + stack_alloc.allocate( attr.size), attr.size, + trampoline2< coroutine_object, typename detail::param< arg_type >::type >), + stack_unwind == attr.do_unwind, + fpu_preserved == attr.preserve_fpu), + fn_( fn), + stack_( base_type::callee_->fc_stack), + stack_alloc_( stack_alloc), + alloc_( alloc) + { enter_( arg); } + + ~coroutine_object() + { + if ( ! this->is_complete() && this->force_unwind() ) unwind_stack_(); + stack_alloc_.deallocate( stack_.sp, stack_.size); + } + + void run( context::fcontext_t * callee) + { + Caller c( callee, false, this->preserve_fpu(), alloc_); + run_( c); + } + + void run( context::fcontext_t * callee, typename detail::param< arg_type >::type arg) + { + Caller c( callee, false, this->preserve_fpu(), alloc_); + c.impl_->result_ = arg; + run_( c); + } + + void deallocate_object() + { destroy_( alloc_, this); } +}; diff --git a/include/boost/coroutine/detail/coroutine_object_result_arity.ipp b/include/boost/coroutine/detail/coroutine_object_result_arity.ipp new file mode 100644 index 0000000..95451f8 --- /dev/null +++ b/include/boost/coroutine/detail/coroutine_object_result_arity.ipp @@ -0,0 +1,565 @@ + +// 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) + +template< + typename Signature, + typename Fn, typename StackAllocator, typename Allocator, + typename Caller, + typename Result, int arity +> +class coroutine_object : + public coroutine_base< Signature > +{ +public: + typedef typename Allocator::template rebind< + coroutine_object< + Signature, Fn, StackAllocator, Allocator, Caller, Result, arity + > + >::other allocator_t; + typedef typename arg< Signature >::type arg_type; + +private: + typedef coroutine_base< Signature > base_type; + + Fn fn_; + context::stack_t stack_; + StackAllocator stack_alloc_; + allocator_t alloc_; + + static void destroy_( allocator_t & alloc, coroutine_object * p) + { + alloc.destroy( p); + alloc.deallocate( p, 1); + } + + coroutine_object( coroutine_object &); + coroutine_object & operator=( coroutine_object const&); + + void enter_() + { + holder< coroutine_object * > hldr_to( & this->caller_, this); + holder< Result > * hldr_from( + reinterpret_cast< holder< Result > * >( context::jump_fcontext( + hldr_to.ctx, this->callee_, + reinterpret_cast< intptr_t >( & hldr_to), + this->preserve_fpu() ) ) ); + this->callee_ = hldr_from->ctx; + this->result_ = hldr_from->data; + if ( this->except_) rethrow_exception( this->except_); + } + + void enter_( typename detail::param< arg_type >::type arg) + { + holder< + tuple< coroutine_object *, + typename detail::param< arg_type >::type > + > hldr_to( + & this->caller_, tuple< coroutine_object *, + typename detail::param< arg_type >::type >( this, arg) ); + holder< Result > * hldr_from( + reinterpret_cast< holder< Result > * >( context::jump_fcontext( + hldr_to.ctx, this->callee_, + reinterpret_cast< intptr_t >( & hldr_to), + this->preserve_fpu() ) ) ); + this->callee_ = hldr_from->ctx; + this->result_ = hldr_from->data; + if ( this->except_) rethrow_exception( this->except_); + } + + void run_( Caller & c) + { + context::fcontext_t * callee( 0); + context::fcontext_t caller; + try + { + fn_( c); + this->flags_ |= flag_complete; + callee = c.impl_->callee_; + BOOST_ASSERT( callee); + holder< Result > hldr_to( & caller); + context::jump_fcontext( + hldr_to.ctx, callee, + reinterpret_cast< intptr_t >( & hldr_to), + this->preserve_fpu() ); + BOOST_ASSERT_MSG( false, "coroutine is complete"); + } + catch ( forced_unwind const&) + {} + catch (...) + { this->except_ = current_exception(); } + + this->flags_ |= flag_complete; + callee = c.impl_->callee_; + BOOST_ASSERT( callee); + context::jump_fcontext( + & caller, callee, + reinterpret_cast< intptr_t >( & caller), + this->preserve_fpu() ); + BOOST_ASSERT_MSG( false, "coroutine is complete"); + } + + void unwind_stack_() BOOST_NOEXCEPT + { + BOOST_ASSERT( ! this->is_complete() ); + + this->flags_ |= flag_unwind_stack; + holder< arg_type > hldr( & this->caller_, true); + context::jump_fcontext( + hldr.ctx, this->callee_, + reinterpret_cast< intptr_t >( & hldr), + this->preserve_fpu() ); + this->flags_ &= ~flag_unwind_stack; + + BOOST_ASSERT( this->is_complete() ); + } + +public: +#ifndef BOOST_NO_RVALUE_REFERENCES + coroutine_object( BOOST_RV_REF( Fn) fn, attributes const& attr, + StackAllocator const& stack_alloc, + allocator_t const& alloc) : + base_type( + context::make_fcontext( + stack_alloc.allocate( attr.size), attr.size, + trampoline1< coroutine_object >), + stack_unwind == attr.do_unwind, + fpu_preserved == attr.preserve_fpu), + fn_( forward< Fn >( fn) ), + stack_( base_type::callee_->fc_stack), + stack_alloc_( stack_alloc), + alloc_( alloc) + { enter_(); } + + coroutine_object( BOOST_RV_REF( Fn) fn, typename detail::param< arg_type >::type arg, attributes const& attr, + StackAllocator const& stack_alloc, + allocator_t const& alloc) : + base_type( + context::make_fcontext( + stack_alloc.allocate( attr.size), attr.size, + trampoline2< coroutine_object, typename detail::param< arg_type >::type >), + stack_unwind == attr.do_unwind, + fpu_preserved == attr.preserve_fpu), + fn_( forward< Fn >( fn) ), + stack_( base_type::callee_->fc_stack), + stack_alloc_( stack_alloc), + alloc_( alloc) + { enter_( arg); } +#else + coroutine_object( Fn fn, attributes const& attr, + StackAllocator const& stack_alloc, + allocator_t const& alloc) : + base_type( + context::make_fcontext( + stack_alloc.allocate( attr.size), attr.size, + trampoline1< coroutine_object >), + stack_unwind == attr.do_unwind, + fpu_preserved == attr.preserve_fpu), + fn_( fn), + stack_( base_type::callee_->fc_stack), + stack_alloc_( stack_alloc), + alloc_( alloc) + { enter_(); } + + coroutine_object( Fn fn, typename detail::param< arg_type >::type arg, attributes const& attr, + StackAllocator const& stack_alloc, + allocator_t const& alloc) : + base_type( + context::make_fcontext( + stack_alloc.allocate( attr.size), attr.size, + trampoline2< coroutine_object, typename detail::param< arg_type >::type >), + stack_unwind == attr.do_unwind, + fpu_preserved == attr.preserve_fpu), + fn_( fn), + stack_( base_type::callee_->fc_stack), + stack_alloc_( stack_alloc), + alloc_( alloc) + { enter_( arg); } + + coroutine_object( BOOST_RV_REF( Fn) fn, attributes const& attr, + StackAllocator const& stack_alloc, + allocator_t const& alloc) : + base_type( + context::make_fcontext( + stack_alloc.allocate( attr.size), attr.size, + trampoline1< coroutine_object >), + stack_unwind == attr.do_unwind, + fpu_preserved == attr.preserve_fpu), + fn_( fn), + stack_( base_type::callee_->fc_stack), + stack_alloc_( stack_alloc), + alloc_( alloc) + { enter_(); } + + coroutine_object( BOOST_RV_REF( Fn) fn, typename detail::param< arg_type >::type arg, attributes const& attr, + StackAllocator const& stack_alloc, + allocator_t const& alloc) : + base_type( + context::make_fcontext( + stack_alloc.allocate( attr.size), attr.size, + trampoline2< coroutine_object, typename detail::param< arg_type >::type >), + stack_unwind == attr.do_unwind, + fpu_preserved == attr.preserve_fpu), + fn_( fn), + stack_( base_type::callee_->fc_stack), + stack_alloc_( stack_alloc), + alloc_( alloc) + { enter_( arg); } +#endif + + ~coroutine_object() + { + if ( ! this->is_complete() && this->force_unwind() ) unwind_stack_(); + stack_alloc_.deallocate( stack_.sp, stack_.size); + } + + void run( context::fcontext_t * callee) + { + Caller c( callee, false, this->preserve_fpu(), alloc_); + run_( c); + } + + void run( context::fcontext_t * callee, typename detail::param< arg_type >::type arg) + { + Caller c( callee, false, this->preserve_fpu(), alloc_); + c.impl_->result_ = arg; + run_( c); + } + + void deallocate_object() + { destroy_( alloc_, this); } +}; + +template< + typename Signature, + typename Fn, typename StackAllocator, typename Allocator, + typename Caller, + typename Result, int arity +> +class coroutine_object< Signature, reference_wrapper< Fn >, StackAllocator, Allocator, Caller, Result, arity > : + public coroutine_base< Signature > +{ +public: + typedef typename Allocator::template rebind< + coroutine_object< + Signature, Fn, StackAllocator, Allocator, Caller, Result, arity + > + >::other allocator_t; + typedef typename arg< Signature >::type arg_type; + +private: + typedef coroutine_base< Signature > base_type; + + Fn fn_; + context::stack_t stack_; + StackAllocator stack_alloc_; + allocator_t alloc_; + + static void destroy_( allocator_t & alloc, coroutine_object * p) + { + alloc.destroy( p); + alloc.deallocate( p, 1); + } + + coroutine_object( coroutine_object &); + coroutine_object & operator=( coroutine_object const&); + + void enter_() + { + holder< coroutine_object * > hldr_to( & this->caller_, this); + holder< Result > * hldr_from( + reinterpret_cast< holder< Result > * >( context::jump_fcontext( + hldr_to.ctx, this->callee_, + reinterpret_cast< intptr_t >( & hldr_to), + this->preserve_fpu() ) ) ); + this->callee_ = hldr_from->ctx; + this->result_ = hldr_from->data; + if ( this->except_) rethrow_exception( this->except_); + } + + void enter_( typename detail::param< arg_type >::type arg) + { + holder< + tuple< coroutine_object *, + typename detail::param< arg_type >::type > + > hldr_to( + & this->caller_, tuple< coroutine_object *, + typename detail::param< arg_type >::type >( this, arg) ); + holder< Result > * hldr_from( + reinterpret_cast< holder< Result > * >( context::jump_fcontext( + hldr_to.ctx, this->callee_, + reinterpret_cast< intptr_t >( & hldr_to), + this->preserve_fpu() ) ) ); + this->callee_ = hldr_from->ctx; + this->result_ = hldr_from->data; + if ( this->except_) rethrow_exception( this->except_); + } + + void run_( Caller & c) + { + context::fcontext_t * callee( 0); + context::fcontext_t caller; + try + { + fn_( c); + this->flags_ |= flag_complete; + callee = c.impl_->callee_; + BOOST_ASSERT( callee); + holder< Result > hldr_to( & caller); + context::jump_fcontext( + hldr_to.ctx, callee, + reinterpret_cast< intptr_t >( & hldr_to), + this->preserve_fpu() ); + BOOST_ASSERT_MSG( false, "coroutine is complete"); + } + catch ( forced_unwind const&) + {} + catch (...) + { this->except_ = current_exception(); } + + this->flags_ |= flag_complete; + callee = c.impl_->callee_; + BOOST_ASSERT( callee); + context::jump_fcontext( + & caller, callee, + reinterpret_cast< intptr_t >( & caller), + this->preserve_fpu() ); + BOOST_ASSERT_MSG( false, "coroutine is complete"); + } + + void unwind_stack_() BOOST_NOEXCEPT + { + BOOST_ASSERT( ! this->is_complete() ); + + this->flags_ |= flag_unwind_stack; + holder< arg_type > hldr( & this->caller_, true); + context::jump_fcontext( + hldr.ctx, this->callee_, + reinterpret_cast< intptr_t >( & hldr), + this->preserve_fpu() ); + this->flags_ &= ~flag_unwind_stack; + + BOOST_ASSERT( this->is_complete() ); + } + +public: + coroutine_object( reference_wrapper< Fn > fn, attributes const& attr, + StackAllocator const& stack_alloc, + allocator_t const& alloc) : + base_type( + context::make_fcontext( + stack_alloc.allocate( attr.size), attr.size, + trampoline1< coroutine_object >), + stack_unwind == attr.do_unwind, + fpu_preserved == attr.preserve_fpu), + fn_( fn), + stack_( base_type::callee_->fc_stack), + stack_alloc_( stack_alloc), + alloc_( alloc) + { enter_(); } + + coroutine_object( reference_wrapper< Fn > fn, typename detail::param< arg_type >::type arg, attributes const& attr, + StackAllocator const& stack_alloc, + allocator_t const& alloc) : + base_type( + context::make_fcontext( + stack_alloc.allocate( attr.size), attr.size, + trampoline2< coroutine_object, typename detail::param< arg_type >::type >), + stack_unwind == attr.do_unwind, + fpu_preserved == attr.preserve_fpu), + fn_( fn), + stack_( base_type::callee_->fc_stack), + stack_alloc_( stack_alloc), + alloc_( alloc) + { enter_( arg); } + + ~coroutine_object() + { + if ( ! this->is_complete() && this->force_unwind() ) unwind_stack_(); + stack_alloc_.deallocate( stack_.sp, stack_.size); + } + + void run( context::fcontext_t * callee) + { + Caller c( callee, false, this->preserve_fpu(), alloc_); + run_( c); + } + + void run( context::fcontext_t * callee, typename detail::param< arg_type >::type arg) + { + Caller c( callee, false, this->preserve_fpu(), alloc_); + c.impl_->result_ = arg; + run_( c); + } + + void deallocate_object() + { destroy_( alloc_, this); } +}; + +template< + typename Signature, + typename Fn, typename StackAllocator, typename Allocator, + typename Caller, + typename Result, int arity +> +class coroutine_object< Signature, const reference_wrapper< Fn >, StackAllocator, Allocator, Caller, Result, arity > : + public coroutine_base< Signature > +{ +public: + typedef typename Allocator::template rebind< + coroutine_object< + Signature, Fn, StackAllocator, Allocator, Caller, Result, arity + > + >::other allocator_t; + typedef typename arg< Signature >::type arg_type; + +private: + typedef coroutine_base< Signature > base_type; + + Fn fn_; + context::stack_t stack_; + StackAllocator stack_alloc_; + allocator_t alloc_; + + static void destroy_( allocator_t & alloc, coroutine_object * p) + { + alloc.destroy( p); + alloc.deallocate( p, 1); + } + + coroutine_object( coroutine_object &); + coroutine_object & operator=( coroutine_object const&); + + void enter_() + { + holder< coroutine_object * > hldr_to( & this->caller_, this); + holder< Result > * hldr_from( + reinterpret_cast< holder< Result > * >( context::jump_fcontext( + hldr_to.ctx, this->callee_, + reinterpret_cast< intptr_t >( & hldr_to), + this->preserve_fpu() ) ) ); + this->callee_ = hldr_from->ctx; + this->result_ = hldr_from->data; + if ( this->except_) rethrow_exception( this->except_); + } + + void enter_( typename detail::param< arg_type >::type arg) + { + holder< + tuple< coroutine_object *, + typename detail::param< arg_type >::type > + > hldr_to( + & this->caller_, tuple< coroutine_object *, + typename detail::param< arg_type >::type >( this, arg) ); + holder< Result > * hldr_from( + reinterpret_cast< holder< Result > * >( context::jump_fcontext( + hldr_to.ctx, this->callee_, + reinterpret_cast< intptr_t >( & hldr_to), + this->preserve_fpu() ) ) ); + this->callee_ = hldr_from->ctx; + this->result_ = hldr_from->data; + if ( this->except_) rethrow_exception( this->except_); + } + + void run_( Caller & c) + { + context::fcontext_t * callee( 0); + context::fcontext_t caller; + try + { + fn_( c); + this->flags_ |= flag_complete; + callee = c.impl_->callee_; + BOOST_ASSERT( callee); + holder< Result > hldr_to( & caller); + context::jump_fcontext( + hldr_to.ctx, callee, + reinterpret_cast< intptr_t >( & hldr_to), + this->preserve_fpu() ); + BOOST_ASSERT_MSG( false, "coroutine is complete"); + } + catch ( forced_unwind const&) + {} + catch (...) + { this->except_ = current_exception(); } + + this->flags_ |= flag_complete; + callee = c.impl_->callee_; + BOOST_ASSERT( callee); + context::jump_fcontext( + & caller, callee, + reinterpret_cast< intptr_t >( & caller), + this->preserve_fpu() ); + BOOST_ASSERT_MSG( false, "coroutine is complete"); + } + + void unwind_stack_() BOOST_NOEXCEPT + { + BOOST_ASSERT( ! this->is_complete() ); + + this->flags_ |= flag_unwind_stack; + holder< arg_type > hldr( & this->caller_, true); + context::jump_fcontext( + hldr.ctx, this->callee_, + reinterpret_cast< intptr_t >( & hldr), + this->preserve_fpu() ); + this->flags_ &= ~flag_unwind_stack; + + BOOST_ASSERT( this->is_complete() ); + } + +public: + coroutine_object( const reference_wrapper< Fn > fn, attributes const& attr, + StackAllocator const& stack_alloc, + allocator_t const& alloc) : + base_type( + context::make_fcontext( + stack_alloc.allocate( attr.size), attr.size, + trampoline1< coroutine_object >), + stack_unwind == attr.do_unwind, + fpu_preserved == attr.preserve_fpu), + fn_( fn), + stack_( base_type::callee_->fc_stack), + stack_alloc_( stack_alloc), + alloc_( alloc) + { enter_(); } + + coroutine_object( const reference_wrapper< Fn > fn, typename detail::param< arg_type >::type arg, attributes const& attr, + StackAllocator const& stack_alloc, + allocator_t const& alloc) : + base_type( + context::make_fcontext( + stack_alloc.allocate( attr.size), attr.size, + trampoline2< coroutine_object, typename detail::param< arg_type >::type >), + stack_unwind == attr.do_unwind, + fpu_preserved == attr.preserve_fpu), + fn_( fn), + stack_( base_type::callee_->fc_stack), + stack_alloc_( stack_alloc), + alloc_( alloc) + { enter_( arg); } + + ~coroutine_object() + { + if ( ! this->is_complete() && this->force_unwind() ) unwind_stack_(); + stack_alloc_.deallocate( stack_.sp, stack_.size); + } + + void run( context::fcontext_t * callee) + { + Caller c( callee, false, this->preserve_fpu(), alloc_); + run_( c); + } + + void run( context::fcontext_t * callee, typename detail::param< arg_type >::type arg) + { + Caller c( callee, false, this->preserve_fpu(), alloc_); + c.impl_->result_ = arg; + run_( c); + } + + void deallocate_object() + { destroy_( alloc_, this); } +}; diff --git a/include/boost/coroutine/detail/coroutine_object_void_0.ipp b/include/boost/coroutine/detail/coroutine_object_void_0.ipp new file mode 100644 index 0000000..1858d44 --- /dev/null +++ b/include/boost/coroutine/detail/coroutine_object_void_0.ipp @@ -0,0 +1,388 @@ + +// 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) + +template< + typename Signature, + typename Fn, typename StackAllocator, typename Allocator, + typename Caller +> +class coroutine_object< Signature, Fn, StackAllocator, Allocator, Caller, void, 0 > : + public coroutine_base< Signature > +{ +public: + typedef typename Allocator::template rebind< + coroutine_object< + Signature, Fn, StackAllocator, Allocator, Caller, void, 0 + > + >::other allocator_t; + +private: + typedef coroutine_base< Signature > base_type; + + Fn fn_; + context::stack_t stack_; + StackAllocator stack_alloc_; + allocator_t alloc_; + + static void destroy_( allocator_t & alloc, coroutine_object * p) + { + alloc.destroy( p); + alloc.deallocate( p, 1); + } + + coroutine_object( coroutine_object const&); + coroutine_object & operator=( coroutine_object const&); + + void enter_() + { + holder< coroutine_object * > hldr_to( & this->caller_, this); + holder< void > * hldr_from( + reinterpret_cast< holder< void > * >( context::jump_fcontext( + hldr_to.ctx, this->callee_, + reinterpret_cast< intptr_t >( & hldr_to), + this->preserve_fpu() ) ) ); + this->callee_ = hldr_from->ctx; + if ( this->except_) rethrow_exception( this->except_); + } + + void unwind_stack_() BOOST_NOEXCEPT + { + BOOST_ASSERT( ! this->is_complete() ); + + this->flags_ |= flag_unwind_stack; + holder< void > hldr( & this->caller_, true); + context::jump_fcontext( + hldr.ctx, this->callee_, + reinterpret_cast< intptr_t >( & hldr), + this->preserve_fpu() ); + this->flags_ &= ~flag_unwind_stack; + + BOOST_ASSERT( this->is_complete() ); + } + +public: +#ifndef BOOST_NO_RVALUE_REFERENCES + coroutine_object( BOOST_RV_REF( Fn) fn, attributes const& attr, + StackAllocator const& stack_alloc, + allocator_t const& alloc) : + base_type( + context::make_fcontext( + stack_alloc.allocate( attr.size), attr.size, + trampoline1< coroutine_object >), + stack_unwind == attr.do_unwind, + fpu_preserved == attr.preserve_fpu), + fn_( forward< Fn >( fn) ), + stack_( base_type::callee_->fc_stack), + stack_alloc_( stack_alloc), + alloc_( alloc) + { enter_(); } +#else + coroutine_object( Fn fn, attributes const& attr, + StackAllocator const& stack_alloc, + allocator_t const& alloc) : + base_type( + context::make_fcontext( + stack_alloc.allocate( attr.size), attr.size, + trampoline1< coroutine_object >), + stack_unwind == attr.do_unwind, + fpu_preserved == attr.preserve_fpu), + fn_( fn), + stack_( base_type::callee_->fc_stack), + stack_alloc_( stack_alloc), + alloc_( alloc) + { enter_(); } + + coroutine_object( BOOST_RV_REF( Fn) fn, attributes const& attr, + StackAllocator const& stack_alloc, + allocator_t const& alloc) : + base_type( + context::make_fcontext( + stack_alloc.allocate( attr.size), attr.size, + trampoline1< coroutine_object >), + stack_unwind == attr.do_unwind, + fpu_preserved == attr.preserve_fpu), + fn_( fn), + stack_( base_type::callee_->fc_stack), + stack_alloc_( stack_alloc), + alloc_( alloc) + { enter_(); } +#endif + + ~coroutine_object() + { + if ( ! this->is_complete() && this->force_unwind() ) unwind_stack_(); + stack_alloc_.deallocate( stack_.sp, stack_.size); + } + + void run( context::fcontext_t * callee) + { + Caller c( callee, false, this->preserve_fpu(), alloc_); + context::fcontext_t caller; + try + { + fn_( c); + this->flags_ |= flag_complete; + callee = c.impl_->callee_; + BOOST_ASSERT( callee); + holder< void > hldr_to( & caller); + context::jump_fcontext( + hldr_to.ctx, callee, + reinterpret_cast< intptr_t >( & hldr_to), + this->preserve_fpu() ); + BOOST_ASSERT_MSG( false, "coroutine is complete"); + } + catch ( forced_unwind const&) + {} + catch (...) + { this->except_ = current_exception(); } + + this->flags_ |= flag_complete; + callee = c.impl_->callee_; + BOOST_ASSERT( callee); + context::jump_fcontext( + & caller, callee, + reinterpret_cast< intptr_t >( & caller), + this->preserve_fpu() ); + BOOST_ASSERT_MSG( false, "coroutine is complete"); + } + + void deallocate_object() + { destroy_( alloc_, this); } +}; + +template< + typename Signature, + typename Fn, typename StackAllocator, typename Allocator, + typename Caller +> +class coroutine_object< Signature, reference_wrapper< Fn >, StackAllocator, Allocator, Caller, void, 0 > : + public coroutine_base< Signature > +{ +public: + typedef typename Allocator::template rebind< + coroutine_object< + Signature, Fn, StackAllocator, Allocator, Caller, void, 0 + > + >::other allocator_t; + +private: + typedef coroutine_base< Signature > base_type; + + Fn fn_; + context::stack_t stack_; + StackAllocator stack_alloc_; + allocator_t alloc_; + + static void destroy_( allocator_t & alloc, coroutine_object * p) + { + alloc.destroy( p); + alloc.deallocate( p, 1); + } + + coroutine_object( coroutine_object const&); + coroutine_object & operator=( coroutine_object const&); + + void enter_() + { + holder< coroutine_object * > hldr_to( & this->caller_, this); + holder< void > * hldr_from( + reinterpret_cast< holder< void > * >( context::jump_fcontext( + hldr_to.ctx, this->callee_, + reinterpret_cast< intptr_t >( & hldr_to), + this->preserve_fpu() ) ) ); + this->callee_ = hldr_from->ctx; + if ( this->except_) rethrow_exception( this->except_); + } + + void unwind_stack_() BOOST_NOEXCEPT + { + BOOST_ASSERT( ! this->is_complete() ); + + this->flags_ |= flag_unwind_stack; + holder< void > hldr( & this->caller_, true); + context::jump_fcontext( + hldr.ctx, this->callee_, + reinterpret_cast< intptr_t >( & hldr), + this->preserve_fpu() ); + this->flags_ &= ~flag_unwind_stack; + + BOOST_ASSERT( this->is_complete() ); + } + +public: + coroutine_object( reference_wrapper< Fn > fn, attributes const& attr, + StackAllocator const& stack_alloc, + allocator_t const& alloc) : + base_type( + context::make_fcontext( + stack_alloc.allocate( attr.size), attr.size, + trampoline1< coroutine_object >), + stack_unwind == attr.do_unwind, + fpu_preserved == attr.preserve_fpu), + fn_( fn), + stack_( base_type::callee_->fc_stack), + stack_alloc_( stack_alloc), + alloc_( alloc) + { enter_(); } + + ~coroutine_object() + { + if ( ! this->is_complete() && this->force_unwind() ) unwind_stack_(); + stack_alloc_.deallocate( stack_.sp, stack_.size); + } + + void run( context::fcontext_t * callee) + { + Caller c( callee, false, this->preserve_fpu(), alloc_); + context::fcontext_t caller; + try + { + fn_( c); + this->flags_ |= flag_complete; + callee = c.impl_->callee_; + BOOST_ASSERT( callee); + holder< void > hldr_to( & caller); + context::jump_fcontext( + hldr_to.ctx, callee, + reinterpret_cast< intptr_t >( & hldr_to), + this->preserve_fpu() ); + BOOST_ASSERT_MSG( false, "coroutine is complete"); + } + catch ( forced_unwind const&) + {} + catch (...) + { this->except_ = current_exception(); } + + this->flags_ |= flag_complete; + callee = c.impl_->callee_; + BOOST_ASSERT( callee); + context::jump_fcontext( + & caller, callee, + reinterpret_cast< intptr_t >( & caller), + this->preserve_fpu() ); + BOOST_ASSERT_MSG( false, "coroutine is complete"); + } + + void deallocate_object() + { destroy_( alloc_, this); } +}; + +template< + typename Signature, + typename Fn, typename StackAllocator, typename Allocator, + typename Caller +> +class coroutine_object< Signature, const reference_wrapper< Fn >, StackAllocator, Allocator, Caller, void, 0 > : + public coroutine_base< Signature > +{ +public: + typedef typename Allocator::template rebind< + coroutine_object< + Signature, Fn, StackAllocator, Allocator, Caller, void, 0 + > + >::other allocator_t; + +private: + typedef coroutine_base< Signature > base_type; + + Fn fn_; + context::stack_t stack_; + StackAllocator stack_alloc_; + allocator_t alloc_; + + static void destroy_( allocator_t & alloc, coroutine_object * p) + { + alloc.destroy( p); + alloc.deallocate( p, 1); + } + + coroutine_object( coroutine_object const&); + coroutine_object & operator=( coroutine_object const&); + + void enter_() + { + holder< coroutine_object * > hldr_to( & this->caller_, this); + holder< void > * hldr_from( + reinterpret_cast< holder< void > * >( context::jump_fcontext( + hldr_to.ctx, this->callee_, + reinterpret_cast< intptr_t >( & hldr_to), + this->preserve_fpu() ) ) ); + this->callee_ = hldr_from->ctx; + if ( this->except_) rethrow_exception( this->except_); + } + + void unwind_stack_() BOOST_NOEXCEPT + { + BOOST_ASSERT( ! this->is_complete() ); + + this->flags_ |= flag_unwind_stack; + holder< void > hldr( & this->caller_, true); + context::jump_fcontext( + hldr.ctx, this->callee_, + reinterpret_cast< intptr_t >( & hldr), + this->preserve_fpu() ); + this->flags_ &= ~flag_unwind_stack; + + BOOST_ASSERT( this->is_complete() ); + } + +public: + coroutine_object( const reference_wrapper< Fn > fn, attributes const& attr, + StackAllocator const& stack_alloc, + allocator_t const& alloc) : + base_type( + context::make_fcontext( + stack_alloc.allocate( attr.size), attr.size, + trampoline1< coroutine_object >), + stack_unwind == attr.do_unwind, + fpu_preserved == attr.preserve_fpu), + fn_( fn), + stack_( base_type::callee_->fc_stack), + stack_alloc_( stack_alloc), + alloc_( alloc) + { enter_(); } + + ~coroutine_object() + { + if ( ! this->is_complete() && this->force_unwind() ) unwind_stack_(); + stack_alloc_.deallocate( stack_.sp, stack_.size); + } + + void run( context::fcontext_t * callee) + { + Caller c( callee, false, this->preserve_fpu(), alloc_); + context::fcontext_t caller; + try + { + fn_( c); + this->flags_ |= flag_complete; + callee = c.impl_->callee_; + BOOST_ASSERT( callee); + holder< void > hldr_to( & caller); + context::jump_fcontext( + hldr_to.ctx, callee, + reinterpret_cast< intptr_t >( & hldr_to), + this->preserve_fpu() ); + BOOST_ASSERT_MSG( false, "coroutine is complete"); + } + catch ( forced_unwind const&) + {} + catch (...) + { this->except_ = current_exception(); } + + this->flags_ |= flag_complete; + callee = c.impl_->callee_; + BOOST_ASSERT( callee); + context::jump_fcontext( + & caller, callee, + reinterpret_cast< intptr_t >( & caller), + this->preserve_fpu() ); + BOOST_ASSERT_MSG( false, "coroutine is complete"); + } + + void deallocate_object() + { destroy_( alloc_, this); } +}; diff --git a/include/boost/coroutine/detail/coroutine_object_void_1.ipp b/include/boost/coroutine/detail/coroutine_object_void_1.ipp new file mode 100644 index 0000000..bf4b494 --- /dev/null +++ b/include/boost/coroutine/detail/coroutine_object_void_1.ipp @@ -0,0 +1,546 @@ + +// 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) + +template< + typename Signature, + typename Fn, typename StackAllocator, typename Allocator, + typename Caller +> +class coroutine_object< Signature, Fn, StackAllocator, Allocator, Caller, void, 1 > : + public coroutine_base< Signature > +{ +public: + typedef typename Allocator::template rebind< + coroutine_object< + Signature, Fn, StackAllocator, Allocator, Caller, void, 1 + > + >::other allocator_t; + typedef typename arg< Signature >::type arg_type; + +private: + typedef coroutine_base< Signature > base_type; + + Fn fn_; + context::stack_t stack_; + StackAllocator stack_alloc_; + allocator_t alloc_; + + static void destroy_( allocator_t & alloc, coroutine_object * p) + { + alloc.destroy( p); + alloc.deallocate( p, 1); + } + + coroutine_object( coroutine_object &); + coroutine_object & operator=( coroutine_object const&); + + void enter_() + { + holder< coroutine_object * > hldr_to( & this->caller_, this); + holder< void > * hldr_from( + reinterpret_cast< holder< void > * >( context::jump_fcontext( + hldr_to.ctx, this->callee_, + reinterpret_cast< intptr_t >( & hldr_to), + this->preserve_fpu() ) ) ); + this->callee_ = hldr_from->ctx; + if ( this->except_) rethrow_exception( this->except_); + } + + void enter_( typename detail::param< arg_type >::type arg) + { + holder< tuple< coroutine_object *, typename detail::param< arg_type >::type > > hldr_to( + & this->caller_, tuple< coroutine_object *, typename detail::param< arg_type >::type >( this, arg) ); + holder< void > * hldr_from( + reinterpret_cast< holder< void > * >( context::jump_fcontext( + hldr_to.ctx, this->callee_, + reinterpret_cast< intptr_t >( & hldr_to), + this->preserve_fpu() ) ) ); + this->callee_ = hldr_from->ctx; + if ( this->except_) rethrow_exception( this->except_); + } + + void run_( Caller & c) + { + context::fcontext_t * callee( 0); + context::fcontext_t caller; + try + { + fn_( c); + this->flags_ |= flag_complete; + callee = c.impl_->callee_; + BOOST_ASSERT( callee); + holder< void > hldr( & caller); + context::jump_fcontext( + hldr.ctx, callee, + ( intptr_t) & hldr, + this->preserve_fpu() ); + BOOST_ASSERT_MSG( false, "coroutine is complete"); + } + catch ( forced_unwind const&) + {} + catch (...) + { this->except_ = current_exception(); } + + this->flags_ |= flag_complete; + callee = c.impl_->callee_; + BOOST_ASSERT( callee); + context::jump_fcontext( + & caller, callee, + reinterpret_cast< intptr_t >( & caller), + this->preserve_fpu() ); + BOOST_ASSERT_MSG( false, "coroutine is complete"); + } + + void unwind_stack_() BOOST_NOEXCEPT + { + BOOST_ASSERT( ! this->is_complete() ); + + this->flags_ |= flag_unwind_stack; + holder< arg_type > hldr( & this->caller_, true); + context::jump_fcontext( + hldr.ctx, this->callee_, + reinterpret_cast< intptr_t >( & hldr), + this->preserve_fpu() ); + this->flags_ &= ~flag_unwind_stack; + + BOOST_ASSERT( this->is_complete() ); + } + +public: +#ifndef BOOST_NO_RVALUE_REFERENCES + coroutine_object( BOOST_RV_REF( Fn) fn, attributes const& attr, + StackAllocator const& stack_alloc, + allocator_t const& alloc) : + base_type( + context::make_fcontext( + stack_alloc.allocate( attr.size), attr.size, + trampoline1< coroutine_object >), + stack_unwind == attr.do_unwind, + fpu_preserved == attr.preserve_fpu), + fn_( forward< Fn >( fn) ), + stack_( base_type::callee_->fc_stack), + stack_alloc_( stack_alloc), + alloc_( alloc) + { enter_(); } + + coroutine_object( BOOST_RV_REF( Fn) fn, typename detail::param< arg_type >::type arg, attributes const& attr, + StackAllocator const& stack_alloc, + allocator_t const& alloc) : + base_type( + context::make_fcontext( + stack_alloc.allocate( attr.size), attr.size, + trampoline2< coroutine_object, typename detail::param< arg_type >::type >), + stack_unwind == attr.do_unwind, + fpu_preserved == attr.preserve_fpu), + fn_( forward< Fn >( fn) ), + stack_( base_type::callee_->fc_stack), + stack_alloc_( stack_alloc), + alloc_( alloc) + { enter_( arg); } +#else + coroutine_object( Fn fn, attributes const& attr, + StackAllocator const& stack_alloc, + allocator_t const& alloc) : + base_type( + context::make_fcontext( + stack_alloc.allocate( attr.size), attr.size, + trampoline1< coroutine_object >), + stack_unwind == attr.do_unwind, + fpu_preserved == attr.preserve_fpu), + fn_( fn), + stack_( base_type::callee_->fc_stack), + stack_alloc_( stack_alloc), + alloc_( alloc) + { enter_(); } + + coroutine_object( Fn fn, typename detail::param< arg_type >::type arg, attributes const& attr, + StackAllocator const& stack_alloc, + allocator_t const& alloc) : + base_type( + context::make_fcontext( + stack_alloc.allocate( attr.size), attr.size, + trampoline2< coroutine_object, typename detail::param< arg_type >::type >), + stack_unwind == attr.do_unwind, + fpu_preserved == attr.preserve_fpu), + fn_( fn), + stack_( base_type::callee_->fc_stack), + stack_alloc_( stack_alloc), + alloc_( alloc) + { enter_( arg); } + + coroutine_object( BOOST_RV_REF( Fn) fn, attributes const& attr, + StackAllocator const& stack_alloc, + allocator_t const& alloc) : + base_type( + context::make_fcontext( + stack_alloc.allocate( attr.size), attr.size, + trampoline1< coroutine_object >), + stack_unwind == attr.do_unwind, + fpu_preserved == attr.preserve_fpu), + fn_( fn), + stack_( base_type::callee_->fc_stack), + stack_alloc_( stack_alloc), + alloc_( alloc) + { enter_(); } + + coroutine_object( BOOST_RV_REF( Fn) fn, typename detail::param< arg_type >::type arg, attributes const& attr, + StackAllocator const& stack_alloc, + allocator_t const& alloc) : + base_type( + context::make_fcontext( + stack_alloc.allocate( attr.size), attr.size, + trampoline2< coroutine_object, typename detail::param< arg_type >::type >), + stack_unwind == attr.do_unwind, + fpu_preserved == attr.preserve_fpu), + fn_( fn), + stack_( base_type::callee_->fc_stack), + stack_alloc_( stack_alloc), + alloc_( alloc) + { enter_( arg); } +#endif + + ~coroutine_object() + { + if ( ! this->is_complete() && this->force_unwind() ) unwind_stack_(); + stack_alloc_.deallocate( stack_.sp, stack_.size); + } + + void run( context::fcontext_t * callee) + { + Caller c( callee, false, this->preserve_fpu(), alloc_); + run_( c); + } + + void run( context::fcontext_t * callee, typename detail::param< arg_type >::type arg) + { + Caller c( callee, false, this->preserve_fpu(), alloc_); + c.impl_->result_ = arg; + run_( c); + } + + void deallocate_object() + { destroy_( alloc_, this); } +}; + +template< + typename Signature, + typename Fn, typename StackAllocator, typename Allocator, + typename Caller +> +class coroutine_object< Signature, reference_wrapper< Fn >, StackAllocator, Allocator, Caller, void, 1 > : + public coroutine_base< Signature > +{ +public: + typedef typename Allocator::template rebind< + coroutine_object< + Signature, Fn, StackAllocator, Allocator, Caller, void, 1 + > + >::other allocator_t; + typedef typename arg< Signature >::type arg_type; + +private: + typedef coroutine_base< Signature > base_type; + + Fn fn_; + context::stack_t stack_; + StackAllocator stack_alloc_; + allocator_t alloc_; + + static void destroy_( allocator_t & alloc, coroutine_object * p) + { + alloc.destroy( p); + alloc.deallocate( p, 1); + } + + coroutine_object( coroutine_object &); + coroutine_object & operator=( coroutine_object const&); + + void enter_() + { + holder< coroutine_object * > hldr_to( & this->caller_, this); + holder< void > * hldr_from( + reinterpret_cast< holder< void > * >( context::jump_fcontext( + hldr_to.ctx, this->callee_, + reinterpret_cast< intptr_t >( & hldr_to), + this->preserve_fpu() ) ) ); + this->callee_ = hldr_from->ctx; + if ( this->except_) rethrow_exception( this->except_); + } + + void enter_( typename detail::param< arg_type >::type arg) + { + holder< tuple< coroutine_object *, typename detail::param< arg_type >::type > > hldr_to( + & this->caller_, tuple< coroutine_object *, typename detail::param< arg_type >::type >( this, arg) ); + holder< void > * hldr_from( + reinterpret_cast< holder< void > * >( context::jump_fcontext( + hldr_to.ctx, this->callee_, + reinterpret_cast< intptr_t >( & hldr_to), + this->preserve_fpu() ) ) ); + this->callee_ = hldr_from->ctx; + if ( this->except_) rethrow_exception( this->except_); + } + + void run_( Caller & c) + { + context::fcontext_t * callee( 0); + context::fcontext_t caller; + try + { + fn_( c); + this->flags_ |= flag_complete; + callee = c.impl_->callee_; + BOOST_ASSERT( callee); + holder< void > hldr( & caller); + context::jump_fcontext( + hldr.ctx, callee, + ( intptr_t) & hldr, + this->preserve_fpu() ); + BOOST_ASSERT_MSG( false, "coroutine is complete"); + } + catch ( forced_unwind const&) + {} + catch (...) + { this->except_ = current_exception(); } + + this->flags_ |= flag_complete; + callee = c.impl_->callee_; + BOOST_ASSERT( callee); + context::jump_fcontext( + & caller, callee, + reinterpret_cast< intptr_t >( & caller), + this->preserve_fpu() ); + BOOST_ASSERT_MSG( false, "coroutine is complete"); + } + + void unwind_stack_() BOOST_NOEXCEPT + { + BOOST_ASSERT( ! this->is_complete() ); + + this->flags_ |= flag_unwind_stack; + holder< arg_type > hldr( & this->caller_, true); + context::jump_fcontext( + hldr.ctx, this->callee_, + reinterpret_cast< intptr_t >( & hldr), + this->preserve_fpu() ); + this->flags_ &= ~flag_unwind_stack; + + BOOST_ASSERT( this->is_complete() ); + } + +public: + coroutine_object( reference_wrapper< Fn > fn, attributes const& attr, + StackAllocator const& stack_alloc, + allocator_t const& alloc) : + base_type( + context::make_fcontext( + stack_alloc.allocate( attr.size), attr.size, + trampoline1< coroutine_object >), + stack_unwind == attr.do_unwind, + fpu_preserved == attr.preserve_fpu), + fn_( fn), + stack_( base_type::callee_->fc_stack), + stack_alloc_( stack_alloc), + alloc_( alloc) + { enter_(); } + + coroutine_object( reference_wrapper< Fn > fn, + typename detail::param< arg_type >::type arg, attributes const& attr, + StackAllocator const& stack_alloc, + allocator_t const& alloc) : + base_type( + context::make_fcontext( + stack_alloc.allocate( attr.size), attr.size, + trampoline2< coroutine_object, typename detail::param< arg_type >::type >), + stack_unwind == attr.do_unwind, + fpu_preserved == attr.preserve_fpu), + fn_( fn), + stack_( base_type::callee_->fc_stack), + stack_alloc_( stack_alloc), + alloc_( alloc) + { enter_( arg); } + + ~coroutine_object() + { + if ( ! this->is_complete() && this->force_unwind() ) unwind_stack_(); + stack_alloc_.deallocate( stack_.sp, stack_.size); + } + + void run( context::fcontext_t * callee) + { + Caller c( callee, false, this->preserve_fpu(), alloc_); + run_( c); + } + + void run( context::fcontext_t * callee, typename detail::param< arg_type >::type arg) + { + Caller c( callee, false, this->preserve_fpu(), alloc_); + c.impl_->result_ = arg; + run_( c); + } + + void deallocate_object() + { destroy_( alloc_, this); } +}; + +template< + typename Signature, + typename Fn, typename StackAllocator, typename Allocator, + typename Caller +> +class coroutine_object< Signature, const reference_wrapper< Fn >, StackAllocator, Allocator, Caller, void, 1 > : + public coroutine_base< Signature > +{ +public: + typedef typename Allocator::template rebind< + coroutine_object< + Signature, Fn, StackAllocator, Allocator, Caller, void, 1 + > + >::other allocator_t; + typedef typename arg< Signature >::type arg_type; + +private: + typedef coroutine_base< Signature > base_type; + + Fn fn_; + context::stack_t stack_; + StackAllocator stack_alloc_; + allocator_t alloc_; + + static void destroy_( allocator_t & alloc, coroutine_object * p) + { + alloc.destroy( p); + alloc.deallocate( p, 1); + } + + coroutine_object( coroutine_object &); + coroutine_object & operator=( coroutine_object const&); + + void enter_() + { + holder< coroutine_object * > hldr_to( & this->caller_, this); + holder< void > * hldr_from( + reinterpret_cast< holder< void > * >( context::jump_fcontext( + hldr_to.ctx, this->callee_, + reinterpret_cast< intptr_t >( & hldr_to), + this->preserve_fpu() ) ) ); + this->callee_ = hldr_from->ctx; + if ( this->except_) rethrow_exception( this->except_); + } + + void enter_( typename detail::param< arg_type >::type arg) + { + holder< tuple< coroutine_object *, typename detail::param< arg_type >::type > > hldr_to( + & this->caller_, tuple< coroutine_object *, typename detail::param< arg_type >::type >( this, arg) ); + holder< void > * hldr_from( + reinterpret_cast< holder< void > * >( context::jump_fcontext( + hldr_to.ctx, this->callee_, + reinterpret_cast< intptr_t >( & hldr_to), + this->preserve_fpu() ) ) ); + this->callee_ = hldr_from->ctx; + if ( this->except_) rethrow_exception( this->except_); + } + + void run_( Caller & c) + { + context::fcontext_t * callee( 0); + context::fcontext_t caller; + try + { + fn_( c); + this->flags_ |= flag_complete; + callee = c.impl_->callee_; + BOOST_ASSERT( callee); + holder< void > hldr( & caller); + context::jump_fcontext( + hldr.ctx, callee, + ( intptr_t) & hldr, + this->preserve_fpu() ); + BOOST_ASSERT_MSG( false, "coroutine is complete"); + } + catch ( forced_unwind const&) + {} + catch (...) + { this->except_ = current_exception(); } + + this->flags_ |= flag_complete; + callee = c.impl_->callee_; + BOOST_ASSERT( callee); + context::jump_fcontext( + & caller, callee, + reinterpret_cast< intptr_t >( & caller), + this->preserve_fpu() ); + BOOST_ASSERT_MSG( false, "coroutine is complete"); + } + + void unwind_stack_() BOOST_NOEXCEPT + { + BOOST_ASSERT( ! this->is_complete() ); + + this->flags_ |= flag_unwind_stack; + holder< arg_type > hldr( & this->caller_, true); + context::jump_fcontext( + hldr.ctx, this->callee_, + reinterpret_cast< intptr_t >( & hldr), + this->preserve_fpu() ); + this->flags_ &= ~flag_unwind_stack; + + BOOST_ASSERT( this->is_complete() ); + } + +public: + coroutine_object( const reference_wrapper< Fn > fn, attributes const& attr, + StackAllocator const& stack_alloc, + allocator_t const& alloc) : + base_type( + context::make_fcontext( + stack_alloc.allocate( attr.size), attr.size, + trampoline1< coroutine_object >), + stack_unwind == attr.do_unwind, + fpu_preserved == attr.preserve_fpu), + fn_( fn), + stack_( base_type::callee_->fc_stack), + stack_alloc_( stack_alloc), + alloc_( alloc) + { enter_(); } + + coroutine_object( const reference_wrapper< Fn > fn, + typename detail::param< arg_type >::type arg, attributes const& attr, + StackAllocator const& stack_alloc, + allocator_t const& alloc) : + base_type( + context::make_fcontext( + stack_alloc.allocate( attr.size), attr.size, + trampoline2< coroutine_object, typename detail::param< arg_type >::type >), + stack_unwind == attr.do_unwind, + fpu_preserved == attr.preserve_fpu), + fn_( fn), + stack_( base_type::callee_->fc_stack), + stack_alloc_( stack_alloc), + alloc_( alloc) + { enter_( arg); } + + ~coroutine_object() + { + if ( ! this->is_complete() && this->force_unwind() ) unwind_stack_(); + stack_alloc_.deallocate( stack_.sp, stack_.size); + } + + void run( context::fcontext_t * callee) + { + Caller c( callee, false, this->preserve_fpu(), alloc_); + run_( c); + } + + void run( context::fcontext_t * callee, typename detail::param< arg_type >::type arg) + { + Caller c( callee, false, this->preserve_fpu(), alloc_); + c.impl_->result_ = arg; + run_( c); + } + + void deallocate_object() + { destroy_( alloc_, this); } +}; diff --git a/include/boost/coroutine/detail/coroutine_object_void_arity.ipp b/include/boost/coroutine/detail/coroutine_object_void_arity.ipp new file mode 100644 index 0000000..528fe8f --- /dev/null +++ b/include/boost/coroutine/detail/coroutine_object_void_arity.ipp @@ -0,0 +1,559 @@ + +// 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) + +template< + typename Signature, + typename Fn, typename StackAllocator, typename Allocator, + typename Caller, + int arity +> +class coroutine_object< Signature, Fn, StackAllocator, Allocator, Caller, void, arity > : + public coroutine_base< Signature > +{ +public: + typedef typename Allocator::template rebind< + coroutine_object< + Signature, Fn, StackAllocator, Allocator, Caller, void, arity + > + >::other allocator_t; + typedef typename arg< Signature >::type arg_type; + +private: + typedef coroutine_base< Signature > base_type; + + Fn fn_; + context::stack_t stack_; + StackAllocator stack_alloc_; + allocator_t alloc_; + + static void destroy_( allocator_t & alloc, coroutine_object * p) + { + alloc.destroy( p); + alloc.deallocate( p, 1); + } + + coroutine_object( coroutine_object &); + coroutine_object & operator=( coroutine_object const&); + + void enter_() + { + holder< coroutine_object * > hldr_to( & this->caller_, this); + holder< void > * hldr_from( + reinterpret_cast< holder< void > * >( context::jump_fcontext( + hldr_to.ctx, this->callee_, + reinterpret_cast< intptr_t >( & hldr_to), + this->preserve_fpu() ) ) ); + this->callee_ = hldr_from->ctx; + if ( this->except_) rethrow_exception( this->except_); + } + + void enter_( typename detail::param< arg_type >::type arg) + { + holder< + tuple< coroutine_object *, + typename detail::param< arg_type >::type > + > hldr_to( + & this->caller_, tuple< coroutine_object *, + typename detail::param< arg_type >::type >( this, arg) ); + holder< void > * hldr_from( + reinterpret_cast< holder< void > * >( context::jump_fcontext( + hldr_to.ctx, this->callee_, + reinterpret_cast< intptr_t >( & hldr_to), + this->preserve_fpu() ) ) ); + this->callee_ = hldr_from->ctx; + if ( this->except_) rethrow_exception( this->except_); + } + + void run_( Caller & c) + { + context::fcontext_t * callee( 0); + context::fcontext_t caller; + try + { + fn_( c); + this->flags_ |= flag_complete; + callee = c.impl_->callee_; + BOOST_ASSERT( callee); + holder< void > hldr( & caller); + context::jump_fcontext( + hldr.ctx, callee, + ( intptr_t) & hldr, + this->preserve_fpu() ); + BOOST_ASSERT_MSG( false, "coroutine is complete"); + } + catch ( forced_unwind const&) + {} + catch (...) + { this->except_ = current_exception(); } + + this->flags_ |= flag_complete; + callee = c.impl_->callee_; + BOOST_ASSERT( callee); + context::jump_fcontext( + & caller, callee, + reinterpret_cast< intptr_t >( & caller), + this->preserve_fpu() ); + BOOST_ASSERT_MSG( false, "coroutine is complete"); + } + + void unwind_stack_() BOOST_NOEXCEPT + { + BOOST_ASSERT( ! this->is_complete() ); + + this->flags_ |= flag_unwind_stack; + holder< arg_type > hldr( & this->caller_, true); + context::jump_fcontext( + hldr.ctx, this->callee_, + reinterpret_cast< intptr_t >( & hldr), + this->preserve_fpu() ); + this->flags_ &= ~flag_unwind_stack; + + BOOST_ASSERT( this->is_complete() ); + } + +public: +#ifndef BOOST_NO_RVALUE_REFERENCES + coroutine_object( BOOST_RV_REF( Fn) fn, attributes const& attr, + StackAllocator const& stack_alloc, + allocator_t const& alloc) : + base_type( + context::make_fcontext( + stack_alloc.allocate( attr.size), attr.size, + trampoline1< coroutine_object >), + stack_unwind == attr.do_unwind, + fpu_preserved == attr.preserve_fpu), + fn_( forward< Fn >( fn) ), + stack_( base_type::callee_->fc_stack), + stack_alloc_( stack_alloc), + alloc_( alloc) + { enter_(); } + + coroutine_object( BOOST_RV_REF( Fn) fn, typename detail::param< arg_type >::type arg, attributes const& attr, + StackAllocator const& stack_alloc, + allocator_t const& alloc) : + base_type( + context::make_fcontext( + stack_alloc.allocate( attr.size), attr.size, + trampoline2< coroutine_object, typename detail::param< arg_type >::type >), + stack_unwind == attr.do_unwind, + fpu_preserved == attr.preserve_fpu), + fn_( forward< Fn >( fn) ), + stack_( base_type::callee_->fc_stack), + stack_alloc_( stack_alloc), + alloc_( alloc) + { enter_( arg); } +#else + coroutine_object( Fn fn, attributes const& attr, + StackAllocator const& stack_alloc, + allocator_t const& alloc) : + base_type( + context::make_fcontext( + stack_alloc.allocate( attr.size), attr.size, + trampoline1< coroutine_object >), + stack_unwind == attr.do_unwind, + fpu_preserved == attr.preserve_fpu), + fn_( fn), + stack_( base_type::callee_->fc_stack), + stack_alloc_( stack_alloc), + alloc_( alloc) + { enter_(); } + + coroutine_object( Fn fn, typename detail::param< arg_type >::type arg, attributes const& attr, + StackAllocator const& stack_alloc, + allocator_t const& alloc) : + base_type( + context::make_fcontext( + stack_alloc.allocate( attr.size), attr.size, + trampoline2< coroutine_object, typename detail::param< arg_type >::type >), + stack_unwind == attr.do_unwind, + fpu_preserved == attr.preserve_fpu), + fn_( fn), + stack_( base_type::callee_->fc_stack), + stack_alloc_( stack_alloc), + alloc_( alloc) + { enter_( arg); } + + coroutine_object( BOOST_RV_REF( Fn) fn, attributes const& attr, + StackAllocator const& stack_alloc, + allocator_t const& alloc) : + base_type( + context::make_fcontext( + stack_alloc.allocate( attr.size), attr.size, + trampoline1< coroutine_object >), + stack_unwind == attr.do_unwind, + fpu_preserved == attr.preserve_fpu), + fn_( fn), + stack_( base_type::callee_->fc_stack), + stack_alloc_( stack_alloc), + alloc_( alloc) + { enter_(); } + + coroutine_object( BOOST_RV_REF( Fn) fn, typename detail::param< arg_type >::type arg, attributes const& attr, + StackAllocator const& stack_alloc, + allocator_t const& alloc) : + base_type( + context::make_fcontext( + stack_alloc.allocate( attr.size), attr.size, + trampoline2< coroutine_object, typename detail::param< arg_type >::type >), + stack_unwind == attr.do_unwind, + fpu_preserved == attr.preserve_fpu), + fn_( fn), + stack_( base_type::callee_->fc_stack), + stack_alloc_( stack_alloc), + alloc_( alloc) + { enter_( arg); } +#endif + + ~coroutine_object() + { + if ( ! this->is_complete() && this->force_unwind() ) unwind_stack_(); + stack_alloc_.deallocate( stack_.sp, stack_.size); + } + + void run( context::fcontext_t * callee) + { + Caller c( callee, false, this->preserve_fpu(), alloc_); + run_( c); + } + + void run( context::fcontext_t * callee, typename detail::param< arg_type >::type arg) + { + Caller c( callee, false, this->preserve_fpu(), alloc_); + c.impl_->result_ = arg; + run_( c); + } + + void deallocate_object() + { destroy_( alloc_, this); } +}; + +template< + typename Signature, + typename Fn, typename StackAllocator, typename Allocator, + typename Caller, + int arity +> +class coroutine_object< Signature, reference_wrapper< Fn >, StackAllocator, Allocator, Caller, void, arity > : + public coroutine_base< Signature > +{ +public: + typedef typename Allocator::template rebind< + coroutine_object< + Signature, Fn, StackAllocator, Allocator, Caller, void, arity + > + >::other allocator_t; + typedef typename arg< Signature >::type arg_type; + +private: + typedef coroutine_base< Signature > base_type; + + Fn fn_; + context::stack_t stack_; + StackAllocator stack_alloc_; + allocator_t alloc_; + + static void destroy_( allocator_t & alloc, coroutine_object * p) + { + alloc.destroy( p); + alloc.deallocate( p, 1); + } + + coroutine_object( coroutine_object &); + coroutine_object & operator=( coroutine_object const&); + + void enter_() + { + holder< coroutine_object * > hldr_to( & this->caller_, this); + holder< void > * hldr_from( + reinterpret_cast< holder< void > * >( context::jump_fcontext( + hldr_to.ctx, this->callee_, + reinterpret_cast< intptr_t >( & hldr_to), + this->preserve_fpu() ) ) ); + this->callee_ = hldr_from->ctx; + if ( this->except_) rethrow_exception( this->except_); + } + + void enter_( typename detail::param< arg_type >::type arg) + { + holder< + tuple< coroutine_object *, + typename detail::param< arg_type >::type > + > hldr_to( + & this->caller_, tuple< coroutine_object *, + typename detail::param< arg_type >::type >( this, arg) ); + holder< void > * hldr_from( + reinterpret_cast< holder< void > * >( context::jump_fcontext( + hldr_to.ctx, this->callee_, + reinterpret_cast< intptr_t >( & hldr_to), + this->preserve_fpu() ) ) ); + this->callee_ = hldr_from->ctx; + if ( this->except_) rethrow_exception( this->except_); + } + + void run_( Caller & c) + { + context::fcontext_t * callee( 0); + context::fcontext_t caller; + try + { + fn_( c); + this->flags_ |= flag_complete; + callee = c.impl_->callee_; + BOOST_ASSERT( callee); + holder< void > hldr( & caller); + context::jump_fcontext( + hldr.ctx, callee, + ( intptr_t) & hldr, + this->preserve_fpu() ); + BOOST_ASSERT_MSG( false, "coroutine is complete"); + } + catch ( forced_unwind const&) + {} + catch (...) + { this->except_ = current_exception(); } + + this->flags_ |= flag_complete; + callee = c.impl_->callee_; + BOOST_ASSERT( callee); + context::jump_fcontext( + & caller, callee, + reinterpret_cast< intptr_t >( & caller), + this->preserve_fpu() ); + BOOST_ASSERT_MSG( false, "coroutine is complete"); + } + + void unwind_stack_() BOOST_NOEXCEPT + { + BOOST_ASSERT( ! this->is_complete() ); + + this->flags_ |= flag_unwind_stack; + holder< arg_type > hldr( & this->caller_, true); + context::jump_fcontext( + hldr.ctx, this->callee_, + reinterpret_cast< intptr_t >( & hldr), + this->preserve_fpu() ); + this->flags_ &= ~flag_unwind_stack; + + BOOST_ASSERT( this->is_complete() ); + } + +public: + coroutine_object( reference_wrapper< Fn > fn, attributes const& attr, + StackAllocator const& stack_alloc, + allocator_t const& alloc) : + base_type( + context::make_fcontext( + stack_alloc.allocate( attr.size), attr.size, + trampoline1< coroutine_object >), + stack_unwind == attr.do_unwind, + fpu_preserved == attr.preserve_fpu), + fn_( fn), + stack_( base_type::callee_->fc_stack), + stack_alloc_( stack_alloc), + alloc_( alloc) + { enter_(); } + + coroutine_object( reference_wrapper< Fn > fn, typename detail::param< arg_type >::type arg, attributes const& attr, + StackAllocator const& stack_alloc, + allocator_t const& alloc) : + base_type( + context::make_fcontext( + stack_alloc.allocate( attr.size), attr.size, + trampoline2< coroutine_object, typename detail::param< arg_type >::type >), + stack_unwind == attr.do_unwind, + fpu_preserved == attr.preserve_fpu), + fn_( fn), + stack_( base_type::callee_->fc_stack), + stack_alloc_( stack_alloc), + alloc_( alloc) + { enter_( arg); } + + ~coroutine_object() + { + if ( ! this->is_complete() && this->force_unwind() ) unwind_stack_(); + stack_alloc_.deallocate( stack_.sp, stack_.size); + } + + void run( context::fcontext_t * callee) + { + Caller c( callee, false, this->preserve_fpu(), alloc_); + run_( c); + } + + void run( context::fcontext_t * callee, typename detail::param< arg_type >::type arg) + { + Caller c( callee, false, this->preserve_fpu(), alloc_); + c.impl_->result_ = arg; + run_( c); + } + + void deallocate_object() + { destroy_( alloc_, this); } +}; + +template< + typename Signature, + typename Fn, typename StackAllocator, typename Allocator, + typename Caller, + int arity +> +class coroutine_object< Signature, const reference_wrapper< Fn >, StackAllocator, Allocator, Caller, void, arity > : + public coroutine_base< Signature > +{ +public: + typedef typename Allocator::template rebind< + coroutine_object< + Signature, Fn, StackAllocator, Allocator, Caller, void, arity + > + >::other allocator_t; + typedef typename arg< Signature >::type arg_type; + +private: + typedef coroutine_base< Signature > base_type; + + Fn fn_; + context::stack_t stack_; + StackAllocator stack_alloc_; + allocator_t alloc_; + + static void destroy_( allocator_t & alloc, coroutine_object * p) + { + alloc.destroy( p); + alloc.deallocate( p, 1); + } + + coroutine_object( coroutine_object &); + coroutine_object & operator=( coroutine_object const&); + + void enter_() + { + holder< coroutine_object * > hldr_to( & this->caller_, this); + holder< void > * hldr_from( + reinterpret_cast< holder< void > * >( context::jump_fcontext( + hldr_to.ctx, this->callee_, + reinterpret_cast< intptr_t >( & hldr_to), + this->preserve_fpu() ) ) ); + this->callee_ = hldr_from->ctx; + if ( this->except_) rethrow_exception( this->except_); + } + + void enter_( typename detail::param< arg_type >::type arg) + { + holder< + tuple< coroutine_object *, + typename detail::param< arg_type >::type > + > hldr_to( + & this->caller_, tuple< coroutine_object *, + typename detail::param< arg_type >::type >( this, arg) ); + holder< void > * hldr_from( + reinterpret_cast< holder< void > * >( context::jump_fcontext( + hldr_to.ctx, this->callee_, + reinterpret_cast< intptr_t >( & hldr_to), + this->preserve_fpu() ) ) ); + this->callee_ = hldr_from->ctx; + if ( this->except_) rethrow_exception( this->except_); + } + + void run_( Caller & c) + { + context::fcontext_t * callee( 0); + context::fcontext_t caller; + try + { + fn_( c); + this->flags_ |= flag_complete; + callee = c.impl_->callee_; + BOOST_ASSERT( callee); + holder< void > hldr( & caller); + context::jump_fcontext( + hldr.ctx, callee, + ( intptr_t) & hldr, + this->preserve_fpu() ); + BOOST_ASSERT_MSG( false, "coroutine is complete"); + } + catch ( forced_unwind const&) + {} + catch (...) + { this->except_ = current_exception(); } + + this->flags_ |= flag_complete; + callee = c.impl_->callee_; + BOOST_ASSERT( callee); + context::jump_fcontext( + & caller, callee, + reinterpret_cast< intptr_t >( & caller), + this->preserve_fpu() ); + BOOST_ASSERT_MSG( false, "coroutine is complete"); + } + + void unwind_stack_() BOOST_NOEXCEPT + { + BOOST_ASSERT( ! this->is_complete() ); + + this->flags_ |= flag_unwind_stack; + holder< arg_type > hldr( & this->caller_, true); + context::jump_fcontext( + hldr.ctx, this->callee_, + reinterpret_cast< intptr_t >( & hldr), + this->preserve_fpu() ); + this->flags_ &= ~flag_unwind_stack; + + BOOST_ASSERT( this->is_complete() ); + } + +public: + coroutine_object( const reference_wrapper< Fn > fn, attributes const& attr, + StackAllocator const& stack_alloc, + allocator_t const& alloc) : + base_type( + context::make_fcontext( + stack_alloc.allocate( attr.size), attr.size, + trampoline1< coroutine_object >), + stack_unwind == attr.do_unwind, + fpu_preserved == attr.preserve_fpu), + fn_( fn), + stack_( base_type::callee_->fc_stack), + stack_alloc_( stack_alloc), + alloc_( alloc) + { enter_(); } + + coroutine_object( const reference_wrapper< Fn > fn, typename detail::param< arg_type >::type arg, attributes const& attr, + StackAllocator const& stack_alloc, + allocator_t const& alloc) : + base_type( + context::make_fcontext( + stack_alloc.allocate( attr.size), attr.size, + trampoline2< coroutine_object, typename detail::param< arg_type >::type >), + stack_unwind == attr.do_unwind, + fpu_preserved == attr.preserve_fpu), + fn_( fn), + stack_( base_type::callee_->fc_stack), + stack_alloc_( stack_alloc), + alloc_( alloc) + { enter_( arg); } + + ~coroutine_object() + { + if ( ! this->is_complete() && this->force_unwind() ) unwind_stack_(); + stack_alloc_.deallocate( stack_.sp, stack_.size); + } + + void run( context::fcontext_t * callee) + { + Caller c( callee, false, this->preserve_fpu(), alloc_); + run_( c); + } + + void run( context::fcontext_t * callee, typename detail::param< arg_type >::type arg) + { + Caller c( callee, false, this->preserve_fpu(), alloc_); + c.impl_->result_ = arg; + run_( c); + } + + void deallocate_object() + { destroy_( alloc_, this); } +}; diff --git a/include/boost/coroutine/detail/coroutine_op.hpp b/include/boost/coroutine/detail/coroutine_op.hpp new file mode 100644 index 0000000..4bab16f --- /dev/null +++ b/include/boost/coroutine/detail/coroutine_op.hpp @@ -0,0 +1,312 @@ + +// 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_COROUTINES_DETAIL_COROUTINE_OP_H +#define BOOST_COROUTINES_DETAIL_COROUTINE_OP_H + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_PREFIX +#endif + +namespace boost { +namespace coroutines { +namespace detail { + +template< typename Signature, typename D, typename Result, int arity > +struct coroutine_op; + +template< typename Signature, typename D > +struct coroutine_op< Signature, D, void, 0 > +{ + D & operator()() + { + BOOST_ASSERT( static_cast< D * >( this)->impl_); + BOOST_ASSERT( ! static_cast< D * >( this)->impl_->is_complete() ); + + static_cast< D * >( this)->impl_->resume(); + + return * static_cast< D * >( this); + } +}; + +template< typename Signature, typename D, typename Result > +struct coroutine_op< Signature, D, Result, 0 > +{ + class iterator : public std::iterator< std::input_iterator_tag, typename remove_reference< Result >::type > + { + private: + D * dp_; + optional< Result > val_; + + void fetch_() + { + BOOST_ASSERT( dp_); + + if ( ! dp_->has_result() ) + { + dp_ = 0; + val_ = none; + return; + } + val_ = dp_->get(); + } + + void increment_() + { + BOOST_ASSERT( dp_); + BOOST_ASSERT( * dp_); + + ( * dp_)(); + fetch_(); + } + + public: + typedef typename std::iterator_traits< iterator >::pointer pointer_t; + typedef typename std::iterator_traits< iterator >::reference reference_t; + + iterator() : + dp_( 0), val_() + {} + + explicit iterator( D * dp) : + dp_( dp), val_() + { fetch_(); } + + iterator( iterator const& other) : + dp_( other.dp_), val_( other.val_) + {} + + iterator & operator=( iterator const& other) + { + if ( this == & other) return * this; + dp_ = other.dp_; + val_ = other.val_; + return * this; + } + + bool operator==( iterator const& other) + { return other.dp_ == dp_ && other.val_ == val_; } + + bool operator!=( iterator const& other) + { return other.dp_ != dp_ || other.val_ != val_; } + + iterator & operator++() + { + increment_(); + return * this; + } + + reference_t operator*() const + { return const_cast< optional< Result > & >( val_).get(); } + + pointer_t operator->() const + { return const_cast< optional< Result > & >( val_).get_ptr(); } + }; + + class const_iterator : public std::iterator< std::input_iterator_tag, typename remove_reference< const Result >::type > + { + private: + D * dp_; + optional< const Result > val_; + + void fetch_() + { + BOOST_ASSERT( dp_); + + if ( ! dp_->has_result() ) + { + dp_ = 0; + val_ = none; + return; + } + val_ = dp_->get(); + } + + void increment_() + { + BOOST_ASSERT( dp_); + BOOST_ASSERT( * dp_); + + ( * dp_)(); + fetch_(); + } + + public: + typedef typename std::iterator_traits< iterator >::pointer pointer_t; + typedef typename std::iterator_traits< iterator >::reference reference_t; + + const_iterator() : + dp_( 0), val_() + {} + + explicit const_iterator( D * dp) : + dp_( dp), val_() + { fetch_(); } + + const_iterator( const_iterator const& other) : + dp_( other.dp_), val_( other.val_) + {} + + const_iterator & operator=( const_iterator const& other) + { + if ( this == & other) return * this; + dp_ = other.dp_; + val_ = other.val_; + return * this; + } + + bool operator==( const_iterator const& other) + { return other.dp_ == dp_ && other.val_ == val_; } + + bool operator!=( const_iterator const& other) + { return other.dp_ != dp_ || other.val_ != val_; } + + const_iterator & operator++() + { + increment_(); + return * this; + } + + reference_t operator*() const + { return val_.get(); } + + pointer_t operator->() const + { return val_.get_ptr(); } + }; + + D & operator()() + { + BOOST_ASSERT( static_cast< D * >( this)->impl_); + BOOST_ASSERT( ! static_cast< D * >( this)->impl_->is_complete() ); + + static_cast< D * >( this)->impl_->resume(); + + return * static_cast< D * >( this); + } +}; + +template< typename Signature, typename D > +struct coroutine_op< Signature, D, void, 1 > +{ + typedef typename arg< Signature >::type arg_type; + + class iterator : public std::iterator< std::output_iterator_tag, void, void, void, void > + { + private: + D * dp_; + + public: + iterator() : + dp_( 0) + {} + + explicit iterator( D * dp) : + dp_( dp) + {} + + iterator & operator=( arg_type a1) + { + BOOST_ASSERT( dp_); + if ( ! ( * dp_)( a1) ) dp_ = 0; + return * this; + } + + bool operator==( iterator const& other) + { return other.dp_ == dp_; } + + bool operator!=( iterator const& other) + { return other.dp_ != dp_; } + + iterator & operator*() + { return * this; } + + iterator & operator++() + { return * this; } + }; + + struct const_iterator; + + D & operator()( arg_type a1) + { + BOOST_ASSERT( static_cast< D * >( this)->impl_); + BOOST_ASSERT( ! static_cast< D * >( this)->impl_->is_complete() ); + + static_cast< D * >( this)->impl_->resume( a1); + + return * static_cast< D * >( this); + } +}; + +template< typename Signature, typename D, typename Result > +struct coroutine_op< Signature, D, Result, 1 > +{ + typedef typename arg< Signature >::type arg_type; + + D & operator()( arg_type a1) + { + BOOST_ASSERT( static_cast< D * >( this)->impl_); + BOOST_ASSERT( ! static_cast< D * >( this)->impl_->is_complete() ); + + static_cast< D * >( this)->impl_->resume( a1); + + return * static_cast< D * >( this); + } +}; + +#define BOOST_COROUTINE_OP_COMMA(n) BOOST_PP_COMMA_IF(BOOST_PP_SUB(n,1)) +#define BOOST_COROUTINE_OP_VAL(z,n,unused) BOOST_COROUTINE_OP_COMMA(n) BOOST_PP_CAT(a,n) +#define BOOST_COROUTINE_OP_VALS(n) BOOST_PP_REPEAT_FROM_TO(1,BOOST_PP_ADD(n,1),BOOST_COROUTINE_OP_VAL,~) +#define BOOST_COROUTINE_OP_ARG_TYPE(n) \ + typename function_traits< Signature >::BOOST_PP_CAT(BOOST_PP_CAT(arg,n),_type) +#define BOOST_COROUTINE_OP_ARG(z,n,unused) BOOST_COROUTINE_OP_COMMA(n) BOOST_COROUTINE_OP_ARG_TYPE(n) BOOST_PP_CAT(a,n) +#define BOOST_COROUTINE_OP_ARGS(n) BOOST_PP_REPEAT_FROM_TO(1,BOOST_PP_ADD(n,1),BOOST_COROUTINE_OP_ARG,~) +#define BOOST_COROUTINE_OP(z,n,unused) \ +template< typename Signature, typename D, typename Result > \ +struct coroutine_op< Signature, D, Result, n > \ +{ \ + D & operator()( BOOST_COROUTINE_OP_ARGS(n)) \ + { \ + BOOST_ASSERT( static_cast< D * >( this)->impl_); \ + BOOST_ASSERT( ! static_cast< D * >( this)->impl_->is_complete() ); \ +\ + static_cast< D * >( this)->impl_->resume(BOOST_COROUTINE_OP_VALS(n)); \ +\ + return * static_cast< D * >( this); \ + } \ +}; +BOOST_PP_REPEAT_FROM_TO(2,11,BOOST_COROUTINE_OP,~) +#undef BOOST_COROUTINE_OP +#undef BOOST_COROUTINE_OP_ARGS +#undef BOOST_COROUTINE_OP_ARG +#undef BOOST_COROUTINE_OP_ARG_TYPE +#undef BOOST_COROUTINE_OP_VALS +#undef BOOST_COROUTINE_OP_VAL +#undef BOOST_COROUTINE_OP_COMMA + +}}} + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_SUFFIX +#endif + +#endif // BOOST_COROUTINES_DETAIL_COROUTINE_OP_H diff --git a/include/boost/coroutine/detail/exceptions.hpp b/include/boost/coroutine/detail/exceptions.hpp new file mode 100644 index 0000000..39f3854 --- /dev/null +++ b/include/boost/coroutine/detail/exceptions.hpp @@ -0,0 +1,28 @@ + +// 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_COROUTINES_DETAIL_EXCEPTIONs_H +#define BOOST_COROUTINES_DETAIL_EXCEPTIONs_H + +#include + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_PREFIX +#endif + +namespace boost { +namespace coroutines { +namespace detail { + +struct forced_unwind {}; + +}}} + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_SUFFIX +#endif + +#endif // BOOST_COROUTINES_DETAIL_EXCEPTIONs_H diff --git a/include/boost/coroutine/detail/flags.hpp b/include/boost/coroutine/detail/flags.hpp new file mode 100644 index 0000000..33f2142 --- /dev/null +++ b/include/boost/coroutine/detail/flags.hpp @@ -0,0 +1,34 @@ + +// 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_COROUTINES_DETAIL_FLAGS_H +#define BOOST_COROUTINES_DETAIL_FLAGS_H + +#include + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_PREFIX +#endif + +namespace boost { +namespace coroutines { +namespace detail { + +enum flag_t +{ + flag_complete = 1 << 1, + flag_unwind_stack = 1 << 2, + flag_force_unwind = 1 << 3, + flag_preserve_fpu = 1 << 4 +}; + +}}} + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_SUFFIX +#endif + +#endif // BOOST_COROUTINES_DETAIL_FLAGS_H diff --git a/include/boost/coroutine/detail/holder.hpp b/include/boost/coroutine/detail/holder.hpp new file mode 100644 index 0000000..f721158 --- /dev/null +++ b/include/boost/coroutine/detail/holder.hpp @@ -0,0 +1,88 @@ + +// 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_COROUTINES_DETAIL_HOLDER_H +#define BOOST_COROUTINES_DETAIL_HOLDER_H + +#include +#include +#include + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_PREFIX +#endif + +namespace boost { +namespace coroutines { +namespace detail { + +template< typename Data > +struct holder +{ + context::fcontext_t * ctx; + optional< Data > data; + bool force_unwind; + + holder( context::fcontext_t * ctx_) : + ctx( ctx_), data(), force_unwind( false) + { BOOST_ASSERT( ctx); } + + holder( context::fcontext_t * ctx_, Data data_) : + ctx( ctx_), data( data_), force_unwind( false) + { BOOST_ASSERT( ctx); } + + holder( context::fcontext_t * ctx_, bool force_unwind_) : + ctx( ctx_), data(), force_unwind( force_unwind_) + { + BOOST_ASSERT( ctx); + BOOST_ASSERT( force_unwind); + } + + holder( holder const& other) : + ctx( other.ctx), data( other.data), + force_unwind( other.force_unwind) + {} + + holder & operator=( holder const& other) + { + if ( this == & other) return * this; + ctx = other.ctx; + data = other.data; + force_unwind = other.force_unwind; + return * this; + } +}; + +template<> +struct holder< void > +{ + context::fcontext_t * ctx; + bool force_unwind; + + holder( context::fcontext_t * ctx_, bool force_unwind_ = false) : + ctx( ctx_), force_unwind( force_unwind_) + { BOOST_ASSERT( ctx); } + + holder( holder const& other) : + ctx( other.ctx), force_unwind( other.force_unwind) + {} + + holder & operator=( holder const& other) + { + if ( this == & other) return * this; + ctx = other.ctx; + force_unwind = other.force_unwind; + return * this; + } +}; + +}}} + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_SUFFIX +#endif + +#endif // BOOST_COROUTINES_DETAIL_HOLDER_H diff --git a/include/boost/coroutine/detail/param.hpp b/include/boost/coroutine/detail/param.hpp new file mode 100644 index 0000000..4e8f1fe --- /dev/null +++ b/include/boost/coroutine/detail/param.hpp @@ -0,0 +1,46 @@ + +// 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_COROUTINES_DETAIL_PARAM_H +#define BOOST_COROUTINES_DETAIL_PARAM_H + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_PREFIX +#endif + +namespace boost { +namespace coroutines { +namespace detail { + +template< typename T > +struct param : + public mpl::eval_if< + mpl::or_< + is_scalar< T >, + is_stateless< T >, + is_reference< T > + >, + mpl::identity< T >, + add_reference< T > + > +{}; + +}}} + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_SUFFIX +#endif + +#endif // BOOST_COROUTINES_DETAIL_PARAM_H diff --git a/include/boost/coroutine/detail/stack_allocator_posix.hpp b/include/boost/coroutine/detail/stack_allocator_posix.hpp new file mode 100644 index 0000000..f14784d --- /dev/null +++ b/include/boost/coroutine/detail/stack_allocator_posix.hpp @@ -0,0 +1,165 @@ + +// 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_COROUTINES_DETAIL_STACK_ALLOCATOR_H +#define BOOST_COROUTINES_DETAIL_STACK_ALLOCATOR_H + +#include + +extern "C" { +#include +#include +#include +#include +#include +#include +#include +#include +} + +//#if _POSIX_C_SOURCE >= 200112L + +#include +#include +#include +#include +#include + +#include +#include +#include + +#if !defined (SIGSTKSZ) +# define SIGSTKSZ (8 * 1024) +# define UDEF_SIGSTKSZ +#endif + + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_PREFIX +#endif + +namespace boost { +namespace coroutines { +namespace detail { + +inline +std::size_t pagesize() +{ + // conform to POSIX.1-2001 + static std::size_t size = ::sysconf( _SC_PAGESIZE); + return size; +} + +inline +rlimit stacksize_limit_() +{ + rlimit limit; + // conforming to POSIX.1-2001 + const int result = ::getrlimit( RLIMIT_STACK, & limit); + BOOST_ASSERT( 0 == result); + return limit; +} + +inline +rlimit stacksize_limit() +{ + static rlimit limit = stacksize_limit_(); + return limit; +} + +inline +std::size_t page_count( std::size_t stacksize) +{ + return static_cast< std::size_t >( + std::ceil( + static_cast< float >( stacksize) / pagesize() ) ); +} + +class stack_allocator +{ +public: + static bool is_stack_unbound() + { return RLIM_INFINITY == stacksize_limit().rlim_max; } + + static std::size_t default_stacksize() + { + std::size_t size = 8 * minimum_stacksize(); + if ( is_stack_unbound() ) return size; + + BOOST_ASSERT( maximum_stacksize() >= minimum_stacksize() ); + return maximum_stacksize() == size + ? size + : std::min( size, maximum_stacksize() ); + } + + static std::size_t minimum_stacksize() + { return SIGSTKSZ + sizeof( context::fcontext_t) + 15; } + + static std::size_t maximum_stacksize() + { + BOOST_ASSERT( ! is_stack_unbound() ); + return static_cast< std::size_t >( stacksize_limit().rlim_max); + } + + void * allocate( std::size_t size) const + { + BOOST_ASSERT( minimum_stacksize() <= size); + BOOST_ASSERT( is_stack_unbound() || ( maximum_stacksize() >= size) ); + + const std::size_t pages( page_count( size) + 1); // add one guard page + const std::size_t size_( pages * pagesize() ); + BOOST_ASSERT( 0 < size && 0 < size_); + + const int fd( ::open("/dev/zero", O_RDONLY) ); + BOOST_ASSERT( -1 != fd); + // conform to POSIX.4 (POSIX.1b-1993, _POSIX_C_SOURCE=199309L) + void * limit = +# if defined(macintosh) || defined(__APPLE__) || defined(__APPLE_CC__) + ::mmap( 0, size_, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0); +# else + ::mmap( 0, size_, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0); +# endif + ::close( fd); + if ( ! limit) throw std::bad_alloc(); + + std::memset( limit, size_, '\0'); + + // conforming to POSIX.1-2001 + const int result( ::mprotect( limit, pagesize(), PROT_NONE) ); + BOOST_ASSERT( 0 == result); + + return static_cast< char * >( limit) + size_; + } + + void deallocate( void * vp, std::size_t size) const + { + BOOST_ASSERT( vp); + BOOST_ASSERT( minimum_stacksize() <= size); + BOOST_ASSERT( is_stack_unbound() || ( maximum_stacksize() >= size) ); + + const std::size_t pages = page_count( size) + 1; + const std::size_t size_ = pages * pagesize(); + BOOST_ASSERT( 0 < size && 0 < size_); + void * limit = static_cast< char * >( vp) - size_; + // conform to POSIX.4 (POSIX.1b-1993, _POSIX_C_SOURCE=199309L) + ::munmap( limit, size_); + } +}; + +}}} + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_SUFFIX +#endif + +#ifdef UDEF_SIGSTKSZ +# undef SIGSTKSZ +#endif + +//#endif + +#endif // BOOST_COROUTINES_DETAIL_STACK_ALLOCATOR_H diff --git a/include/boost/coroutine/detail/stack_allocator_windows.hpp b/include/boost/coroutine/detail/stack_allocator_windows.hpp new file mode 100644 index 0000000..20183b1 --- /dev/null +++ b/include/boost/coroutine/detail/stack_allocator_windows.hpp @@ -0,0 +1,160 @@ + +// 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_COROUTINES_DETAIL_STACK_ALLOCATOR_H +#define BOOST_COROUTINES_DETAIL_STACK_ALLOCATOR_H + +#include + +extern "C" { +#include +} + +//#if defined (BOOST_WINDOWS) || _POSIX_C_SOURCE >= 200112L + +#include +#include +#include +#include +#include + +#include +#include +#include + +# if defined(BOOST_MSVC) +# pragma warning(push) +# pragma warning(disable:4244 4267) +# endif + +// 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 boost { +namespace coroutines { +namespace detail { + +inline +SYSTEM_INFO system_info_() +{ + SYSTEM_INFO si; + ::GetSystemInfo( & si); + return si; +} + +inline +SYSTEM_INFO system_info() +{ + static SYSTEM_INFO si = system_info_(); + return si; +} + +inline +std::size_t pagesize() +{ return static_cast< std::size_t >( system_info().dwPageSize); } + +inline +std::size_t page_count( std::size_t stacksize) +{ + return static_cast< std::size_t >( + std::ceil( + static_cast< float >( stacksize) / pagesize() ) ); +} + +class stack_allocator +{ +public: + // Windows seams not to provide a limit for the stacksize + static bool is_stack_unbound() + { return true; } + + static std::size_t default_stacksize() + { + using namespace std; + + std::size_t size = 64 * 1024; // 64 kB + if ( is_stack_unbound() ) + return max( size, minimum_stacksize() ); + + BOOST_ASSERT( maximum_stacksize() >= minimum_stacksize() ); + return maximum_stacksize() == minimum_stacksize() + ? minimum_stacksize() + : min( size, maximum_stacksize() ); + } + + // because Windows seams not to provide a limit for minimum stacksize + static std::size_t minimum_stacksize() + { return MIN_STACKSIZE; } + + // because Windows seams not to provide a limit for maximum stacksize + // maximum_stacksize() can never be called (pre-condition ! is_stack_unbound() ) + static std::size_t maximum_stacksize() + { + BOOST_ASSERT( ! is_stack_unbound() ); + return 1 * 1024 * 1024 * 1024; // 1GB + } + + void * allocate( std::size_t size) const + { + BOOST_ASSERT( minimum_stacksize() <= size); + BOOST_ASSERT( is_stack_unbound() || ( maximum_stacksize() >= size) ); + + const std::size_t pages( page_count( size) + 1); // add one guard page + const std::size_t size_ = pages * pagesize(); + BOOST_ASSERT( 0 < size && 0 < size_); + + void * limit = ::VirtualAlloc( 0, size_, MEM_COMMIT, PAGE_READWRITE); + if ( ! limit) throw std::bad_alloc(); + + std::memset( limit, size_, '\0'); + + DWORD old_options; + const BOOL result = ::VirtualProtect( + limit, pagesize(), PAGE_READWRITE | PAGE_GUARD /*PAGE_NOACCESS*/, & old_options); + BOOST_ASSERT( FALSE != result); + + return static_cast< char * >( limit) + size_; + } + + void deallocate( void * vp, std::size_t size) const + { + BOOST_ASSERT( vp); + BOOST_ASSERT( minimum_stacksize() <= size); + BOOST_ASSERT( is_stack_unbound() || ( maximum_stacksize() >= size) ); + + const std::size_t pages = page_count( size) + 1; + const std::size_t size_ = pages * pagesize(); + BOOST_ASSERT( 0 < size && 0 < size_); + void * limit = static_cast< char * >( vp) - size_; + ::VirtualFree( limit, 0, MEM_RELEASE); + } +}; + +}}} + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_SUFFIX +#endif + +//#endif + +#endif // BOOST_COROUTINES_DETAIL_STACK_ALLOCATOR_H diff --git a/include/boost/coroutine/flags.hpp b/include/boost/coroutine/flags.hpp new file mode 100644 index 0000000..a8194c6 --- /dev/null +++ b/include/boost/coroutine/flags.hpp @@ -0,0 +1,27 @@ + +// 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_COROUTINES_FLAGS_H +#define BOOST_COROUTINES_FLAGS_H + +namespace boost { +namespace coroutines { + +enum flag_unwind_t +{ + stack_unwind = 0, + no_stack_unwind +}; + +enum flag_fpu_t +{ + fpu_preserved = 0, + fpu_not_preserved +}; + +}} + +#endif // BOOST_COROUTINES_FLAGS_H diff --git a/include/boost/coroutine/stack_allocator.hpp b/include/boost/coroutine/stack_allocator.hpp new file mode 100644 index 0000000..6d80583 --- /dev/null +++ b/include/boost/coroutine/stack_allocator.hpp @@ -0,0 +1,23 @@ + +// 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_COROUTINES_STACK_ALLOCATOR_H +#define BOOST_COROUTINES_STACK_ALLOCATOR_H + +#include + +#if defined (BOOST_WINDOWS) +#include +#else +#include +#endif + +namespace boost { +namespace coroutines { +using detail::stack_allocator; +}} + +#endif // BOOST_COROUTINES_STACK_ALLOCATOR_H diff --git a/index.html b/index.html new file mode 100644 index 0000000..0ade6cb --- /dev/null +++ b/index.html @@ -0,0 +1,14 @@ + + + + + +Automatic redirection failed, please go to +doc/html/index.html +
+

© Copyright Beman Dawes, 2001

+

Distributed under the Boost Software +License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +www.boost.org/LICENSE_1_0.txt)

+ + diff --git a/performance/Jamfile.v2 b/performance/Jamfile.v2 new file mode 100644 index 0000000..108c0e1 --- /dev/null +++ b/performance/Jamfile.v2 @@ -0,0 +1,64 @@ + +# 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/context/performance + : requirements + /boost/context//boost_context + static + "-lrt" + single + ; + +alias sources + : performance.cpp + bind_processor_aix.cpp + : aix + ; + +alias sources + : performance.cpp + bind_processor_freebsd.cpp + : freebsd + ; + +alias sources + : performance.cpp + bind_processor_hpux.cpp + : hpux + ; + +alias sources + : performance.cpp + bind_processor_linux.cpp + : linux + ; + +alias sources + : performance.cpp + bind_processor_solaris.cpp + : solaris + ; + +alias sources + : performance.cpp + bind_processor_windows.cpp + : windows + ; + +explicit sources ; + +exe performance + : sources + ; diff --git a/performance/bind_processor.hpp b/performance/bind_processor.hpp new file mode 100644 index 0000000..f89b507 --- /dev/null +++ b/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/performance/bind_processor_aix.cpp b/performance/bind_processor_aix.cpp new file mode 100644 index 0000000..7ddc2eb --- /dev/null +++ b/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 +#include +} + +#include + +#include + +void bind_to_processor( unsigned int n) +{ + if ( ::bindprocessor( BINDTHREAD, ::thread_self(), static_cast< cpu_t >( n) ) == -1) + throw std::runtime_error("::bindprocessor() failed"); +} + +#include diff --git a/performance/bind_processor_freebsd.cpp b/performance/bind_processor_freebsd.cpp new file mode 100644 index 0000000..7e28b70 --- /dev/null +++ b/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 +#include +} + +#include + +#include + +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 diff --git a/performance/bind_processor_hpux.cpp b/performance/bind_processor_hpux.cpp new file mode 100644 index 0000000..af33c11 --- /dev/null +++ b/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 +} + +#include + +#include + +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 diff --git a/performance/bind_processor_linux.cpp b/performance/bind_processor_linux.cpp new file mode 100644 index 0000000..3fb9588 --- /dev/null +++ b/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 +#include +} + +#include + +#include + +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 diff --git a/performance/bind_processor_solaris.cpp b/performance/bind_processor_solaris.cpp new file mode 100644 index 0000000..0830c1b --- /dev/null +++ b/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 +#include +#include +} + +#include + +#include + +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 diff --git a/performance/bind_processor_windows.cpp b/performance/bind_processor_windows.cpp new file mode 100644 index 0000000..6fefbc6 --- /dev/null +++ b/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 +} + +#include + +#include + +void bind_to_processor( unsigned int n) +{ + if ( ::SetThreadAffinityMask( ::GetCurrentThread(), ( DWORD_PTR)1 << n) == 0) + throw std::runtime_error("::SetThreadAffinityMask() failed"); +} + +#include diff --git a/performance/cycle.hpp b/performance/cycle.hpp new file mode 100644 index 0000000..c26c859 --- /dev/null +++ b/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/performance/cycle_i386.hpp b/performance/cycle_i386.hpp new file mode 100644 index 0000000..3e67007 --- /dev/null +++ b/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 +#include +#include +#include + +#include +#include +#include + +#define BOOST_CONTEXT_CYCLE + +typedef boost::uint64_t cycle_t; + +#if _MSC_VER +inline +cycle_t cycles() +{ + cycle_t 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_t 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_t)hi << 32 | lo; +} +#else +# error "this compiler is not supported" +#endif + +struct measure_cycles +{ + cycle_t operator()() + { + cycle_t start( cycles() ); + return cycles() - start; + } +}; + +inline +cycle_t overhead_cycles() +{ + std::size_t iterations( 10); + std::vector< cycle_t > overhead( iterations, 0); + for ( std::size_t i( 0); i < iterations; ++i) + std::generate( + overhead.begin(), overhead.end(), + measure_cycles() ); + BOOST_ASSERT( overhead.begin() != overhead.end() ); + return std::accumulate( overhead.begin(), overhead.end(), 0) / iterations; +} + +#endif // CYCLE_I386_H diff --git a/performance/cycle_x86-64.hpp b/performance/cycle_x86-64.hpp new file mode 100644 index 0000000..a2212b8 --- /dev/null +++ b/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 +#include +#include +#include + +#include +#include +#include + +#define BOOST_CONTEXT_CYCLE + +typedef boost::uint64_t cycle_t; + +#if _MSC_VER >= 1400 +# include +# pragma intrinsic(__rdtsc) +inline +cycle_t cycles() +{ return __rdtsc(); } +#elif defined(__INTEL_COMPILER) || defined(__ICC) || defined(_ECC) || defined(__ICL) +inline +cycle_t cycles() +{ return __rdtsc(); } +#elif defined(__GNUC__) || defined(__SUNPRO_C) +inline +cycle_t 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_t)hi << 32 | lo; +} +#else +# error "this compiler is not supported" +#endif + +struct measure_cycles +{ + cycle_t operator()() + { + cycle_t start( cycles() ); + return cycles() - start; + } +}; + +inline +cycle_t overhead_cycles() +{ + std::size_t iterations( 10); + std::vector< cycle_t > overhead( iterations, 0); + for ( std::size_t i( 0); i < iterations; ++i) + std::generate( + overhead.begin(), overhead.end(), + measure_cycles() ); + BOOST_ASSERT( overhead.begin() != overhead.end() ); + return std::accumulate( overhead.begin(), overhead.end(), 0) / iterations; +} + +#endif // CYCLE_X86_64_H diff --git a/performance/performance.cpp b/performance/performance.cpp new file mode 100644 index 0000000..834df24 --- /dev/null +++ b/performance/performance.cpp @@ -0,0 +1,119 @@ + +// 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) + +#define BOOST_PP_LIMIT_MAG 10 + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "bind_processor.hpp" +#include "cycle.hpp" +#include "simple_stack_allocator.hpp" + +#if _POSIX_C_SOURCE >= 199309L +#include "zeit.hpp" +#endif + +namespace coro = boost::coroutines; +namespace ctx = boost::context; + +typedef coro::coroutine< void() > coro_t; + +#define COUNTER BOOST_PP_LIMIT_MAG + +#define CALL_COROUTINE(z,n,unused) \ + c(); + +void fn( coro_t::caller_type & c) +{ while ( true) c(); } + +#ifdef BOOST_CONTEXT_CYCLE +cycle_t test_cycles( cycle_t ov, coro::flag_fpu_t preserve_fpu) +{ + ctx::simple_stack_allocator< 8 * 1024 * 1024, 64 * 1024, 8 * 1024 > alloc; + coro_t c( fn, coro::attributes( preserve_fpu), alloc); + + // cache warum-up +BOOST_PP_REPEAT_FROM_TO( 0, COUNTER, CALL_COROUTINE, ~) + + cycle_t start( cycles() ); +BOOST_PP_REPEAT_FROM_TO( 0, COUNTER, CALL_COROUTINE, ~) + cycle_t total( cycles() - start); + + // we have two jumps and two measuremt-overheads + total -= ov; // overhead of measurement + total /= COUNTER; // per call + total /= 2; // 2x jump_to c1->c2 && c2->c1 + + return total; +} +#endif + +#if _POSIX_C_SOURCE >= 199309L +zeit_t test_zeit( zeit_t ov, coro::flag_fpu_t preserve_fpu) +{ + ctx::simple_stack_allocator< 8 * 1024 * 1024, 64 * 1024, 8 * 1024 > alloc; + coro_t c( fn, coro::attributes( preserve_fpu), alloc); + + // cache warum-up +BOOST_PP_REPEAT_FROM_TO( 0, BOOST_PP_LIMIT_MAG, CALL_COROUTINE, ~) + + zeit_t start( zeit() ); +BOOST_PP_REPEAT_FROM_TO( 0, BOOST_PP_LIMIT_MAG, CALL_COROUTINE, ~) + zeit_t total( zeit() - start); + + // we have two jumps and two measuremt-overheads + total -= ov; // overhead of measurement + total /= BOOST_PP_LIMIT_MAG; // per call + total /= 2; // 2x jump_to c1->c2 && c2->c1 + + return total; +} +#endif + +int main( int argc, char * argv[]) +{ + try + { + coro::flag_fpu_t preserve_fpu = coro::fpu_not_preserved; + bind_to_processor( 0); + +#ifdef BOOST_CONTEXT_CYCLE + { + cycle_t ov( overhead_cycles() ); + std::cout << "overhead for rdtsc == " << ov << " cycles" << std::endl; + + unsigned int res = test_cycles( ov, preserve_fpu); + std::cout << "coroutine: average of " << res << " cycles per switch" << std::endl; + } +#endif + +#if _POSIX_C_SOURCE >= 199309L + { + zeit_t ov( overhead_zeit() ); + std::cout << "\noverhead for clock_gettime() == " << ov << " ns" << std::endl; + + unsigned int res = test_zeit( ov, preserve_fpu); + std::cout << "coroutine: average of " << res << " ns per switch" << 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/performance/simple_stack_allocator.hpp b/performance/simple_stack_allocator.hpp new file mode 100644 index 0000000..afc5068 --- /dev/null +++ b/performance/simple_stack_allocator.hpp @@ -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) + +#ifndef BOOST_CONTEXT_SIMPLE_STACK_ALLOCATOR_H +#define BOOST_CONTEXT_SIMPLE_STACK_ALLOCATOR_H + +#include +#include +#include + +#include +#include + +#include + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_PREFIX +#endif + +namespace boost { +namespace context { + +template< std::size_t Max, std::size_t Default, std::size_t Min > +class simple_stack_allocator +{ +public: + static std::size_t maximum_stacksize() + { return Max; } + + static std::size_t default_stacksize() + { return Default; } + + static std::size_t minimum_stacksize() + { return Min; } + + void * allocate( std::size_t size) const + { + BOOST_ASSERT( minimum_stacksize() <= size); + BOOST_ASSERT( maximum_stacksize() >= size); + + void * limit = std::calloc( size, sizeof( char) ); + if ( ! limit) throw std::bad_alloc(); + + return static_cast< char * >( limit) + size; + } + + void deallocate( void * vp, std::size_t size) const + { + BOOST_ASSERT( vp); + BOOST_ASSERT( minimum_stacksize() <= size); + BOOST_ASSERT( maximum_stacksize() >= size); + + void * limit = static_cast< char * >( vp) - size; + std::free( limit); + } +}; + +}} + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_SUFFIX +#endif + +#endif // BOOST_CONTEXT_SIMPLE_STACK_ALLOCATOR_H diff --git a/performance/zeit.hpp b/performance/zeit.hpp new file mode 100644 index 0000000..2c082bf --- /dev/null +++ b/performance/zeit.hpp @@ -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) + +#ifndef ZEIT_H +#define ZEIT_H + +#include + +#include +#include +#include +#include + +#include +#include +#include + +typedef boost::uint64_t zeit_t; + +inline +zeit_t zeit() +{ + timespec t; + ::clock_gettime( CLOCK_PROCESS_CPUTIME_ID, & t); + return t.tv_sec * 1000000000 + t.tv_nsec; +} + +struct measure_zeit +{ + zeit_t operator()() + { + zeit_t start( zeit() ); + return zeit() - start; + } +}; + +inline +zeit_t overhead_zeit() +{ + std::size_t iterations( 10); + std::vector< zeit_t > overhead( iterations, 0); + for ( std::size_t i( 0); i < iterations; ++i) + std::generate( + overhead.begin(), overhead.end(), + measure_zeit() ); + BOOST_ASSERT( overhead.begin() != overhead.end() ); + return std::accumulate( overhead.begin(), overhead.end(), 0) / iterations; +} + +#endif // ZEIT_H diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 new file mode 100644 index 0000000..b11e69b --- /dev/null +++ b/test/Jamfile.v2 @@ -0,0 +1,25 @@ +# 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 + ../../test/build//boost_unit_test_framework + /boost/context//boost_context + static + ; + +test-suite "coroutine" : + [ run test_coroutine.cpp ] + ; diff --git a/test/test_coroutine.cpp b/test/test_coroutine.cpp new file mode 100644 index 0000000..46f0a03 --- /dev/null +++ b/test/test_coroutine.cpp @@ -0,0 +1,494 @@ + +// 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 +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace coro = boost::coroutines; + +int value1 = 0; +std::string value2 = ""; +bool value3 = false; + +typedef coro::coroutine< void() > coro_void_void; +typedef coro::coroutine< int() > coro_int_void; +typedef coro::coroutine< std::string() > coro_string_void; +typedef coro::coroutine< void(int) > coro_void_int; +typedef coro::coroutine< void(std::string const&) > coro_void_string; +typedef coro::coroutine< double(double,double) > coro_double; +typedef coro::coroutine< int(int,int) > coro_int; +typedef coro::coroutine< int(int) > coro_int_int; +typedef coro::coroutine< int*(int*) > coro_ptr; +typedef coro::coroutine< int const&(int const&) > coro_ref; +typedef coro::coroutine< boost::tuple(int&,int&) > coro_tuple; + +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_int_void::caller_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_int_void::caller_type &) + { value3 = state; } +}; + +struct my_exception {}; + +void f1( coro_void_void::caller_type & s) +{ s(); } + +void f2( coro_void_void::caller_type &) +{ ++value1; } + +void f3( coro_void_void::caller_type & self) +{ + ++value1; + self(); + ++value1; +} + +void f4( coro_int_void::caller_type & self) +{ + self( 3); + self( 7); +} + +void f5( coro_string_void::caller_type & self) +{ + std::string res("abc"); + self( res); + res = "xyz"; + self( res); +} + +void f6( coro_void_int::caller_type & self) +{ value1 = self.get(); } + +void f7( coro_void_string::caller_type & self) +{ value2 = self.get(); } + +void f8( coro_double::caller_type & self) +{ + double x = 0, y = 0; + boost::tie( x, y) = self.get(); + self( x + y); + boost::tie( x, y) = self.get(); + self( x + y); +} + +void f9( coro_ptr::caller_type & self) +{ self( self.get() ); } + +void f10( coro_ref::caller_type & self) +{ self( self.get() ); } + +void f11( coro_tuple::caller_type & self) +{ + boost::tuple tpl( self.get().get< 0 >(), self.get().get< 1 >() ); + self( tpl); +} + +void f12( coro_int::caller_type & self) +{ + X x_; + int x, y; + boost::tie( x, y) = self.get(); + self( x +y); + boost::tie( x, y) = self.get(); + self( x +y); +} + +template< typename E > +void f14( coro_void_void::caller_type & self, E const& e) +{ throw e; } + +void f16( coro_int_void::caller_type & self) +{ + self( 1); + self( 2); + self( 3); + self( 4); + self( 5); +} + +void f17( coro_void_int::caller_type & self, std::vector< int > & vec) +{ + int x = self.get(); + while ( 5 > x) + { + vec.push_back( x); + x = self().get(); + } +} + +void f18( coro_int_int::caller_type & self) +{ + if ( self.has_result() ) + { + int x = self.get(); + self( x + 1); + } + else + { + self( -1); + } +} + +void test_move() +{ + { + coro_void_void coro1; + coro_void_void coro2( f1); + BOOST_CHECK( ! coro1); + BOOST_CHECK( coro1.empty() ); + BOOST_CHECK( coro2); + BOOST_CHECK( ! coro2.empty() ); + coro1 = boost::move( coro2); + BOOST_CHECK( coro1); + BOOST_CHECK( ! coro1.empty() ); + BOOST_CHECK( ! coro2); + BOOST_CHECK( coro2.empty() ); + } + + { + value3 = false; + copyable cp( 3); + BOOST_CHECK( cp.state); + BOOST_CHECK( ! value3); + coro_int_void coro( cp); + BOOST_CHECK( cp.state); + BOOST_CHECK( value3); + } + + { + value3 = false; + moveable mv( 7); + BOOST_CHECK( mv.state); + BOOST_CHECK( ! value3); + coro_int_void coro( boost::move( mv) ); + BOOST_CHECK( ! mv.state); + BOOST_CHECK( value3); + } +} + +void test_complete() +{ + value1 = 0; + + coro_void_void coro( f2); + BOOST_CHECK( ! coro); + BOOST_CHECK_EQUAL( ( int)1, value1); +} + +void test_jump() +{ + value1 = 0; + + coro_void_void 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_int_void 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_string_void 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_void_int coro( f6, 3); + BOOST_CHECK( ! coro); + BOOST_CHECK_EQUAL( 3, value1); +} + +void test_arg_string() +{ + value2 = ""; + + coro_void_string coro( f7, std::string("abc") ); + BOOST_CHECK( ! coro); + BOOST_CHECK_EQUAL( std::string("abc"), value2); +} + +void test_fp() +{ + coro_double coro( f8, coro_double::arguments( 7.35, 3.14) ); + BOOST_CHECK( coro); + double res = coro.get(); + BOOST_CHECK( coro); + BOOST_CHECK_EQUAL( ( double) 10.49, res); + res = coro( 1.15, 3.14).get(); + BOOST_CHECK( coro); + BOOST_CHECK_EQUAL( ( double) 4.29, res); + coro( 1.15, 3.14); + BOOST_CHECK( ! coro); +} + +void test_ptr() +{ + int a = 3; + coro_ptr coro( f9, & a); + BOOST_CHECK( coro); + int * res = coro.get(); + BOOST_CHECK( coro); + BOOST_CHECK_EQUAL( & a, res); + coro( & a); + BOOST_CHECK( ! coro); +} + +void test_ref() +{ + int a = 3; + coro_ref coro( f10, a); + BOOST_CHECK( coro); + int const& res = coro.get(); + BOOST_CHECK( coro); + BOOST_CHECK_EQUAL( & a, & res); + coro( a); + BOOST_CHECK( ! coro); +} + +void test_tuple() +{ + int a = 3, b = 7; + coro_tuple coro( f11, coro_tuple::arguments( a, b) ); + BOOST_CHECK( coro); + boost::tuple tpl = coro.get(); + BOOST_CHECK( coro); + BOOST_CHECK_EQUAL( & a, & tpl.get< 0 >() ); + BOOST_CHECK_EQUAL( & b, & tpl.get< 1 >() ); + coro( a, b); + BOOST_CHECK( ! coro); +} + +void test_unwind() +{ + value1 = 0; + { + BOOST_CHECK_EQUAL( ( int) 0, value1); + coro_int coro( f12, coro_int::arguments( 3, 7) ); + BOOST_CHECK( coro); + int res = coro.get(); + BOOST_CHECK_EQUAL( ( int) 7, value1); + BOOST_CHECK( coro); + BOOST_CHECK_EQUAL( ( int) 10, res); + } + BOOST_CHECK_EQUAL( ( int) 0, value1); +} + +void test_no_unwind() +{ + value1 = 0; + { + BOOST_CHECK_EQUAL( ( int) 0, value1); + coro_int coro( + f12, + coro_int::arguments( 3, 7), + coro::attributes( + coro::stack_allocator::default_stacksize(), + coro::no_stack_unwind) ); + BOOST_CHECK( coro); + int res = coro.get(); + BOOST_CHECK( coro); + BOOST_CHECK_EQUAL( ( int) 10, res); + } + BOOST_CHECK_EQUAL( ( int) 7, value1); +} + +void test_exceptions() +{ + bool thrown = false; + std::runtime_error ex("abc"); + try + { + coro_void_void coro( boost::bind( f14< std::runtime_error >, _1, ex) ); + BOOST_CHECK( ! coro); + BOOST_CHECK( false); + } + catch ( std::runtime_error const&) + { thrown = true; } + catch ( std::exception const&) + {} + catch (...) + {} + BOOST_CHECK( thrown); +} + +void test_output_iterator() +{ + std::vector< int > vec; + coro_int_void 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] ); +} + +void test_input_iterator() +{ + int counter = 0; + std::vector< int > vec; + coro_void_int coro( + boost::bind( f17, _1, boost::ref( vec) ), + counter); + coro_void_int::iterator e( boost::end( coro) ); + for ( coro_void_int::iterator i( boost::begin( coro) ); + i != e; ++i) + { + i = ++counter; + } + BOOST_CHECK_EQUAL( ( std::size_t)5, vec.size() ); + BOOST_CHECK_EQUAL( ( int)0, vec[0] ); + BOOST_CHECK_EQUAL( ( int)1, vec[1] ); + BOOST_CHECK_EQUAL( ( int)2, vec[2] ); + BOOST_CHECK_EQUAL( ( int)3, vec[3] ); + BOOST_CHECK_EQUAL( ( int)4, vec[4] ); +} + +void test_pre() +{ + coro_int_int coro( f18, 0); + BOOST_CHECK( coro); + int res = coro.get(); + BOOST_CHECK_EQUAL( ( int) 1, res); + BOOST_CHECK( coro); + coro( -1); + BOOST_CHECK( ! coro); +} + +void test_post() +{ + coro_int_int coro( f18); + BOOST_CHECK( coro); + int res = coro.get(); + BOOST_CHECK_EQUAL( ( int) -1, res); + BOOST_CHECK( coro); + coro( -1); + BOOST_CHECK( ! coro); +} + + +boost::unit_test::test_suite * init_unit_test_suite( int, char* []) +{ + boost::unit_test::test_suite * test = + BOOST_TEST_SUITE("Boost.coroutine: coroutine test suite"); + + test->add( BOOST_TEST_CASE( & test_move) ); + test->add( BOOST_TEST_CASE( & test_complete) ); + test->add( BOOST_TEST_CASE( & test_jump) ); + test->add( BOOST_TEST_CASE( & test_pre) ); + test->add( BOOST_TEST_CASE( & test_post) ); + 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_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_output_iterator) ); + test->add( BOOST_TEST_CASE( & test_input_iterator) ); + + return test; +}