Blame view

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