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
10Each instance of __fcontext__ represents a context (CPU registers and stack
11space). Together with its related functions __jump_fcontext__ and
12__make_fcontext__ it provides a execution control transfer mechanism similar
13interface like
14[@http://www.kernel.org/doc/man-pages/online/pages/man2/getcontext.2.html
15ucontext_t].
16__fcontext__ and its functions are located in __context_ns__ and the functions
17are declared as extern "C".
18
19[warning If __fcontext__ is used in a multi threaded application, it can migrated
20between 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
30A new context supposed to execute a __context_fn__ (returning void and accepting
31intptr_t as argument) will be created on top of the stack (at 16 byte boundary)
32by 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
46Calling __jump_fcontext__ invokes the __context_fn__ in a newly created context
47complete with registers, flags, stack and instruction pointers.  When control
48should be returned to the original calling context, call __jump_fcontext__.
49The current context information (registers, flags, and stack and instruction
50pointers) is saved and the original context information is restored. Calling
51__jump_fcontext__ again resumes execution in the second context after saving the
52new state of the original context.
53
54        boost::context::fcontext_t fcm,fc1,fc2;
55
56        void f1(intptr_t)
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(intptr_t)
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
91First call of __jump_fcontext__ enters the __context_fn__ `f1()` by starting
92context fc1 (context fcm saves the registers of `main()`). For jumping between
93context's fc1 and fc2 `jump_fcontext()` is called.
94Because 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
98context 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
103cooperative (programmer controls when switch will happen). The kernel is not
104involved in the context switches.]
105
106
107[heading Transfer of data]
108
109The third argument passed to __jump_fcontext__, in one context, is passed as
110the first argument of the __context_fn__ if the context is started for the
111first time.
112In all following invocations of __jump_fcontext__ the intptr_t passed to
113__jump_fcontext__, in one context, is returned by __jump_fcontext__ in the
114other context.
115
116        boost::context::fcontext_t fcm,fc;
117
118        typedef std::pair<int,int> pair_t;
119
120        void f(intptr_t param)
121        {
122            pair_t* p=(pair_t*)param;
123            p=(pair_t*)boost::context::jump_fcontext(&fc,fcm,(intptr_t)(p->first+p->second));
124            boost::context::jump_fcontext(&fc,fcm,(intptr_t)(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,(intptr_t)&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,(intptr_t)&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
147If 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 than re-throw the
151exception in another execution context.]
152
153
154[heading Preserving floating point registers]
155
156Preserving the floating point registers increases the cycle count for a context
157switch (see performance tests).
158The fourth argument of __jump_fcontext__ controls if fpu registers should be
159preserved by the context jump.
160
161[important The use of the fpu controlling argument of __jump_fcontext__ must
162be consistent in the application. Otherwise the behaviour is undefined.]
163
164
165[heading Stack unwinding]
166
167Sometimes it is necessary to unwind the stack of an unfinished context to
168destroy local stack variables so they can release allocated resources (RAII
169pattern). The user is responsible for this task.
170
171
172[heading `fcontext_t` and related functions]
173
174        struct stack_t
175        {
176            void* sp;
177            std::size_t size;
178        };
179
180        typedef <opaque pointer > fcontext_t;
181
182        intptr_t jump_fcontext(fcontext_t* ofc,fcontext_t nfc,intptr_t vp,bool preserve_fpu=true);
183        fcontext_t make_fcontext(void* sp,std::size_t size,void(*fn)(intptr_t));
184
185[heading `sp`]
186[variablelist
187[[Member:] [Pointer to the beginning of the stack (depending of the architecture the stack grows
188downwards or upwards).]]
189]
190
191[heading `size`]
192[variablelist
193[[Member:] [Size of the stack in bytes.]]
194]
195
196[heading `fc_stack`]
197[variablelist
198[[Member:] [Tracks the memory for the context's stack.]]
199]
200
201[heading `intptr_t jump_fcontext(fcontext_t* ofc,fcontext_t nfc,intptr_t p,bool preserve_fpu=true)`]
202[variablelist
203[[Effects:] [Stores the current context data (stack pointer, instruction
204pointer, and CPU registers) to `*ofc` and restores the context data from `nfc`,
205which implies jumping to `nfc`'s execution context. The intptr_t argument, `p`,
206is passed to the current context to be returned by the most recent call to
207`jump_fcontext()` in the same thread. The last argument controls if fpu registers
208have to be preserved.]]
209[[Returns:] [The third pointer argument passed to the most recent call to
210`jump_fcontext()`, if any.]]
211]
212
213[heading `fcontext_t make_fcontext(void* sp,std::size_t size,void(*fn)(intptr_t))`]
214[variablelist
215[[Precondition:] [Stack `sp` and function pointer `fn` are valid (depending on the architecture
216`sp` points to the top or bottom of the stack) and `size` > 0.]]
217[[Effects:] [Creates an fcontext_t on top of the stack and prepares the stack
218to execute the __context_fn__ `fn`.]]
219[[Returns:][Returns a fcontext_t which is placed on the stack.]]
220]
221
222[endsect]
223