1[/ 2 Copyright Oliver Kowalke 2014. 3 Distributed under the Boost Software License, Version 1.0. 4 (See accompanying file LICENSE_1_0.txt or copy at 5 http://www.boost.org/LICENSE_1_0.txt 6] 7 8[section:asymmetric Asymmetric coroutine] 9 10Two asymmetric coroutine types - __push_coro__ and __pull_coro__ - provide a 11unidirectional transfer of data. 12[note ['asymmetric_coroutine<>] is a typedef of __coro__.] 13 14 15[heading __pull_coro__] 16__pull_coro__ transfers data from another execution context (== pulled-from). 17The template parameter defines the transferred parameter type. 18The constructor of __pull_coro__ takes a function (__coro_fn__) accepting a 19reference to an __push_coro__ as argument. Instantiating an __pull_coro__ passes 20the control of execution to __coro_fn__ and a complementary __push_coro__ is 21synthesized by the library and passed as reference to __coro_fn__. 22 23This kind of coroutine provides __pull_coro_op__. This method only switches 24context; it transfers no data. 25 26__pull_coro__ provides input iterators (__pull_coro_it__) and __begin__/__end__ 27are overloaded. The increment-operation switches the context and transfers data. 28 29 typedef boost::coroutines2::coroutine<int> coro_t; 30 31 coro_t::pull_type source( 32 [&](coro_t::push_type& sink){ 33 int first=1,second=1; 34 sink(first); 35 sink(second); 36 for(int i=0;i<8;++i){ 37 int third=first+second; 38 first=second; 39 second=third; 40 sink(third); 41 } 42 }); 43 44 for(auto i:source) 45 std::cout << i << " "; 46 47 output: 48 1 1 2 3 5 8 13 21 34 55 49 50In this example an __pull_coro__ is created in the main execution context taking 51a lambda function (== __coro_fn__) which calculates Fibonacci numbers in a 52simple ['for]-loop. 53The __coro_fn__ is executed in a newly created execution context which is 54managed by the instance of __pull_coro__. 55An __push_coro__ is automatically generated by the library and passed as 56reference to the lambda function. Each time the lambda function calls 57__push_coro_op__ with another Fibonacci number, __push_coro__ transfers it back 58to the main execution context. The local state of __coro_fn__ is preserved and 59will be restored upon transferring execution control back to __coro_fn__ 60to calculate the next Fibonacci number. 61Because __pull_coro__ provides input iterators and __begin__/__end__ are 62overloaded, a ['range-based for]-loop can be used to iterate over the generated 63Fibonacci numbers. 64 65 66[heading __push_coro__] 67__push_coro__ transfers data to the other execution context (== pushed-to). 68The template parameter defines the transferred parameter type. 69The constructor of __push_coro__ takes a function (__coro_fn__) accepting a 70reference to an __pull_coro__ as argument. In contrast to __pull_coro__, 71instantiating an __push_coro__ does not pass the control of execution to 72__coro_fn__ - instead the first call of __push_coro_op__ synthesizes a 73complementary __pull_coro__ and passes it as reference to __coro_fn__. 74 75The __push_coro__ interface does not contain a ['get()]-function: you can not retrieve 76values from another execution context with this kind of coroutine. 77 78__push_coro__ provides output iterators (__push_coro_it__) and 79__begin__/__end__ are overloaded. The increment-operation switches the context 80and transfers data. 81 82 typedef boost::coroutines2::coroutine<std::string> coro_t; 83 84 struct FinalEOL{ 85 ~FinalEOL(){ 86 std::cout << std::endl; 87 } 88 }; 89 90 const int num=5, width=15; 91 coro_t::push_type writer( 92 [&](coro_t::pull_type& in){ 93 // finish the last line when we leave by whatever means 94 FinalEOL eol; 95 // pull values from upstream, lay them out 'num' to a line 96 for (;;){ 97 for(int i=0;i<num;++i){ 98 // when we exhaust the input, stop 99 if(!in) return; 100 std::cout << std::setw(width) << in.get(); 101 // now that we've handled this item, advance to next 102 in(); 103 } 104 // after 'num' items, line break 105 std::cout << std::endl; 106 } 107 }); 108 109 std::vector<std::string> words{ 110 "peas", "porridge", "hot", "peas", 111 "porridge", "cold", "peas", "porridge", 112 "in", "the", "pot", "nine", 113 "days", "old" }; 114 115 std::copy(begin(words),end(words),begin(writer)); 116 117 output: 118 peas porridge hot peas porridge 119 cold peas porridge in the 120 pot nine days old 121 122In this example an __push_coro__ is created in the main execution context 123accepting a lambda function (== __coro_fn__) which requests strings and lays out 124'num' of them on each line. 125This demonstrates the inversion of control permitted by coroutines. Without 126coroutines, a utility function to perform the same job would necessarily 127accept each new value as a function parameter, returning after processing that 128single value. That function would depend on a static state variable. A 129__coro_fn__, however, can request each new value as if by calling a function 130-- even though its caller also passes values as if by calling a function. 131The __coro_fn__ is executed in a newly created execution context which is 132managed by the instance of __push_coro__. 133The main execution context passes the strings to the __coro_fn__ by calling 134__push_coro_op__. 135An __pull_coro__ instance is automatically generated by the library and passed as 136reference to the lambda function. The __coro_fn__ accesses the strings passed 137from the main execution context by calling __pull_coro_get__ and lays those 138strings out on ['std::cout] according the parameters 'num' and 'width'. 139The local state of __coro_fn__ is preserved and will be restored after 140transferring execution control back to __coro_fn__. 141Because __push_coro__ provides output iterators and __begin__/__end__ are 142overloaded, the ['std::copy] algorithm can be used to iterate over the vector 143containing the strings and pass them one by one to the coroutine. 144 145 146[heading coroutine-function] 147The __coro_fn__ returns ['void] and takes its counterpart-coroutine as 148argument, so that using the coroutine passed as argument to __coro_fn__ is the 149only way to transfer data and execution control back to the caller. 150Both coroutine types take the same template argument. 151For __pull_coro__ the __coro_fn__ is entered at __pull_coro__ construction. 152For __push_coro__ the __coro_fn__ is not entered at __push_coro__ construction 153but entered by the first invocation of __push_coro_op__. 154After execution control is returned from __coro_fn__ the state of the 155coroutine can be checked via __pull_coro_bool__ returning `true` if the 156coroutine is still valid (__coro_fn__ has not terminated). Unless the first 157template parameter is `void`, `true` also implies that a data value is 158available. 159 160 161[heading passing data from a pull-coroutine to main-context] 162In order to transfer data from an __pull_coro__ to the main-context the framework 163synthesizes an __push_coro__ associated with the __pull_coro__ instance in the 164main-context. The synthesized __push_coro__ is passed as argument to __coro_fn__. 165The __coro_fn__ must call this __push_coro_op__ in order to transfer each 166data value back to the main-context. 167In the main-context, the __pull_coro_bool__ determines whether the coroutine is 168still valid and a data value is available or __coro_fn__ has terminated 169(__pull_coro__ is invalid; no data value available). Access to the transferred 170data value is given by __pull_coro_get__. 171 172 typedef boost::coroutines2::coroutine<int> coro_t; 173 174 coro_t::pull_type source( // constructor enters coroutine-function 175 [&](coro_t::push_type& sink){ 176 sink(1); // push {1} back to main-context 177 sink(1); // push {1} back to main-context 178 sink(2); // push {2} back to main-context 179 sink(3); // push {3} back to main-context 180 sink(5); // push {5} back to main-context 181 sink(8); // push {8} back to main-context 182 }); 183 184 while(source){ // test if pull-coroutine is valid 185 int ret=source.get(); // access data value 186 source(); // context-switch to coroutine-function 187 } 188 189 190[heading passing data from main-context to a push-coroutine] 191In order to transfer data to an __push_coro__ from the main-context the framework 192synthesizes an __pull_coro__ associated with the __push_coro__ instance in the 193main-context. The synthesized __pull_coro__ is passed as argument to __coro_fn__. 194The main-context must call this __push_coro_op__ in order to transfer each data 195value into the __coro_fn__. 196Access to the transferred data value is given by __pull_coro_get__. 197 198 typedef boost::coroutines2::coroutine<int> coro_t; 199 200 coro_t::push_type sink( // constructor does NOT enter coroutine-function 201 [&](coro_t::pull_type& source){ 202 for (int i:source) { 203 std::cout << i << " "; 204 } 205 }); 206 207 std::vector<int> v{1,1,2,3,5,8,13,21,34,55}; 208 for( int i:v){ 209 sink(i); // push {i} to coroutine-function 210 } 211 212 213[heading accessing parameters] 214Parameters returned from or transferred to the __coro_fn__ can be accessed with 215__pull_coro_get__. 216 217Splitting-up the access of parameters from context switch function enables to 218check if __pull_coro__ is valid after return from __pull_coro_op__, e.g. 219__pull_coro__ has values and __coro_fn__ has not terminated. 220 221 typedef boost::coroutines2::coroutine<boost::tuple<int,int>> coro_t; 222 223 coro_t::push_type sink( 224 [&](coro_t::pull_type& source){ 225 // access tuple {7,11}; x==7 y==1 226 int x,y; 227 boost::tie(x,y)=source.get(); 228 }); 229 230 sink(boost::make_tuple(7,11)); 231 232 233[heading exceptions] 234An exception thrown inside an __pull_coro__'s __coro_fn__ before its first call 235to __push_coro_op__ will be re-thrown by the __pull_coro__ constructor. After an 236__pull_coro__'s __coro_fn__'s first call to __push_coro_op__, any subsequent 237exception inside that __coro_fn__ will be re-thrown by __pull_coro_op__. 238__pull_coro_get__ does not throw. 239 240An exception thrown inside an __push_coro__'s __coro_fn__ will be re-thrown by 241__push_coro_op__. 242 243[important Code executed by __coro_fn__ must not prevent the propagation of the 244__forced_unwind__ exception. Absorbing that exception will cause stack 245unwinding to fail. Thus, any code that catches all exceptions must re-throw any 246pending __forced_unwind__ exception.] 247 248 try { 249 // code that might throw 250 } catch(const boost::coroutines2::detail::forced_unwind&) { 251 throw; 252 } catch(...) { 253 // possibly not re-throw pending exception 254 } 255 256[important Do not jump from inside a catch block and than re-throw the 257exception in another execution context.] 258 259 260[heading Stack unwinding] 261Sometimes it is necessary to unwind the stack of an unfinished coroutine to 262destroy local stack variables so they can release allocated resources (RAII 263pattern). The `attributes` argument of the coroutine constructor 264indicates whether the destructor should unwind the stack (stack is unwound by 265default). 266 267Stack unwinding assumes the following preconditions: 268 269* The coroutine is not __not_a_coro__ 270* The coroutine is not complete 271* The coroutine is not running 272* The coroutine owns a stack 273 274After unwinding, a __coro__ is complete. 275 276 struct X { 277 X(){ 278 std::cout<<"X()"<<std::endl; 279 } 280 281 ~X(){ 282 std::cout<<"~X()"<<std::endl; 283 } 284 }; 285 286 { 287 typedef boost::coroutines2::coroutine<void>::push_type coro_t; 288 289 coro_t::push_type sink( 290 [&](coro_t::pull_type& source){ 291 X x; 292 for(int=0;;++i){ 293 std::cout<<"fn(): "<<i<<std::endl; 294 // transfer execution control back to main() 295 source(); 296 } 297 }); 298 299 sink(); 300 sink(); 301 sink(); 302 sink(); 303 sink(); 304 305 std::cout<<"sink is complete: "<<std::boolalpha<<!sink<<"\n"; 306 } 307 308 output: 309 X() 310 fn(): 0 311 fn(): 1 312 fn(): 2 313 fn(): 3 314 fn(): 4 315 fn(): 5 316 sink is complete: false 317 ~X() 318 319 320[heading Range iterators] 321__boost_coroutine__ provides output- and input-iterators using __boost_range__. 322__pull_coro__ can be used via input-iterators using __begin__ and __end__. 323 324 typedef boost::coroutines2::coroutine< int > coro_t; 325 326 int number=2,exponent=8; 327 coro_t::pull_type source( 328 [&](coro_t::push_type & sink){ 329 int counter=0,result=1; 330 while(counter++<exponent){ 331 result=result*number; 332 sink(result); 333 } 334 }); 335 336 for (auto i:source) 337 std::cout << i << " "; 338 339 output: 340 2 4 8 16 32 64 128 256 341 342['coroutine<>::pull_type::iterator::operator++()] corresponds to 343__pull_coro_op__; ['coroutine<>::pull_type::iterator::operator*()] 344roughly corresponds to __pull_coro_get__. An iterator originally obtained from 345__begin__ of an __pull_coro__ compares equal to an iterator obtained from 346__end__ of that same __pull_coro__ instance when its __pull_coro_bool__ would 347return `false`]. 348 349[note If `T` is a move-only type, then 350['coroutine<T>::pull_type::iterator] may only be dereferenced once 351before it is incremented again.] 352 353Output-iterators can be created from __push_coro__. 354 355 typedef boost::coroutines2::coroutine<int> coro_t; 356 357 coro_t::push_type sink( 358 [&](coro_t::pull_type& source){ 359 while(source){ 360 std::cout << source.get() << " "; 361 source(); 362 } 363 }); 364 365 std::vector<int> v{1,1,2,3,5,8,13,21,34,55}; 366 std::copy(begin(v),end(v),begin(sink)); 367 368['coroutine<>::push_type::iterator::operator*()] roughly 369corresponds to __push_coro_op__. An iterator originally obtained from 370__begin__ of an __push_coro__ compares equal to an iterator obtained from 371__end__ of that same __push_coro__ instance when its __push_coro_bool__ would 372return `false`. 373 374 375[heading Exit a __coro_fn__] 376__coro_fn__ is exited with a simple return statement jumping back to the calling 377routine. The __pull_coro__, __push_coro__ becomes complete, e.g. __pull_coro_bool__, 378__push_coro_bool__ will return `false`. 379 380[important After returning from __coro_fn__ the __coro__ is complete (can not 381resumed with __push_coro_op__, __pull_coro_op__).] 382 383 384 385[section:pull_coro Class `coroutine<>::pull_type`] 386 387 #include <boost/coroutine2/coroutine.hpp> 388 389 template< typename R > 390 class coroutine<>::pull_type 391 { 392 public: 393 template< typename Fn > 394 pull_type( Fn && fn); 395 396 template< typename StackAllocator, typename Fn > 397 pull_type( StackAllocator stack_alloc, Fn && fn); 398 399 pull_type( pull_type const& other)=delete; 400 401 pull_type & operator=( pull_type const& other)=delete; 402 403 ~pull_type(); 404 405 pull_type( pull_type && other) noexcept; 406 407 pull_type & operator=( pull_type && other) noexcept; 408 409 pull_coroutine & operator()(); 410 411 explicit operator bool() const noexcept; 412 413 bool operator!() const noexcept; 414 415 R get() noexcept; 416 }; 417 418 template< typename R > 419 range_iterator< pull_type< R > >::type begin( pull_type< R > &); 420 421 template< typename R > 422 range_iterator< pull_type< R > >::type end( pull_type< R > &); 423 424[heading `template< typename Fn > 425 pull_type( Fn && fn)`] 426[variablelist 427[[Effects:] [Creates a coroutine which will execute `fn`, and enters it.]] 428[[Throws:] [Exceptions thrown inside __coro_fn__.]] 429] 430 431[heading `template< typename StackAllocator, typename Fn > 432 pull_type( StackAllocator const& stack_alloc, Fn && fn)`] 433[variablelist 434[[Effects:] [Creates a coroutine which will execute `fn`. 435For allocating/deallocating the stack `stack_alloc` is used.]] 436[[Throws:] [Exceptions thrown inside __coro_fn__.]] 437] 438 439[heading `~pull_type()`] 440[variablelist 441[[Effects:] [Destroys the context and deallocates the stack.]] 442] 443 444[heading `pull_type( pull_type && other)`] 445[variablelist 446[[Effects:] [Moves the internal data of `other` to `*this`. 447`other` becomes __not_a_coro__.]] 448[[Throws:] [Nothing.]] 449] 450 451[heading `pull_type & operator=( pull_type && other)`] 452[variablelist 453[[Effects:] [Destroys the internal data of `*this` and moves the 454internal data of `other` to `*this`. `other` becomes __not_a_coro__.]] 455[[Throws:] [Nothing.]] 456] 457 458[heading `explicit operator bool() const noexcept`] 459[variablelist 460[[Returns:] [If `*this` refers to __not_a_coro__ or the coroutine-function 461has returned (completed), the function returns `false`. Otherwise `true`.]] 462[[Throws:] [Nothing.]] 463] 464 465[heading `bool operator!() const noexcept`] 466[variablelist 467[[Returns:] [If `*this` refers to __not_a_coro__ or the coroutine-function 468has returned (completed), the function returns `true`. Otherwise `false`.]] 469[[Throws:] [Nothing.]] 470] 471 472[heading `pull_type<> & operator()()`] 473[variablelist 474[[Preconditions:] [`*this` is not a __not_a_coro__.]] 475[[Effects:] [Execution control is transferred to __coro_fn__ (no parameter is 476passed to the coroutine-function).]] 477[[Throws:] [Exceptions thrown inside __coro_fn__.]] 478] 479 480[heading `R get() noexcept`] 481 482 R coroutine<R,StackAllocator>::pull_type::get(); 483 R& coroutine<R&,StackAllocator>::pull_type::get(); 484 void coroutine<void,StackAllocator>::pull_type::get()=delete; 485 486[variablelist 487[[Preconditions:] [`*this` is not a __not_a_coro__.]] 488[[Returns:] [Returns data transferred from coroutine-function via 489__push_coro_op__.]] 490[[Throws:] [`invalid_result`]] 491[[Note:] [If `R` is a move-only type, you may only call `get()` once before 492the next __pull_coro_op__ call.]] 493] 494 495[heading Non-member function `begin( pull_type< R > &)`] 496 template< typename R > 497 range_iterator< pull_type< R > >::type begin( pull_type< R > &); 498 499[variablelist 500[[Returns:] [Returns a range-iterator (input-iterator).]] 501] 502 503[heading Non-member function `end( pull_type< R > &)`] 504 template< typename R > 505 range_iterator< pull_type< R > >::type end( pull_type< R > &); 506 507[variablelist 508[[Returns:] [Returns an end range-iterator (input-iterator).]] 509[[Note:] [When first obtained from `begin( pull_type< R > &)`, or after some 510number of increment operations, an iterator will compare equal to the iterator 511returned by `end( pull_type< R > &)` when the corresponding __pull_coro_bool__ 512would return `false`.]] 513] 514 515[endsect] 516 517 518[section:push_coro Class `coroutine<>::push_type`] 519 520 #include <boost/coroutine2/coroutine.hpp> 521 522 template< typename Arg > 523 class coroutine<>::push_type 524 { 525 public: 526 template< typename Fn > 527 push_type( Fn && fn); 528 529 template< typename StackAllocator, typename Fn > 530 push_type( StackAllocator stack_alloc, Fn && fn); 531 532 push_type( push_type const& other)=delete; 533 534 push_type & operator=( push_type const& other)=delete; 535 536 ~push_type(); 537 538 push_type( push_type && other) noexcept; 539 540 push_type & operator=( push_type && other) noexcept; 541 542 explicit operator bool() const noexcept; 543 544 bool operator!() const noexcept; 545 546 push_type & operator()( Arg arg); 547 }; 548 549 template< typename Arg > 550 range_iterator< push_type< Arg > >::type begin( push_type< Arg > &); 551 552 template< typename Arg > 553 range_iterator< push_type< Arg > >::type end( push_type< Arg > &); 554 555[heading `template< typename Fn > 556 push_type( Fn && fn)`] 557[variablelist 558[[Effects:] [Creates a coroutine which will execute `fn`.]] 559] 560 561[heading `template< typename StackAllocator, typename Fn > 562 push_type( StackAllocator const& stack_alloc, Fn && fn)`] 563[variablelist 564[[Effects:] [Creates a coroutine which will execute `fn`. 565For allocating/deallocating the stack `stack_alloc` is used.]] 566] 567 568[heading `~push_type()`] 569[variablelist 570[[Effects:] [Destroys the context and deallocates the stack.]] 571] 572 573[heading `push_type( push_type && other) noexcept`] 574[variablelist 575[[Effects:] [Moves the internal data of `other` to `*this`. 576`other` becomes __not_a_coro__.]] 577[[Throws:] [Nothing.]] 578] 579 580[heading `push_type & operator=( push_type && other) noexcept`] 581[variablelist 582[[Effects:] [Destroys the internal data of `*this` and moves the 583internal data of `other` to `*this`. `other` becomes __not_a_coro__.]] 584[[Throws:] [Nothing.]] 585] 586 587[heading `explicit operator bool() const noexcept`] 588[variablelist 589[[Returns:] [If `*this` refers to __not_a_coro__ or the coroutine-function 590has returned (completed), the function returns `false`. Otherwise `true`.]] 591[[Throws:] [Nothing.]] 592] 593 594[heading `bool operator!() const noexcept`] 595[variablelist 596[[Returns:] [If `*this` refers to __not_a_coro__ or the coroutine-function 597has returned (completed), the function returns `true`. Otherwise `false`.]] 598[[Throws:] [Nothing.]] 599] 600 601[heading `push_type & operator()(Arg arg)`] 602 603 push_type& coroutine<Arg>::push_type::operator()(Arg); 604 push_type& coroutine<Arg&>::push_type::operator()(Arg&); 605 push_type& coroutine<void>::push_type::operator()(); 606 607[variablelist 608[[Preconditions:] [operator unspecified-bool-type() returns `true` for `*this`.]] 609[[Effects:] [Execution control is transferred to __coro_fn__ and the argument 610`arg` is passed to the coroutine-function.]] 611[[Throws:] [Exceptions thrown inside __coro_fn__.]] 612] 613 614[heading Non-member function `begin( push_type< Arg > &)`] 615 template< typename Arg > 616 range_iterator< push_type< Arg > >::type begin( push_type< Arg > &); 617 618[variablelist 619[[Returns:] [Returns a range-iterator (output-iterator).]] 620] 621 622[heading Non-member function `end( push_type< Arg > &)`] 623 template< typename Arg > 624 range_iterator< push_type< Arg > >::type end( push_type< Arg > &); 625 626[variablelist 627[[Returns:] [Returns a end range-iterator (output-iterator).]] 628[[Note:] [When first obtained from `begin( push_type< R > &)`, or after some 629number of increment operations, an iterator will compare equal to the iterator 630returned by `end( push_type< R > &)` when the corresponding __push_coro_bool__ 631would return `false`.]] 632] 633 634[endsect] 635 636 637 638[endsect] 639