summaryrefslogtreecommitdiff
path: root/libs/context/doc/execution_context.qbk
blob: 81535f7aae14507f3f191aa25ce67e98f38f8ad5 (plain)
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
[/
          Copyright Oliver Kowalke 2014.
 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
]

[section:econtext Class execution_context]

[important __econtext__ is supported only by C++14.]

Class __econtext__ encapsulates __fcontext__ and related functions (
__jump_fcontext__ and __make_fcontext__) as well as stack management.
__econtext__ permits access to the current, active context via
`execution_context::current()`.

        /*
         * grammar:
         *   P ---> E '\0'
         *   E ---> T {('+'|'-') T}
         *   T ---> S {('*'|'/') S}
         *   S ---> digit | '(' E ')'
         */
        class Parser{
            // implementation omitted; see examples directory
        };

        int main() {
            std::istringstream is("1+1");
            bool done=false;
            char c;

            // create handle to main execution context
            boost::context::execution_context main_ctx(
                boost::context::execution_context::current() );

            // executes parser in new execution context
            boost::context::execution_context parser_ctx(
                boost::context::fixedsize_stack(),
                [&main_ctx,&is,&c,&done](){
                    // create parser with callback function
                    Parser p(is,
                             [&main_ctx,&c](char ch){
                                 c=ch;
                                 // resume main execution context
                                 main_ctx.resume();
                             });
                    // start recursive parsing
                    p.run();
                    done=true;
                    // return to main execution context
                    main_ctx.resume();
                });

            // user-code pulls parsed data from parser
            // inverted control flow
            parser_ctx.resume();
            do {
                printf("Parsed: %c\n",c);
                parser_ctx.resume();
            } while( ! done);
        }

        output:
            Parsed: 1
            Parsed: +
            Parsed: 1


In this example a recursive descent parser uses a callback to emit a newly passed
symbol. Using __econtext__ 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 interface of __econtext__ does not transfer data. This is not required
because usually sharing data's address (pointer/reference) sufficient.

If the code executed by __econtext__ emits an exception, `std::terminate()` will
be called.

[important Do not jump from inside a catch block and than re-throw exceptions in
another execution context.]

Sometimes it is necessary to unwind the stack of an unfinished context to
destroy local stack variables so they can release allocated resources (RAII
pattern). The user is responsible for this task.

[heading allocating control strutures 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
__econtext__ is created.
[note The user is responsible for destructing the control structure at the top
of the stack.]

        // stack-alloctor 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  {
            // execution context
            execution_context ectx;

            template< typename StackAllocator >
            my_control_structure( void * sp, std::size_t size, stack_context sctx, StackAllocator salloc) :
                // create execution context
                ectx( preallocated( sp, size, sctx), salloc, entry_func) {
            }
            ...
        };

[heading exception handling]
If the function executed inside a __econtext__ emitts ans exception, `std::terminate()` is
called - `std::exception_ptr` can used to store exceptions thrown inside the other context.

[heading parameter passing]
Input and output parameters are transfered via a lambda capture list and references/pointers.

        class X {
        private:
            int * inp_;
            std::string outp_;
            std::exception_ptr excptr_;
            boost::context::execution_context caller_;
            boost::context::execution_context callee_;

        public:
            X() :
                inp_( nullptr),
                outp_(),
                excptr_(),
                caller_( boost::context::execution_context::current() ),
                callee_( boost::context::fixedsize_stack(),
                         [=] () {
                            try {
                                int i = * inp_;
                                outp_ = boost::lexical_cast< std::string >( i);
                                caller_.resume();
                            } catch (...) {
                                excptr_ = std::current_exception();
                            }
                         })
            {}

            std::string operator()( int i) {
                inp_ = & i;
                callee_.resume();
                if ( excptr_) {
                    std::rethrow_exception( excptr_);
                }
                return outp_;
            }
        };

        int main() {
            X x;
            std::cout << x( 7) << std::endl;
            std::cout << "done" << std::endl;
        }


[heading Class `execution_context`]

        class execution_context {
        public:
            static execution_context current() noexcept;

            template< typename StackAlloc, typename Fn >
            execution_context( StackAlloc salloc, Fn && fn);

            template< typename StackAlloc, typename Fn, typename ... Args >
            execution_context( StackAlloc salloc, Fn && fn, Args && ... args);

            template< typename StackAlloc, typename Fn >
            execution_context( preallocated palloc, StackAlloc salloc, Fn && fn);

            template< typename StackAlloc, typename Fn, typename ... Args >
            execution_context( preallocated palloc, StackAlloc salloc, Fn && fn, Args && ... args);

            void resume() noexcept;

            explicit operator bool() const noexcept;

            bool operator!() const noexcept;
        };

[heading `static execution_context current()`]
[variablelist
[[Returns:] [Returns an instance of excution_context pointing to the active
execution context.]]
[[Throws:] [Nothing.]]
]

[heading `template< typename StackAlloc, typname Fn > execution_context( StackAlloc salloc, Fn && fn)`]
[variablelist
[[Effects:] [Creates a new execution context and prepares the context to execute
`fn`.]]
]

[heading `template< typename StackAlloc, typname Fn, typename ... Args > execution_context( StackAlloc salloc, Fn && fn, Args && ... args)`]
[variablelist
[[Effects:] [Creates a new execution context and prepares the context to execute
`fn`.]]
]

[heading `template< typename StackAlloc, typname Fn > execution_context( preallocated palloc, StackAlloc salloc, Fn && fn)`]
[variablelist
[[Effects:] [Creates a new execution context and prepares the context to execute
`fn`. Used to store control structures on top of the stack.]]
]

[heading `template< typename StackAlloc, typname Fn, typename ... Args > execution_context( preallocated palloc, StackAlloc salloc, Fn && fn, Args && ... args)`]
[variablelist
[[Effects:] [Creates a new execution context and prepares the context to execute
`fn`. Used to store control structures on top of the stack.]]
]

[heading `void resume()`]
[variablelist
[[Effects:] [Stores internally the current context data (stack pointer,
instruction pointer, and CPU registers) to the current active context and
restores the context data from `*this`, which implies jumping to `*this`'s
execution context.]]
[[Note:] [The behaviour is undefined if `resume()` is called while `execution_context::current()`
returns `*this` (e.g. resuming an alredy running cotnext).]]
[[Throws:] [Nothing.]]
]

[heading `explicit operator bool() const`]
[variablelist
[[Returns:] [If `*this` refers to an invalid context or the context-function
has returned (completed), the function returns `false`. Otherwise `true`.]]
[[Throws:] [Nothing.]]
]

[heading `bool operator!() const`]
[variablelist
[[Returns:] [If `*this` refers to an invalid context or the context-function
has returned (completed), the function returns `true`. Otherwise `false`.]]
[[Throws:] [Nothing.]]
]


[heading Struct `preallocated`]

        struct preallocated {
            void        *   sp;
            std::size_t     size;
            stack_context   sctx;

            preallocated( void * sp, std:size_t size, stack_allocator sctx) noexcept;
        };

[heading `preallocated( void * sp, std:size_t size, stack_allocator sctx)`]
[variablelist
[[Effects:] [Crreates an object of preallocated.]]
]


[endsect]