]> git.proxmox.com Git - ceph.git/blob - ceph/src/boost/libs/context/doc/execution_context_v2.qbk
add subtree-ish sources for 12.0.3
[ceph.git] / ceph / src / boost / libs / context / doc / execution_context_v2.qbk
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 [#ecv2]
9 [section:ecv2 Class execution_context (version 2)]
10
11 [note This class is enabled per default.]
12
13 Class __econtext__ encapsulates context switching and manages the associated
14 context' stack (allocation/deallocation).
15
16 __econtext__ allocates the context stack (using its [link stack
17 __stack_allocator__] argument) and creates a control structure on top of it.
18 This structure is responsible for managing context' stack. The address of the
19 control structure is stored in the first frame of context' stack (e.g. it can
20 not directly accessed from within __econtext__). In contrast to __ecv1__ the
21 ownership of the control structure is not shared (no member variable to control
22 structure in __econtext__). __econtext__ keeps internally a state that is moved
23 by a call of __ec_op__ (`*this` will be invalidated), e.g. after a calling
24 __ec_op__, `*this` can not be used for an additional context switch.
25
26 __econtext__ is only move-constructible and move-assignable.
27
28 The moved state is assigned to a new instance of __econtext__. This object
29 becomes the first argument of the context-function, if the context was resumed
30 the first time, or the first element in a tuple returned by __ec_op__ that has
31 been called in the resumed context.
32 In contrast to __ecv1__, the context switch is faster because no global pointer
33 etc. is involved.
34
35 [important Segmented stacks are not supported by __econtext__ (v2).]
36
37 On return the context-function of the current context has to specify an
38 __econtext__ to which the execution control is transferred after termination
39 of the current context.
40
41 If an instance with valid state goes out of scope and the context-function has
42 not yet returned, the stack is traversed in order to access the control
43 structure (address stored at the first stack frame) and context' stack is
44 deallocated via the __stack_allocator__. The stack walking makes the destruction
45 of __econtext__ slow and should be prevented if possible.
46
47 __econtext__ expects a __context_fn__ with signature
48 `execution_context(execution_context ctx, Args ... args)`. The parameter `ctx`
49 represents the context from which this context was resumed (e.g. that has called
50 __ec_op__ on `*this`) and `args` are the data passed to __ec_op__. The return
51 value represents the execution_context that has to be resumed, after termiantion
52 of this context.
53
54 Benefits of __ecv2__ over __ecv1__ are: faster context switch, type-safety of
55 passed/returned arguments.
56
57
58 [heading usage of __econtext__]
59
60 int n=35;
61 ctx::execution_context<int> source(
62 [n](ctx::execution_context<int> sink, int) mutable {
63 int a=0;
64 int b=1;
65 while(n-->0){
66 auto result=sink(a);
67 sink=std::move(std::get<0>(result));
68 auto next=a+b;
69 a=b;
70 b=next;
71 }
72 return sink;
73 });
74 for(int i=0;i<10;++i){
75 auto result=source(i);
76 source=std::move(std::get<0>(result));
77 std::cout<<std::get<1>(result)<<" ";
78 }
79
80 output:
81 0 1 1 2 3 5 8 13 21 34
82
83 This simple example demonstrates the basic usage of __econtext__ as a generator.
84 The context `sink` represents the ['main]-context (function ['main()] running).
85 `sink` is generated by the framework (first element of lambda's parameter list).
86 Because the state is invalidated (== changed) by each call of __ec_op__, the new
87 state of the __econtext__, returned by __ec_op__, needs to be assigned to `sink`
88 after each call.
89
90 The lambda that calculates the Fibonacci numbers is executed inside
91 the context represented by `source`. Calculated Fibonacci numbers are
92 transferred between the two context' via expression ['sink(a)] (and returned by
93 ['source()]). Note that this example represents a ['generator] thus the value
94 transferred into the lambda via ['source()] is not used. Using
95 ['boost::optional<>] as transferred type, might also appropriate to express this
96 fact.
97
98 The locale variables `a`, `b` and ` next` remain their values during each
99 context switch (['yield(a)]). This is possible due `source` has its own stack
100 and the stack is exchanged by each context switch.
101
102
103 [heading parameter passing]
104 With `execution_context<void>` no data will be transferred, only the context
105 switch is executed.
106
107 boost::context::execution_context<void> ctx1([](boost::context::execution_context<void> ctx2){
108 std::printf("inside ctx1\n");
109 return ctx2();
110 });
111 ctx1();
112
113 output:
114 inside ctx1
115
116 `ctx1()` resumes `ctx1`, e.g. the lambda passed at the constructor of `ctx1` is
117 entered. Argument `ctx2` represents the context that has been suspended with the
118 invocation of `ctx1()`. When the lambda returns `ctx2`, context `ctx1` will be
119 terminated while the context represented by `ctx2` is resumed, hence the control
120 of execution returns from `ctx1()`.
121
122 The arguments passed to __ec_op__, in one context, is passed as the last
123 arguments of the __context_fn__ if the context is started for the first time.
124 In all following invocations of __ec_op__ the arguments passed to __ec_op__, in
125 one context, is returned by __ec_op__ in the other context.
126
127 boost::context::execution_context<int> ctx1([](boost::context::execution_context<int> ctx2, int j){
128 std::printf("inside ctx1, j == %d\n", j);
129 return ctx2(j+1);
130 });
131 int i = 1;
132 std::tie(ctx1, i) = ctx1(i);
133 std::printf("i == %d\n", i);
134
135 output:
136 inside ctx1, j == 1
137 i == 2
138
139 `ctx1(i)` enters the lambda in context `ctx1` with argument `j=1`. The
140 expression `ctx2(j+1)` resumes the context represented by `ctx2` and transfers
141 back an integer of `j+1`. On return of `ctx1(i)`, the variable `i` contains the
142 value of `j+1`.
143
144 If more than one argument has to be transferred, the signature of the
145 context-function is simply extended.
146
147 boost::context::execution_context<int,int> ctx1([](boost::context::execution_context<int,int> ctx2, int i, int j){
148 std::printf("inside ctx1, i == %d j == %d\n", i, j);
149 return ctx2(i+j,i-j);
150 });
151 int i = 2, j = 1;
152 std::tie(ctx1, i, j) = ctx1(i,j);
153 std::printf("i == %d j == %d\n", i, j);
154
155 output:
156 inside ctx1, i == 2 j == 1
157 i == 3 j == 1
158
159 For use-cases, that require to transfer data of different type in each
160 direction, ['boost::variant<>] could be used.
161
162 class X{
163 private:
164 std::exception_ptr excptr_;
165 boost::context::execution_context<boost::variant<int,std::string>> ctx_;
166
167 public:
168 X():
169 excptr_(),
170 ctx_(
171 [=](boost::context::execution_context<boost::variant<int,std::string>> ctx, boost::variant<int,std::string> data){
172 try {
173 for (;;) {
174 int i = boost::get<int>(data);
175 data = boost::lexical_cast<std::string>(i);
176 auto result = ctx( data);
177 ctx = std::move( std::get<0>( result) );
178 data = std::get<1>( result);
179 } catch (std::bad_cast const&) {
180 excptr_=std::current_exception();
181 }
182 return ctx;
183 })
184 {}
185
186 std::string operator()(int i){
187 boost::variant<int,std::string> data = i;
188 auto result = ctx_( data);
189 ctx_ = std::move( std::get<0>( result) );
190 data = std::get<1>( result);
191 if(excptr_){
192 std::rethrow_exception(excptr_);
193 }
194 return boost::get<std::string>(data);
195 }
196 };
197
198 X x;
199 std::cout << x( 7) << std::endl;
200
201 output:
202 7
203
204 In the case of unidirectional transfer of data, ['boost::optional<>] or a
205 pointer are appropriate.
206
207
208 [heading exception handling]
209 If the function executed inside a __econtext__ emits ans exception, the
210 application is terminated by calling ['std::terminate()]. ['std::exception_ptr]
211 can be used to transfer exceptions between different execution contexts.
212
213 [important Do not jump from inside a catch block and then re-throw the exception
214 in another execution context.]
215
216 [#ecv2_ontop]
217 [heading Executing function on top of a context]
218 Sometimes it is useful to execute a new function on top of a resumed context.
219 For this purpose __ec_op__ with first argument `exec_ontop_arg` has to be used.
220 The function passed as argument must return a tuple of execution_context and
221 arguments.
222
223 boost::context::execution_context<int> f1(boost::context::execution_context<int> ctx,int data) {
224 std::cout << "f1: entered first time: " << data << std::endl;
225 std::tie(ctx,data) = ctx(data+1);
226 std::cout << "f1: entered second time: " << data << std::endl;
227 std::tie(ctx,data) = ctx(data+1);
228 std::cout << "f1: entered third time: " << data << std::endl;
229 return ctx;
230 }
231
232 std::tuple<boost::context::execution_context<int>,int> f2(boost::context::execution_context<int> ctx,int data) {
233 std::cout << "f2: entered: " << data << std::endl;
234 return std::make_tuple(std::move(ctx),-1);
235 }
236
237 int data = 0;
238 ctx::execution_context< int > ctx(f1);
239 std::tie(ctx,data) = ctx(data+1);
240 std::cout << "f1: returned first time: " << data << std::endl;
241 std::tie(ctx,data) = ctx(data+1);
242 std::cout << "f1: returned second time: " << data << std::endl;
243 std::tie(ctx,data) = ctx(ctx::exec_ontop_arg,f2,data+1);
244
245 output:
246 f1: entered first time: 1
247 f1: returned first time: 2
248 f1: entered second time: 3
249 f1: returned second time: 4
250 f2: entered: 5
251 f1: entered third time: -1
252
253 The expression `ctx(ctx::exec_ontop_arg,f2,data+1)` executes `f2()` on top of
254 context `ctx`, e.g. an additional stack frame is allocated on top of the context
255 stack (in front of `f1()`). `f2()` returns argument `-1` that will returned by
256 the second invocation of `ctx(data+1)` in `f1()`.
257
258 Another option is to execute a function on top of the context that throws an
259 exception.
260
261 struct interrupt {
262 boost::context::execution_context< void > ctx;
263
264 interrupt( boost::context::execution_context< void > && ctx_) :
265 ctx( std::forward< boost::context::execution_context< void > >( ctx_) ) {
266 }
267 };
268
269 boost::context::execution_context<void> f1(boost::context::execution_context<void> ctx) {
270 try {
271 for (;;) {
272 std::cout << "f1()" << std::endl;
273 ctx = ctx();
274 }
275 } catch (interrupt & e) {
276 std::cout << "f1(): interrupted" << std::endl;
277 ctx = std::move( e.ctx);
278 }
279 return ctx;
280 }
281
282 boost::context::execution_context<void> f2(boost::context::execution_context<void> ctx) {
283 throw interrupt(std::move(ctx));
284 return ctx;
285 }
286
287 boost::context::execution_context< void > ctx(f1);
288 ctx = ctx();
289 ctx = ctx();
290 ctx = ctx(boost::context::exec_ontop_arg,f2);
291
292 output:
293 f1()
294 f1()
295 f1(): interrupted
296
297 In this example `f2()` is used to interrupt the `for`-loop in `f1()`.
298
299
300 [heading stack unwinding]
301 On construction of __econtext__ a stack is allocated.
302 If the __context_fn__ returns the stack will be destructed.
303 If the __context_fn__ has not yet returned and the destructor of an valid
304 __econtext__ instance (e.g. ['execution_context::operator bool()] returns
305 `true`) is called, the stack will be destructed too.
306
307 [important Code executed by __context_fn__ must not prevent the propagation of the
308 __forced_unwind__ exception. Absorbing that exception will cause stack
309 unwinding to fail. Thus, any code that catches all exceptions must re-throw any
310 pending __forced_unwind__ exception.]
311
312
313 [#ecv2_prealloc]
314 [heading allocating control structures on top of stack]
315 Allocating control structures on top of the stack requires to allocated the
316 __stack_context__ and create the control structure with placement new before
317 __econtext__ is created.
318 [note The user is responsible for destructing the control structure at the top
319 of the stack.]
320
321 // stack-allocator used for (de-)allocating stack
322 fixedsize_stack salloc( 4048);
323 // allocate stack space
324 stack_context sctx( salloc.allocate() );
325 // reserve space for control structure on top of the stack
326 void * sp = static_cast< char * >( sctx.sp) - sizeof( my_control_structure);
327 std::size_t size = sctx.size - sizeof( my_control_structure);
328 // placement new creates control structure on reserved space
329 my_control_structure * cs = new ( sp) my_control_structure( sp, size, sctx, salloc);
330 ...
331 // destructing the control structure
332 cs->~my_control_structure();
333 ...
334 struct my_control_structure {
335 // captured context
336 execution_context cctx;
337
338 template< typename StackAllocator >
339 my_control_structure( void * sp, std::size_t size, stack_context sctx, StackAllocator salloc) :
340 // create captured context
341 cctx( std::allocator_arg, preallocated( sp, size, sctx), salloc, entry_func) {
342 }
343 ...
344 };
345
346
347 [heading inverting the control flow]
348
349 /*
350 * grammar:
351 * P ---> E '\0'
352 * E ---> T {('+'|'-') T}
353 * T ---> S {('*'|'/') S}
354 * S ---> digit | '(' E ')'
355 */
356 class Parser{
357 // implementation omitted; see examples directory
358 };
359
360 std::istringstream is("1+1");
361 bool done=false;
362 std::exception_ptr except;
363
364 // execute parser in new execution context
365 boost::context::execution_context<char> source(
366 [&is,&done,&except](ctx::execution_context<char> sink,char){
367 // create parser with callback function
368 Parser p( is,
369 [&sink](char ch){
370 // resume main execution context
371 auto result = sink(ch);
372 sink = std::move(std::get<0>(result));
373 });
374 try {
375 // start recursive parsing
376 p.run();
377 } catch (...) {
378 // store other exceptions in exception-pointer
379 except = std::current_exception();
380 }
381 // set termination flag
382 done=true;
383 // resume main execution context
384 return sink;
385 });
386
387 // user-code pulls parsed data from parser
388 // invert control flow
389 auto result = source('\0');
390 source = std::move(std::get<0>(result));
391 char c = std::get<1>(result);
392 if ( except) {
393 std::rethrow_exception(except);
394 }
395 while( ! done) {
396 printf("Parsed: %c\n",c);
397 std::tie(source,c) = source('\0');
398 if (except) {
399 std::rethrow_exception(except);
400 }
401 }
402
403 output:
404 Parsed: 1
405 Parsed: +
406 Parsed: 1
407
408 In this example a recursive descent parser uses a callback to emit a newly
409 passed symbol. Using __econtext__ the control flow can be inverted, e.g. the
410 user-code pulls parsed symbols from the parser - instead to get pushed from the
411 parser (via callback).
412
413 The data (character) is transferred between the two __econtext__.
414
415 If the code executed by __econtext__ emits an exception, the application is
416 terminated. ['std::exception_ptr] can be used to transfer exceptions between
417 different execution contexts.
418
419 Sometimes it is necessary to unwind the stack of an unfinished context to
420 destroy local stack variables so they can release allocated resources (RAII
421 pattern). The user is responsible for this task.
422
423
424 [heading Class `execution_context`]
425
426 struct exec_ontop_arg_t {};
427 const exec_ontop_arg_t exec_ontop_arg{};
428
429 template< typename ... Args >
430 class execution_context {
431 public:
432 template< typename Fn, typename ... Params >
433 execution_context( Fn && fn, Params && ... params);
434
435 template< typename StackAlloc, typename Fn, typename ... Params >
436 execution_context( std::allocator_arg_t, StackAlloc salloc, Fn && fn, Params && ... params);
437
438 template< typename StackAlloc, typename Fn, typename ... Params >
439 execution_context( std::allocator_arg_t, preallocated palloc, StackAlloc salloc, Fn && fn, Params && ... params);
440
441 template< typename Fn, typename ... Params >
442 execution_context( std::allocator_arg_t, segemented_stack, Fn && fn, Params && ... params) = delete;
443
444 template< typename Fn, typename ... Params >
445 execution_context( std::allocator_arg_t, preallocated palloc, segmented, Fn && fn, Params && ... params)= delete;
446
447 ~execution_context();
448
449 execution_context( execution_context && other) noexcept;
450 execution_context & operator=( execution_context && other) noexcept;
451
452 execution_context( execution_context const& other) noexcept = delete;
453 execution_context & operator=( execution_context const& other) noexcept = delete;
454
455 explicit operator bool() const noexcept;
456 bool operator!() const noexcept;
457
458 std::tuple< execution_context, Args ... > operator()( Args ... args);
459
460 template< typename Fn >
461 std::tuple< execution_context, Args ... > operator()( exec_ontop_arg_t, Fn && fn, Args ... args);
462
463 bool operator==( execution_context const& other) const noexcept;
464
465 bool operator!=( execution_context const& other) const noexcept;
466
467 bool operator<( execution_context const& other) const noexcept;
468
469 bool operator>( execution_context const& other) const noexcept;
470
471 bool operator<=( execution_context const& other) const noexcept;
472
473 bool operator>=( execution_context const& other) const noexcept;
474
475 template< typename charT, class traitsT >
476 friend std::basic_ostream< charT, traitsT > &
477 operator<<( std::basic_ostream< charT, traitsT > & os, execution_context const& other);
478 };
479
480 [constructor_heading ecv2..constructor]
481
482 template< typename Fn, typename ... Params >
483 execution_context( Fn && fn, Params && ... params);
484
485 template< typename StackAlloc, typename Fn, typename ... Params >
486 execution_context( std::allocator_arg_t, StackAlloc salloc, Fn && fn, Params && ... params);
487
488 template< typename StackAlloc, typename Fn, typename ... Params >
489 execution_context( std::allocator_arg_t, preallocated palloc, StackAlloc salloc, Fn && fn, Params && ... params);
490
491 [variablelist
492 [[Effects:] [Creates a new execution context and prepares the context to execute
493 `fn`. `fixedsize_stack` is used as default stack allocator
494 (stack size == fixedsize_stack::traits::default_size()).
495 The constructor with argument type `preallocated`, is used to create a user
496 defined data [link ecv2_prealloc (for instance additional control structures)] on
497 top of the stack.]]
498 ]
499 [destructor_heading ecv2..destructor destructor]
500
501 ~execution_context();
502
503 [variablelist
504 [[Effects:] [Destructs the associated stack if `*this` is a valid context,
505 e.g. ['execution_context::operator bool()] returns `true`.]]
506 [[Throws:] [Nothing.]]
507 ]
508
509 [move_constructor_heading ecv2..move constructor]
510
511 execution_context( execution_context && other) noexcept;
512
513 [variablelist
514 [[Effects:] [Moves underlying capture record to `*this`.]]
515 [[Throws:] [Nothing.]]
516 ]
517
518 [move_assignment_heading ecv2..move assignment]
519
520 execution_context & operator=( execution_context && other) noexcept;
521
522 [variablelist
523 [[Effects:] [Moves the state of `other` to `*this` using move semantics.]]
524 [[Throws:] [Nothing.]]
525 ]
526
527 [operator_heading ecv2..operator_bool..operator bool]
528
529 explicit operator bool() const noexcept;
530
531 [variablelist
532 [[Returns:] [`true` if `*this` points to a capture record.]]
533 [[Throws:] [Nothing.]]
534 ]
535
536 [operator_heading ecv2..operator_not..operator!]
537
538 bool operator!() const noexcept;
539
540 [variablelist
541 [[Returns:] [`true` if `*this` does not point to a capture record.]]
542 [[Throws:] [Nothing.]]
543 ]
544
545 [operator_heading ecv2..operator_call..operator()]
546
547 std::tuple< execution_context< Args ... >, Args ... > operator()( Args ... args); // member of generic execution_context template
548
549 execution_context< void > operator()(); // member of execution_context< void >
550
551 [variablelist
552 [[Effects:] [Stores internally the current context data (stack pointer,
553 instruction pointer, and CPU registers) of the current active context and
554 restores the context data from `*this`, which implies jumping to `*this`'s
555 context.
556 The arguments, `... args`, are passed to the current context to be returned
557 by the most recent call to `execution_context::operator()` in the same thread.]]
558 [[Returns:] [The tuple of execution_context and returned arguments passed to the
559 most recent call to `execution_context::operator()`, if any and a
560 execution_context representing the context that has been suspended.]]
561 [[Note:] [The returned execution_context indicates if the suspended context has
562 terminated (return from context-function) via `bool operator()`. If the returned
563 execution_context has terminated no data are transferred in the returned tuple.]]
564 ]
565
566 [operator_heading ecv2..operator_call_ontop..operator()]
567
568 template< typename Fn >
569 std::tuple< execution_context< Args ... >, Args ... > operator()( exec_ontop_arg_t, Fn && fn, Args ... args); // member of generic execution_context
570
571 template< typename Fn >
572 execution_context< void > operator()( exec_ontop_arg_t, Fn && fn); // member of execution_context< void >
573
574 [variablelist
575 [[Effects:] [Same as __ec_op__. Additionally, function `fn` is executed
576 in the context of `*this` (e.g. the stack frame of `fn` is allocated on
577 stack of `*this`).]]
578 [[Returns:] [The tuple of execution_context and returned arguments passed to the
579 most recent call to `execution_context::operator()`, if any and a
580 execution_context representing the context that has been suspended .]]
581 [[Note:] [The tuple of execution_context and returned arguments from `fn` are
582 passed as arguments to the context-function of resumed context (if the context
583 is entered the first time) or those arguments are returned from
584 `execution_context::operator()` within the resumed context.]]
585 [[Note:] [Function `fn` needs to return a tuple of execution_context and
586 arguments ([link ecv2_ontop see description]).]]
587 [[Note:] [The context calling this function must not be destroyed before the
588 arguments, that will be returned from `fn`, are preserved at least in the stack
589 frame of the resumed context.]]
590 [[Note:] [The returned execution_context indicates if the suspended context has
591 terminated (return from context-function) via `bool operator()`. If the returned
592 execution_context has terminated no data are transferred in the returned tuple.]]
593 ]
594
595 [operator_heading ecv2..operator_equal..operator==]
596
597 bool operator==( execution_context const& other) const noexcept;
598
599 [variablelist
600 [[Returns:] [`true` if `*this` and `other` represent the same execution context,
601 `false` otherwise.]]
602 [[Throws:] [Nothing.]]
603 ]
604
605 [operator_heading ecv2..operator_notequal..operator!=]
606
607 bool operator!=( execution_context const& other) const noexcept;
608
609 [variablelist
610 [[Returns:] [[`! (other == * this)]]]
611 [[Throws:] [Nothing.]]
612 ]
613
614 [operator_heading ecv2..operator_less..operator<]
615
616 bool operator<( execution_context const& other) const noexcept;
617
618 [variablelist
619 [[Returns:] [`true` if `*this != other` is true and the
620 implementation-defined total order of `execution_context` values places `*this`
621 before `other`, false otherwise.]]
622 [[Throws:] [Nothing.]]
623 ]
624
625 [operator_heading ecv2..operator_greater..operator>]
626
627 bool operator>( execution_context const& other) const noexcept;
628
629 [variablelist
630 [[Returns:] [`other < * this`]]
631 [[Throws:] [Nothing.]]
632 ]
633
634 [operator_heading ecv2..operator_lesseq..operator<=]
635
636 bool operator<=( execution_context const& other) const noexcept;
637
638 [variablelist
639 [[Returns:] [`! (other < * this)`]]
640 [[Throws:] [Nothing.]]
641 ]
642
643 [operator_heading ecv2..operator_greatereq..operator>=]
644
645 bool operator>=( execution_context const& other) const noexcept;
646
647 [variablelist
648 [[Returns:] [`! (* this < other)`]]
649 [[Throws:] [Nothing.]]
650 ]
651
652 [hding ecv2_..Non-member function [`operator<<()]]
653
654 template< typename charT, class traitsT >
655 std::basic_ostream< charT, traitsT > &
656 operator<<( std::basic_ostream< charT, traitsT > & os, execution_context const& other);
657
658 [variablelist
659 [[Efects:] [Writes the representation of `other` to stream `os`.]]
660 [[Returns:] [`os`]]
661 ]
662
663
664 [endsect]