]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
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 | [section:context Struct fcontext_t] | |
9 | ||
10 | Each instance of __fcontext__ represents a context (CPU registers and stack | |
11 | space). Together with its related functions __jump_fcontext__ and | |
12 | __make_fcontext__ it provides a execution control transfer mechanism similar | |
13 | interface like | |
14 | [@http://www.kernel.org/doc/man-pages/online/pages/man2/getcontext.2.html | |
15 | ucontext_t]. | |
16 | __fcontext__ and its functions are located in __context_ns__ and the functions | |
17 | are declared as extern "C". | |
18 | ||
19 | [warning If __fcontext__ is used in a multi threaded application, it can migrated | |
20 | between threads, but must not reference __tls__.] | |
21 | ||
22 | [important The low level API is the part to port to new platforms.] | |
23 | ||
24 | [note If __fls__ is used on Windows, the user is responsible for calling | |
25 | __fls_alloc__, __fls_free__.] | |
26 | ||
27 | ||
28 | [heading Executing a context] | |
29 | ||
30 | A new context supposed to execute a __context_fn__ (returning void and accepting | |
31 | void * as argument) will be created on top of the stack (at 16 byte boundary) | |
32 | by function __make_fcontext__. | |
33 | ||
34 | // context-function | |
35 | void f(intptr); | |
36 | ||
37 | // creates a new stack | |
38 | std::size_t size = 8192; | |
39 | void* sp(std::malloc(size)); | |
40 | ||
41 | // context fc uses f() as context function | |
42 | // fcontext_t is placed on top of context stack | |
43 | // a pointer to fcontext_t is returned | |
44 | fcontext_t fc(make_fcontext(sp,size,f)); | |
45 | ||
46 | Calling __jump_fcontext__ invokes the __context_fn__ in a newly created context | |
47 | complete with registers, flags, stack and instruction pointers. When control | |
48 | should be returned to the original calling context, call __jump_fcontext__. | |
49 | The current context information (registers, flags, and stack and instruction | |
50 | pointers) is saved and the original context information is restored. Calling | |
51 | __jump_fcontext__ again resumes execution in the second context after saving the | |
52 | new state of the original context. | |
53 | ||
54 | boost::context::fcontext_t fcm,fc1,fc2; | |
55 | ||
56 | void f1(void *) | |
57 | { | |
58 | std::cout<<"f1: entered"<<std::endl; | |
59 | std::cout<<"f1: call jump_fcontext( & fc1, fc2, 0)"<< std::endl; | |
60 | boost::context::jump_fcontext(&fc1,fc2,0); | |
61 | std::cout<<"f1: return"<<std::endl; | |
62 | boost::context::jump_fcontext(&fc1,fcm,0); | |
63 | } | |
64 | ||
65 | void f2(void *) | |
66 | { | |
67 | std::cout<<"f2: entered"<<std::endl; | |
68 | std::cout<<"f2: call jump_fcontext( & fc2, fc1, 0)"<<std::endl; | |
69 | boost::context::jump_fcontext(&fc2,fc1,0); | |
70 | BOOST_ASSERT(false&&!"f2: never returns"); | |
71 | } | |
72 | ||
73 | std::size_t size(8192); | |
74 | void* sp1(std::malloc(size)); | |
75 | void* sp2(std::malloc(size)); | |
76 | ||
77 | fc1=boost::context::make_fcontext(sp1,size,f1); | |
78 | fc2=boost::context::make_fcontext(sp2,size,f2); | |
79 | ||
80 | std::cout<<"main: call jump_fcontext( & fcm, fc1, 0)"<<std::endl; | |
81 | boost::context::jump_fcontext(&fcm,fc1,0); | |
82 | ||
83 | output: | |
84 | main: call jump_fcontext( & fcm, fc1, 0) | |
85 | f1: entered | |
86 | f1: call jump_fcontext( & fc1, fc2, 0) | |
87 | f2: entered | |
88 | f2: call jump_fcontext( & fc2, fc1, 0) | |
89 | f1: return | |
90 | ||
91 | First call of __jump_fcontext__ enters the __context_fn__ `f1()` by starting | |
92 | context fc1 (context fcm saves the registers of `main()`). For jumping between | |
93 | context's fc1 and fc2 `jump_fcontext()` is called. | |
94 | Because context fcm is chained to fc1, `main()` is entered (returning from | |
95 | __jump_fcontext__) after context fc1 becomes complete (return from `f1()`). | |
96 | ||
97 | [warning Calling __jump_fcontext__ to the same context from inside the same | |
98 | context results in undefined behaviour.] | |
99 | ||
100 | [important The size of the stack is required to be larger than the size of fcontext_t.] | |
101 | ||
102 | [note In contrast to threads, which are preemtive, __fcontext__ switches are | |
103 | cooperative (programmer controls when switch will happen). The kernel is not | |
104 | involved in the context switches.] | |
105 | ||
106 | ||
107 | [heading Transfer of data] | |
108 | ||
109 | The third argument passed to __jump_fcontext__, in one context, is passed as | |
110 | the first argument of the __context_fn__ if the context is started for the | |
111 | first time. | |
112 | In all following invocations of __jump_fcontext__ the void * passed to | |
113 | __jump_fcontext__, in one context, is returned by __jump_fcontext__ in the | |
114 | other context. | |
115 | ||
116 | boost::context::fcontext_t fcm,fc; | |
117 | ||
118 | typedef std::pair<int,int> pair_t; | |
119 | ||
120 | void f(void * param) | |
121 | { | |
122 | pair_t* p=(pair_t*)param; | |
123 | p=(pair_t*)boost::context::jump_fcontext(&fc,fcm,(void *)(p->first+p->second)); | |
124 | boost::context::jump_fcontext(&fc,fcm,(void *)(p->first+p->second)); | |
125 | } | |
126 | ||
127 | std::size_t size(8192); | |
128 | void* sp(std::malloc(size)); | |
129 | ||
130 | pair_t p(std::make_pair(2,7)); | |
131 | fc=boost::context::make_fcontext(sp,size,f); | |
132 | ||
133 | int res=(int)boost::context::jump_fcontext(&fcm,fc,(void *)&p); | |
134 | std::cout<<p.first<<" + "<<p.second<<" == "<<res<<std::endl; | |
135 | ||
136 | p=std::make_pair(5,6); | |
137 | res=(int)boost::context::jump_fcontext(&fcm,fc,(void *)&p); | |
138 | std::cout<<p.first<<" + "<<p.second<<" == "<<res<<std::endl; | |
139 | ||
140 | output: | |
141 | 2 + 7 == 9 | |
142 | 5 + 6 == 11 | |
143 | ||
144 | ||
145 | [heading Exceptions in __context_fn__] | |
146 | ||
147 | If the __context_fn__ emits an exception, the behaviour is undefined. | |
148 | ||
149 | [important __context_fn__ should wrap the code in a try/catch block.] | |
150 | [important Do not jump from inside a catch block and then re-throw the | |
151 | exception in another execution context.] | |
152 | ||
153 | ||
154 | [heading Stack unwinding] | |
155 | ||
156 | Sometimes it is necessary to unwind the stack of an unfinished context to | |
157 | destroy local stack variables so they can release allocated resources (RAII | |
158 | pattern). The user is responsible for this task. | |
159 | ||
160 | ||
161 | [heading `fcontext_t` and related functions] | |
162 | ||
163 | struct stack_t | |
164 | { | |
165 | void* sp; | |
166 | std::size_t size; | |
167 | }; | |
168 | ||
169 | typedef <opaque pointer > fcontext_t; | |
170 | ||
171 | void * jump_fcontext(fcontext_t* ofc,fcontext_t nfc,void * vp); | |
172 | fcontext_t make_fcontext(void* sp,std::size_t size,void(*fn)(void *)); | |
173 | ||
174 | [heading `sp`] | |
175 | [variablelist | |
176 | [[Member:] [Pointer to the beginning of the stack (depending of the architecture the stack grows | |
177 | downwards or upwards).]] | |
178 | ] | |
179 | ||
180 | [heading `size`] | |
181 | [variablelist | |
182 | [[Member:] [Size of the stack in bytes.]] | |
183 | ] | |
184 | ||
185 | [heading `fc_stack`] | |
186 | [variablelist | |
187 | [[Member:] [Tracks the memory for the context's stack.]] | |
188 | ] | |
189 | ||
190 | [heading `void * jump_fcontext(fcontext_t* ofc,fcontext_t nfc,void * p)`] | |
191 | [variablelist | |
192 | [[Effects:] [Stores the current context data (stack pointer, instruction | |
193 | pointer, and CPU registers) to `*ofc` and restores the context data from `nfc`, | |
194 | which implies jumping to execution context `nfc`. The void * argument, `p`, | |
195 | is passed to the current context to be returned by the most recent call to | |
196 | `jump_fcontext()` in the same thread.]] | |
197 | [[Returns:] [The third pointer argument passed to the most recent call to | |
198 | `jump_fcontext()`, if any.]] | |
199 | ] | |
200 | ||
201 | [heading `fcontext_t make_fcontext(void* sp,std::size_t size,void(*fn)(void *))`] | |
202 | [variablelist | |
203 | [[Precondition:] [Stack `sp` and function pointer `fn` are valid (depending on the architecture | |
204 | `sp` points to the top or bottom of the stack) and `size` > 0.]] | |
205 | [[Effects:] [Creates an fcontext_t on top of the stack and prepares the stack | |
206 | to execute the __context_fn__ `fn`.]] | |
207 | [[Returns:][Returns a fcontext_t which is placed on the stack.]] | |
208 | ] | |
209 | ||
210 | [endsect] |