1[/ 2 Copyright Oliver Kowalke 2016. 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[#cc] 9[section:cc Context switching with call/cc] 10 11[note __callcc__ is the reference implementation of C++ proposal 12[@http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0534r3.pdf P0534R3: 13call/cc (call-with-current-continuation): A low-level API for stackful context 14switching].] 15 16__callcc__ (call with current continuation) is a universal control operator 17(well-known from the programming language Scheme) that captures the current 18continuation as a first-class object and pass it as an argument to another 19continuation. 20 21A continuation (abstract concept of functional programming languages) 22represents the state of the control flow of a program at a given point in time. 23Continuations can be suspended and resumed later in order to change the control 24flow of a program. 25 26Modern micro-processors are registers machines; the content of processor 27registers represent a continuation of the executed program at a given point in 28time. 29Operating systems simulate parallel execution of programs on a single processor 30by switching between programs (context switch) by preserving and restoring the 31continuation, e.g. the content of all registers. 32 33 34[heading __cc__] 35 36__cc__ is the C++ equivalent to Scheme's __callcc__ operator. It captures the 37current continuation (the rest of the computation; code after __cc__) and 38triggers a context switch. The context switch is achieved by preserving certain 39registers (including instruction and stack pointer), defined by the calling 40convention of the ABI, of the current continuation and restoring those 41registers of the resumed continuation. The control flow of the resumed 42continuation continues. 43The current continuation is suspended and passed as argument to the resumed 44continuation. 45 46__cc__ expects a __context_fn__ with signature 47`'continuation(continuation && c)'`. The parameter `c` represents the current 48continuation from which this continuation was resumed (e.g. that has called 49__cc__). 50 51On return the __context_fn__ of the current continuation has to specify an 52__con__ to which the execution control is transferred after termination 53of the current continuation. 54 55If an instance with valid state goes out of scope and the __context_fn__ has 56not yet returned, the stack is traversed in order to access the control 57structure (address stored at the first stack frame) and continuation's stack is 58deallocated via the __stack_allocator__. 59 60[note [link segmented ['Segmented stacks]] are supported by __cc__ using 61[link implementation ['ucontext_t]].] 62 63 64[heading __con__] 65 66__con__ represents a continuation; it contains the content of preserved 67registers and manages the associated stack (allocation/deallocation). 68__con__ is a one-shot continuation - it can be used only once, after calling 69__resume__ or __resume_with__ it is invalidated. 70 71__con__ is only move-constructible and move-assignable. 72 73As a first-class object __con__ can be applied to and returned from a function, 74assigned to a variable or stored in a container. 75 76A continuation is continued by calling `resume()`/`resume_with()`. 77 78 79[heading Usage] 80 81 namespace ctx=boost::context; 82 int a; 83 ctx::continuation source=ctx::callcc( 84 [&a](ctx::continuation && sink){ 85 a=0; 86 int b=1; 87 for(;;){ 88 sink=sink.resume(); 89 int next=a+b; 90 a=b; 91 b=next; 92 } 93 return std::move(sink); 94 }); 95 for (int j=0;j<10;++j) { 96 std::cout << a << " "; 97 source=source.resume(); 98 } 99 100 output: 101 0 1 1 2 3 5 8 13 21 34 102 103This simple example demonstrates the basic usage of __callcc__ as a ['generator]. 104The continuation `sink` represents the ['main]-continuation (function `main()`). 105`sink` is captured (current-continuation) by invoking __cc__ and passed as 106parameter to the lambda. 107 108Because the state is invalidated (one-shot continuation) by each call of 109__resume__, the new state of the __con__, returned by __resume__, needs to be 110assigned to `sink` after each call. 111 112The lambda that calculates the Fibonacci numbers is executed inside the 113continuation represented by `source`. Calculated Fibonacci numbers are 114transferred between the two continuations via variable `a` (lambda capture 115reference). 116 117The locale variables `b` and ` next` remain their values during each context 118switch. This is possible due `source` has its own stack and the stack is 119exchanged by each context switch. 120 121 122[heading Parameter passing] 123 124Data can be transferred between two continuations via global pointers, 125calling wrappers (like `std::bind`) or lambda captures. 126 127 namespace ctx=boost::context; 128 int i=1; 129 ctx::continuation c1=callcc([&i](ctx::continuation && c2){ 130 std::printf("inside c1,i==%d\n",i); 131 i+=1; 132 return c2.resume(); 133 }); 134 std::printf("i==%d\n",i); 135 136 output: 137 inside c1,i==1 138 i==2 139 140`callcc(<lambda>)` enters the lambda in continuation represented by `c1` with 141lambda capture reference `i=1`. 142The expression `c2.resume()` resumes the continuation `c2`. 143On return of `callcc(<lambda>)`, the variable `i` has the value of `i+1`. 144 145 146[heading Exception handling] 147 148If the function executed inside a __context_fn__ emits an exception, the 149application is terminated by calling `std::terminate()`. `std::exception_ptr` 150can be used to transfer exceptions between different continuations. 151 152[important Do not jump from inside a catch block and then re-throw the exception 153in another continuation.] 154 155 156[#cc_ontop] 157[heading Executing function on top of a continuation] 158 159Sometimes it is useful to execute a new function on top of a resumed 160continuation. For this purpose __resume_with__ has to be used. 161The function passed as argument must accept a rvalue reference to __con__ and 162return `void`. 163 164 namespace ctx=boost::context; 165 int data=0; 166 ctx::continuation c=ctx::callcc([&data](ctx::continuation && c) { 167 std::cout << "f1: entered first time: " << data << std::endl; 168 data+=1; 169 c=c.resume(); 170 std::cout << "f1: entered second time: " << data << std::endl; 171 data+=1; 172 c=c.resume(); 173 std::cout << "f1: entered third time: " << data << std::endl; 174 return std::move(c); 175 }); 176 std::cout << "f1: returned first time: " << data << std::endl; 177 data+=1; 178 c=c.resume(); 179 std::cout << "f1: returned second time: " << data << std::endl; 180 data+=1; 181 c=c.resume_with([&data](ctx::continuation && c){ 182 std::cout << "f2: entered: " << data << std::endl; 183 data=-1; 184 return std::move( c); 185 }); 186 std::cout << "f1: returned third time" << std::endl; 187 188 output: 189 f1: entered first time: 0 190 f1: returned first time: 1 191 f1: entered second time: 2 192 f1: returned second time: 3 193 f2: entered: 4 194 f1: entered third time: -1 195 f1: returned third time 196 197 198The expression `c.resume_with(...)` executes a lambda on top of continuation 199`c`, e.g. an additional stack frame is allocated on top of the stack. 200This lambda assigns `-1` to `data` and returns to the second invocation of 201`c.resume()`. 202 203Another option is to execute a function on top of the continuation that throws 204an exception. 205 206 namespace ctx=boost::context; 207 struct my_exception : public std::runtime_error { 208 ctx::continuation c; 209 my_exception(ctx::continuation && c_,std::string const& what) : 210 std::runtime_error{ what }, 211 c{ std::move( c_) } { 212 } 213 }; 214 215 ctx::continuation c=ctx::callcc([](ctx::continuation && c) { 216 for (;;) { 217 try { 218 std::cout << "entered" << std::endl; 219 c=c.resume(); 220 } catch (my_exception & ex) { 221 std::cerr << "my_exception: " << ex.what() << std::endl; 222 return std::move(ex.c); 223 } 224 } 225 return std::move(c); 226 }); 227 c = c.resume_with( 228 [](ctx::continuation && c){ 229 throw my_exception(std::move(c),"abc"); 230 return std::move( c); 231 }); 232 233 output: 234 entered 235 my_exception: abc 236 237In this exception `my_exception` is throw from a function invoked on-top of 238continuation `c` and catched inside the `for`-loop. 239 240[heading Stack unwinding] 241On construction of __con__ a stack is allocated. 242If the __context_fn__ returns the stack will be destructed. 243If the __context_fn__ has not yet returned and the destructor of an valid 244__con__ instance (e.g. ['continuation::operator bool()] returns 245`true`) is called, the stack will be destructed too. 246 247[important Code executed by __context_fn__ must not prevent the propagation ofs 248the __forced_unwind__ exception. Absorbing that exception will cause stack 249unwinding to fail. Thus, any code that catches all exceptions must re-throw any 250pending __forced_unwind__ exception.] 251 252 253[#cc_prealloc] 254[heading Allocating control structures on top of stack] 255Allocating control structures on top of the stack requires to allocated the 256__stack_context__ and create the control structure with placement new before 257__con__ is created. 258[note The user is responsible for destructing the control structure at the top 259of the stack.] 260 261 namespace ctx=boost::context; 262 // stack-allocator used for (de-)allocating stack 263 fixedsize_stack salloc(4048); 264 // allocate stack space 265 stack_context sctx(salloc.allocate()); 266 // reserve space for control structure on top of the stack 267 void * sp=static_cast<char*>(sctx.sp)-sizeof(my_control_structure); 268 std::size_t size=sctx.size-sizeof(my_control_structure); 269 // placement new creates control structure on reserved space 270 my_control_structure * cs=new(sp)my_control_structure(sp,size,sctx,salloc); 271 ... 272 // destructing the control structure 273 cs->~my_control_structure(); 274 ... 275 struct my_control_structure { 276 // captured continuation 277 ctx::continuation c; 278 279 template< typename StackAllocator > 280 my_control_structure(void * sp,std::size_t size,stack_context sctx,StackAllocator salloc) : 281 // create captured continuation 282 c{} { 283 c=ctx::callcc(std::allocator_arg,preallocated(sp,size,sctx),salloc,entry_func); 284 } 285 ... 286 }; 287 288 289[heading Inverting the control flow] 290 291 namespace ctx=boost::context; 292 /* 293 * grammar: 294 * P ---> E '\0' 295 * E ---> T {('+'|'-') T} 296 * T ---> S {('*'|'/') S} 297 * S ---> digit | '(' E ')' 298 */ 299 class Parser{ 300 char next; 301 std::istream& is; 302 std::function<void(char)> cb; 303 304 char pull(){ 305 return std::char_traits<char>::to_char_type(is.get()); 306 } 307 308 void scan(){ 309 do{ 310 next=pull(); 311 } 312 while(isspace(next)); 313 } 314 315 public: 316 Parser(std::istream& is_,std::function<void(char)> cb_) : 317 next(), is(is_), cb(cb_) 318 {} 319 320 void run() { 321 scan(); 322 E(); 323 } 324 325 private: 326 void E(){ 327 T(); 328 while (next=='+'||next=='-'){ 329 cb(next); 330 scan(); 331 T(); 332 } 333 } 334 335 void T(){ 336 S(); 337 while (next=='*'||next=='/'){ 338 cb(next); 339 scan(); 340 S(); 341 } 342 } 343 344 void S(){ 345 if (isdigit(next)){ 346 cb(next); 347 scan(); 348 } 349 else if(next=='('){ 350 cb(next); 351 scan(); 352 E(); 353 if (next==')'){ 354 cb(next); 355 scan(); 356 }else{ 357 throw std::runtime_error("parsing failed"); 358 } 359 } 360 else{ 361 throw std::runtime_error("parsing failed"); 362 } 363 } 364 }; 365 366 std::istringstream is("1+1"); 367 // execute parser in new continuation 368 ctx::continuation source; 369 // user-code pulls parsed data from parser 370 // invert control flow 371 char c; 372 bool done=false; 373 source=ctx::callcc( 374 [&is,&c,&done](ctx::continuation && sink){ 375 // create parser with callback function 376 Parser p(is, 377 [&sink,&c](char c_){ 378 // resume main continuation 379 c=c_; 380 sink=sink.resume(); 381 }); 382 // start recursive parsing 383 p.run(); 384 // signal termination 385 done=true; 386 // resume main continuation 387 return std::move(sink); 388 }); 389 while(!done){ 390 printf("Parsed: %c\n",c); 391 source=source.resume(); 392 } 393 394 output: 395 Parsed: 1 396 Parsed: + 397 Parsed: 1 398 399In this example a recursive descent parser uses a callback to emit a newly 400passed symbol. Using __callcc__ the control flow can be inverted, e.g. the 401user-code pulls parsed symbols from the parser - instead to get pushed from the 402parser (via callback). 403 404The data (character) is transferred between the two continuations. 405 406 407[#implementation] 408[section Implementations: fcontext_t, ucontext_t and WinFiber] 409 410[heading fcontext_t] 411The implementation uses __fcontext__ per default. fcontext_t is based on 412assembler and not available for all platforms. It provides a much better 413performance than __ucontext__ 414(the context switch takes two magnitudes of order less CPU cycles; see section 415[link performance ['performance]]) and __winfib__. 416 417[note Because the TIB (thread information block on Windows) is not fully 418described in the MSDN, it might be possible that not all required TIB-parts are 419swapped. Using WinFiber implementation migh be an alternative.] 420 421 422[heading ucontext_t] 423As an alternative, [@https://en.wikipedia.org/wiki/Setcontext __ucontext__] 424can be used by compiling with `BOOST_USE_UCONTEXT` and b2 property 425`context-impl=ucontext`. 426__ucontext__ might be available on a broader range of POSIX-platforms but has 427some [link ucontext ['disadvantages]] (for instance deprecated since 428POSIX.1-2003, not C99 conform). 429 430[note __cc__ supports [link segmented ['Segmented stacks]] only with 431__ucontext__ as its implementation.] 432 433 434[heading WinFiber] 435With `BOOST_USE_WINFIB` and b2 property `context-impl=winfib` Win32-Fibers are 436used as implementation for __cc__. 437 438[note The first call of __cc__ converts the thread into a Windows fiber by 439invoking `ConvertThreadToFiber()`. If desired, `ConvertFiberToThread()` has 440to be called by the user explicitly in order to release resources allocated 441by `ConvertThreadToFiber()` (e.g. after using boost.context). ] 442 443[endsect] 444 445 446[section Class `continuation`] 447 448 #include <boost/context/continuation.hpp> 449 450 class continuation { 451 public: 452 continuation() noexcept = default; 453 454 ~continuation(); 455 456 continuation(continuation && other) noexcept; 457 458 continuation & operator=(continuation && other) noexcept; 459 460 continuation(continuation const& other) noexcept = delete; 461 continuation & operator=(continuation const& other) noexcept = delete; 462 463 continuation resume(); 464 465 template<typename Fn> 466 continuation resume_with(Fn && fn); 467 468 explicit operator bool() const noexcept; 469 470 bool operator!() const noexcept; 471 472 bool operator==(continuation const& other) const noexcept; 473 474 bool operator!=(continuation const& other) const noexcept; 475 476 bool operator<(continuation const& other) const noexcept; 477 478 bool operator>(continuation const& other) const noexcept; 479 480 bool operator<=(continuation const& other) const noexcept; 481 482 bool operator>=(continuation const& other) const noexcept; 483 484 template<typename charT,class traitsT> 485 friend std::basic_ostream<charT,traitsT> & 486 operator<<(std::basic_ostream<charT,traitsT> & os,continuation const& other) { 487 488 void swap(continuation & other) noexcept; 489 }; 490 491[constructor_heading cc..constructor] 492 493 continuation() noexcept; 494 495[variablelist 496[[Effects:] [Creates a invalid continuation.]] 497[[Throws:] [Nothing.]] 498] 499 500[destructor_heading cc..destructor destructor] 501 502 ~continuation(); 503 504[variablelist 505[[Effects:] [Destructs the associated stack if `*this` is a valid continuation, 506e.g. ['continuation::operator bool()] returns `true`.]] 507[[Throws:] [Nothing.]] 508] 509 510[move_constructor_heading cc..move constructor] 511 512 continuation(continuation && other) noexcept; 513 514[variablelist 515[[Effects:] [Moves underlying capture continuation to `*this`.]] 516[[Throws:] [Nothing.]] 517] 518 519[move_assignment_heading cc..move assignment] 520 521 continuation & operator=(continuation && other) noexcept; 522 523[variablelist 524[[Effects:] [Moves the state of `other` to `*this` using move semantics.]] 525[[Throws:] [Nothing.]] 526] 527 528[operator_heading cc..operator_call..operator()] 529 530 continuation resume(); 531 532 template<typename Fn> 533 continuation resume_with(Fn && fn); 534 535[variablelist 536[[Effects:] [Captures current continuation and resumes `*this`. 537The function `resume_with`, is used to execute function `fn` in the execution context of 538`*this` (e.g. the stack frame of `fn` is allocated on stack of `*this`).]] 539[[Returns:] [The continuation representing the continuation that has been 540suspended.]] 541[[Note:] [Function `fn` needs to return `continuation`.]] 542[[Note:] [The returned continuation indicates if the suspended continuation has 543terminated (return from context-function) via `bool operator()`.]] 544] 545 546[operator_heading cc..operator_bool..operator bool] 547 548 explicit operator bool() const noexcept; 549 550[variablelist 551[[Returns:] [`true` if `*this` points to a captured continuation.]] 552[[Throws:] [Nothing.]] 553] 554 555[operator_heading cc..operator_not..operator!] 556 557 bool operator!() const noexcept; 558 559[variablelist 560[[Returns:] [`true` if `*this` does not point to a captured continuation.]] 561[[Throws:] [Nothing.]] 562] 563 564[operator_heading cc..operator_equal..operator==] 565 566 bool operator==(continuation const& other) const noexcept; 567 568[variablelist 569[[Returns:] [`true` if `*this` and `other` represent the same continuation, 570`false` otherwise.]] 571[[Throws:] [Nothing.]] 572] 573 574[operator_heading cc..operator_notequal..operator!=] 575 576 bool operator!=(continuation const& other) const noexcept; 577 578[variablelist 579[[Returns:] [[`! (other == * this)]]] 580[[Throws:] [Nothing.]] 581] 582 583[operator_heading cc..operator_less..operator<] 584 585 bool operator<(continuation const& other) const noexcept; 586 587[variablelist 588[[Returns:] [`true` if `*this != other` is true and the 589implementation-defined total order of `continuation` values places `*this` 590before `other`, false otherwise.]] 591[[Throws:] [Nothing.]] 592] 593 594[operator_heading cc..operator_greater..operator>] 595 596 bool operator>(continuation const& other) const noexcept; 597 598[variablelist 599[[Returns:] [`other < * this`]] 600[[Throws:] [Nothing.]] 601] 602 603[operator_heading cc..operator_lesseq..operator<=] 604 605 bool operator<=(continuation const& other) const noexcept; 606 607[variablelist 608[[Returns:] [`! (other < * this)`]] 609[[Throws:] [Nothing.]] 610] 611 612[operator_heading cc..operator_greatereq..operator>=] 613 614 bool operator>=(continuation const& other) const noexcept; 615 616[variablelist 617[[Returns:] [`! (* this < other)`]] 618[[Throws:] [Nothing.]] 619] 620 621[hding cc_..Non-member function [`operator<<()]] 622 623 template<typename charT,class traitsT> 624 std::basic_ostream<charT,traitsT> & 625 operator<<(std::basic_ostream<charT,traitsT> & os,continuation const& other); 626 627[variablelist 628[[Effects:] [Writes the representation of `other` to stream `os`.]] 629[[Returns:] [`os`]] 630] 631 632 633[heading Call with current continuation] 634 635 #include <boost/context/continuation.hpp> 636 637 template<typename Fn> 638 continuation callcc(Fn && fn); 639 640 template<typename StackAlloc,typename Fn> 641 continuation callcc(std::allocator_arg_t,StackAlloc salloc,Fn && fn); 642 643 template<typename StackAlloc,typename Fn> 644 continuation callcc(std::allocator_arg_t,preallocated palloc,StackAlloc salloc,Fn && fn); 645 646[variablelist 647[[Effects:] [Captures current continuation and creates a new continuation 648prepared to execute `fn`. `fixedsize_stack` is used as default stack allocator 649(stack size == fixedsize_stack::traits::default_size()). 650The function with argument type `preallocated`, is used to create a user 651defined data [link cc_prealloc (for instance additional control structures)] on 652top of the stack.]] 653[[Returns:] [The continuation representing the contexcontinuation that has been 654suspended.]] 655[[Note:] [The returned continuation indicates if the suspended continuation has 656terminated (return from context-function) via `bool operator()`.]] 657] 658 659[endsect] 660 661 662[endsect] 663