callcc.qbk 21 KB

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