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 0000000..9fa59c5 Binary files /dev/null and b/doc/foo_bar.png differ diff --git a/doc/images/foo_bar.png b/doc/images/foo_bar.png new file mode 100644 index 0000000..d01a9c0 Binary files /dev/null and b/doc/images/foo_bar.png differ diff --git a/doc/images/foo_bar_seq.png b/doc/images/foo_bar_seq.png new file mode 100644 index 0000000..33ab044 Binary files /dev/null and b/doc/images/foo_bar_seq.png differ diff --git a/doc/intro.qbk b/doc/intro.qbk new file mode 100644 index 0000000..4046011 --- /dev/null +++ b/doc/intro.qbk @@ -0,0 +1,335 @@ +[/ + 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:intro Introduction] + +[heading Definition] + +In computer science routines are defined as a sequence of operations. +The execution of routines form a parent-child relationship and the child +terminates always before the parent. +Coroutines are a generalization of routines. +The principal difference between coroutines and routines is that a coroutine +enables explicit suspend and resume their progress via additional operations by +preserving local state, e.g. a coroutine is a kind of continuation. +A continuation is a object representing a suspended execution (registers, +stack). Each coroutine has its own stack and local variables, sub-routine calls +etc. +In this sense coroutines are (actually) a language concept. + +[heading How it works] + +Functions foo() and bar() are supposed to alternate their execution (leave and +enter function body). + +[$../../../../libs/coroutine/doc/images/foo_bar.png [align center]] + +If coroutines would be called such as routines, the stack would grow with +every call and will never be degraded. A jump into the middle of a coroutine +would not be possible, because the return address would have been on top of +stack entries. + +The solution is that each coroutine has its own stack and control-block +(__fcontext__ from __boost_context__). +Before the coroutine gets suspended, the non-volatile registers (including stack +and instruction/program pointer) of the currently active coroutine are stored in +coroutine's control-block. +The registers of the newly activated coroutine must be restored from its +associated control-block before it can continue with their work. + +[$../../../../libs/coroutine/doc/images/foo_bar_seq.png [align center]] + +The context switch requires no system privileges and provides cooperative +multitasking on the level of language. Coroutines provide quasi parallelism. +When a program is supposed to do several things at the same time, coroutines +help to do this much simpler and more elegant than with only a single flow of +control. +Advantages can be seen particularly clearly with the use of a recursive +function, such as traversal of binary trees (see example 'same fringe'). + + +[heading Example: asio::io_stream with std::stream] + +This section demonstrates how stackfull coroutines help to use standard C++ +IO-streams together with IO-demultiplexer like __io_service__ (using +non-blocking IO). + + int main( int argc, char * argv[]) + { + ... + { + boost::asio::io_service io_service; + io_service.post( + boost::bind( + & server::start, + server::create( + io_service, port) ) ); + io_service.run(); + } + ... + } + + +__server__ accepts connection-requests made by clients, creates for each new +connection an instance of type __session__ and invokes __start__ on it. + + 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) + { + // 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; +}