1 | \chapter{Implementation}
|
---|
2 | % Goes over how all the features are implemented.
|
---|
3 |
|
---|
4 | \section{Virtual System}
|
---|
5 | % Virtual table rules. Virtual tables, the pointer to them and the cast.
|
---|
6 | The \CFA virtual system only has one public facing feature: virtual casts.
|
---|
7 | However there is a lot of structure to support that and provide some other
|
---|
8 | features for the standard library.
|
---|
9 |
|
---|
10 | All of this is accessed through a field inserted at the beginning of every
|
---|
11 | virtual type. Currently it is called \codeC{virtual_table} but it is not
|
---|
12 | ment to be accessed by the user. This field is a pointer to the type's
|
---|
13 | virtual table instance. It is assigned once during the object's construction
|
---|
14 | and left alone after that.
|
---|
15 |
|
---|
16 | \subsection{Virtual Table Construction}
|
---|
17 | For each virtual type a virtual table is constructed. This is both a new type
|
---|
18 | and an instance of that type. Other instances of the type could be created
|
---|
19 | but the system doesn't use them. So this section will go over the creation of
|
---|
20 | the type and the instance.
|
---|
21 |
|
---|
22 | Creating the single instance is actually very important. The address of the
|
---|
23 | table acts as the unique identifier for the virtual type. Similarly the first
|
---|
24 | field in every virtual table is the parent's id; a pointer to the parent
|
---|
25 | virtual table instance.
|
---|
26 |
|
---|
27 | The remaining fields contain the type's virtual members. First come the ones
|
---|
28 | present on the parent type, in the same order as they were the parent, and
|
---|
29 | then any that this type introduces. The types of the ones inherited from the
|
---|
30 | parent may have a slightly modified type, in that references to the
|
---|
31 | dispatched type are replaced with the current virtual type. These are always
|
---|
32 | taken by pointer or reference.
|
---|
33 |
|
---|
34 | The structure itself is created where the virtual type is created. The name
|
---|
35 | of the type is created by mangling the name of the base type. The name of the
|
---|
36 | instance is also generated by name mangling.
|
---|
37 |
|
---|
38 | The fields are initialized automatically.
|
---|
39 | The parent field is initialized by getting the type of the parent field and
|
---|
40 | using that to calculate the mangled name of the parent's virtual table type.
|
---|
41 | There are two special fields that are included like normal fields but have
|
---|
42 | special initialization rules: the \codeC{size} field is the type's size and is
|
---|
43 | initialized with a sizeof expression, the \codeC{align} field is the type's
|
---|
44 | alignment and uses an alignof expression. The remaining fields are resolved
|
---|
45 | to a name matching the field's name and type using the normal visibility
|
---|
46 | and overload resolution rules of the type system.
|
---|
47 |
|
---|
48 | These operations are split up into several groups depending on where they
|
---|
49 | take place which can vary for monomorphic and polymorphic types. The first
|
---|
50 | devision is between the declarations and the definitions. Declarations, such
|
---|
51 | as a function signature or a structure's name, must always be visible but may
|
---|
52 | be repeated so they go in headers. Definitions, such as function bodies and a
|
---|
53 | structure's layout, don't have to be visible on use but must occur exactly
|
---|
54 | once and go into source files.
|
---|
55 |
|
---|
56 | The declarations include the virtual type definition and forward declarations
|
---|
57 | of the virtual table instance, constructor, message function and
|
---|
58 | \codeCFA{get_exception_vtable}. The definition includes the storage and
|
---|
59 | initialization of the virtual table instance and the bodies of the three
|
---|
60 | functions.
|
---|
61 |
|
---|
62 | Monomorphic instances put all of these two groups in one place each.
|
---|
63 |
|
---|
64 | Polymorphic instances also split out the core declarations and definitions
|
---|
65 | from the per-instance information. The virtual table type and most of the
|
---|
66 | functions are polymorphic so they are all part of the core. The virtual table
|
---|
67 | instance and the \codeCFA{get_exception_vtable} function.
|
---|
68 |
|
---|
69 | Coroutines and threads need instances of \codeCFA{CoroutineCancelled} and
|
---|
70 | \codeCFA{ThreadCancelled} respectively to use all of their functionality.
|
---|
71 | When a new data type is declared with \codeCFA{coroutine} or \codeCFA{thread}
|
---|
72 | the forward declaration for the instance is created as well. The definition
|
---|
73 | of the virtual table is created at the definition of the main function.
|
---|
74 |
|
---|
75 | \subsection{Virtual Cast}
|
---|
76 | Virtual casts are implemented as a function call that does the check and a
|
---|
77 | old C-style cast to do the type conversion. The C-cast is just to make sure
|
---|
78 | the generated code is correct so the rest of the section is about that
|
---|
79 | function.
|
---|
80 |
|
---|
81 | The function is \codeC{__cfa__virtual_cast} and it is implemented in the
|
---|
82 | standard library. It takes a pointer to the target type's virtual table and
|
---|
83 | the object pointer being cast. The function is very simple, getting the
|
---|
84 | object's virtual table pointer and then checking to see if it or any of
|
---|
85 | its ancestors, by using the parent pointers, are the same as the target type
|
---|
86 | virtual table pointer. It does this in a simple loop.
|
---|
87 |
|
---|
88 | For the generated code a forward decaration of the virtual works as follows.
|
---|
89 | There is a forward declaration of \codeC{__cfa__virtual_cast} in every cfa
|
---|
90 | file so it can just be used. The object argument is the expression being cast
|
---|
91 | so that is just placed in the argument list.
|
---|
92 |
|
---|
93 | To build the target type parameter the compiler will create a mapping from
|
---|
94 | concrete type-name -- so for polymorphic types the parameters are filled in
|
---|
95 | -- to virtual table address. Every virtual table declaraction is added to the
|
---|
96 | this table; repeats are ignored unless they have conflicting definitions.
|
---|
97 | This does mean the declaractions have to be in scope, but they should usually
|
---|
98 | be introduced as part of the type definition.
|
---|
99 |
|
---|
100 | \section{Exceptions}
|
---|
101 | % Anything about exception construction.
|
---|
102 |
|
---|
103 | \section{Unwinding}
|
---|
104 | % Adapt the unwind chapter, just describe the sections of libunwind used.
|
---|
105 | % Mention that termination and cancellation use it. Maybe go into why
|
---|
106 | % resumption doesn't as well.
|
---|
107 |
|
---|
108 | Many modern languages work with an interal stack that function push and pop
|
---|
109 | their local data to. Stack unwinding removes large sections of the stack,
|
---|
110 | often across functions.
|
---|
111 |
|
---|
112 | At a very basic level this can be done with \codeC{setjmp} \& \codeC{longjmp}
|
---|
113 | which simply move the top of the stack, discarding everything on the stack
|
---|
114 | above a certain point. However this ignores all the clean-up code that should
|
---|
115 | be run when certain sections of the stack are removed (for \CFA these are from
|
---|
116 | destructors and finally clauses) and also requires that the point to which the
|
---|
117 | stack is being unwound is known ahead of time. libunwind is used to address
|
---|
118 | both of these problems.
|
---|
119 |
|
---|
120 | Libunwind, provided in \texttt{unwind.h} on most platorms, is a C library
|
---|
121 | that provides \CPP style stack unwinding. Its operation is divided into two
|
---|
122 | phases. The search phase -- phase 1 -- is used to scan the stack and decide
|
---|
123 | where the unwinding will stop, this allows for a dynamic target. The clean-up
|
---|
124 | phase -- phase 2 -- does the actual unwinding and also runs any clean-up code
|
---|
125 | as it goes.
|
---|
126 |
|
---|
127 | To use the libunwind each function must have a personality function and an
|
---|
128 | LSDA (Language Specific Data Area). Libunwind actually does very little, it
|
---|
129 | simply moves down the stack from function to function. Most of the actions are
|
---|
130 | implemented by the personality function which libunwind calls on every
|
---|
131 | function. Since this is shared across many functions or even every function in
|
---|
132 | a language it will need a bit more information. This is provided by the LSDA
|
---|
133 | which has the unique information for each function.
|
---|
134 |
|
---|
135 | Theoretically the LSDA can contain anything but conventionally it is a table
|
---|
136 | with entries reperenting areas of the function and what has to be done there
|
---|
137 | during unwinding. These areas are described in terms of where the instruction
|
---|
138 | pointer is. If the current value of the instruction pointer is between two
|
---|
139 | values reperenting the beginning and end of a region then execution is
|
---|
140 | currently being executed. These are used to mark out try blocks and the
|
---|
141 | scopes of objects with destructors to run.
|
---|
142 |
|
---|
143 | GCC will generate an LSDA and attach its personality function with the
|
---|
144 | \texttt{-fexceptions} flag. However this only handles the cleanup attribute.
|
---|
145 | This attribute is used on a variable and specifies a function that should be
|
---|
146 | run when the variable goes out of scope. The function is passed a pointer to
|
---|
147 | the object as well so it can be used to mimic destructors. It however cannot
|
---|
148 | be used to mimic try statements.
|
---|
149 |
|
---|
150 | \subsection{Implementing Personality Functions}
|
---|
151 | Personality functions have a complex interface specified by libunwind.
|
---|
152 | This section will cover some of the important parts of that interface.
|
---|
153 |
|
---|
154 | \begin{lstlisting}
|
---|
155 | typedef _Unwind_Reason_Code (*_Unwind_Personality_Fn)(
|
---|
156 | int version,
|
---|
157 | _Unwind_Action action,
|
---|
158 | _Unwind_Exception_Class exception_class,
|
---|
159 | _Unwind_Exception * exception,
|
---|
160 | struct _Unwind_Context * context);
|
---|
161 | \end{lstlisting}
|
---|
162 |
|
---|
163 | The return value, the reason code, is an enumeration of possible messages
|
---|
164 | that can be passed several places in libunwind. It includes a number of
|
---|
165 | messages for special cases (some of which should never be used by the
|
---|
166 | personality function) and error codes but unless otherwise noted the
|
---|
167 | personality function should always return \codeC{_URC_CONTINUE_UNWIND}.
|
---|
168 |
|
---|
169 | The \codeC{version} argument is the verson of the implementation that is
|
---|
170 | calling the personality function. At this point it appears to always be 1 and
|
---|
171 | it will likely stay that way until a new version of the API is updated.
|
---|
172 |
|
---|
173 | The \codeC{action} argument is set of flags that tell the personality
|
---|
174 | function when it is being called and what it must do on this invocation.
|
---|
175 | The flags are as follows:
|
---|
176 | \begin{itemize}
|
---|
177 | \item\codeC{_UA_SEARCH_PHASE}: This flag is set whenever the personality
|
---|
178 | function is called during the search phase. The personality function should
|
---|
179 | decide if unwinding will stop in this function or not. If it does then the
|
---|
180 | personality function should return \codeC{_URC_HANDLER_FOUND}.
|
---|
181 | \item\codeC{_UA_CLEANUP_PHASE}: This flag is set whenever the personality
|
---|
182 | function is called during the cleanup phase. If no other flags are set this
|
---|
183 | means the entire frame will be unwound and all cleanup code should be run.
|
---|
184 | \item\codeC{_UA_HANDLER_FRAME}: This flag is set during the cleanup phase
|
---|
185 | on the function frame that found the handler. The personality function must
|
---|
186 | prepare to return to normal code execution and return
|
---|
187 | \codeC{_URC_INSTALL_CONTEXT}.
|
---|
188 | \item\codeC{_UA_FORCE_UNWIND}: This flag is set if the personality function
|
---|
189 | is called through a forced unwind call. Forced unwind only performs the
|
---|
190 | cleanup phase and uses a different means to decide when to stop. See its
|
---|
191 | section below.
|
---|
192 | \end{itemize}
|
---|
193 |
|
---|
194 | The \codeC{exception_class} argument is a copy of the \codeC{exception}'s
|
---|
195 | \codeC{exception_class} field.
|
---|
196 |
|
---|
197 | The \codeC{exception} argument is a pointer to the user provided storage
|
---|
198 | object. It has two public fields, the exception class which is actually just
|
---|
199 | a number that identifies the exception handling mechanism that created it and
|
---|
200 | the other is the clean-up function. The clean-up function is called if the
|
---|
201 | exception needs to
|
---|
202 |
|
---|
203 | The \codeC{context} argument is a pointer to an opaque type. This is passed
|
---|
204 | to the many helper functions that can be called inside the personality
|
---|
205 | function.
|
---|
206 |
|
---|
207 | \subsection{Raise Exception}
|
---|
208 | This could be considered the central function of libunwind. It preforms the
|
---|
209 | two staged unwinding the library is built around and most of the rest of the
|
---|
210 | interface of libunwind is here to support it. It's signature is as follows:
|
---|
211 |
|
---|
212 | \begin{lstlisting}
|
---|
213 | _Unwind_Reason_Code _Unwind_RaiseException(_Unwind_Exception *);
|
---|
214 | \end{lstlisting}
|
---|
215 |
|
---|
216 | When called the function begins the search phase, calling the personality
|
---|
217 | function of the most recent stack frame. It will continue to call personality
|
---|
218 | functions traversing the stack new-to-old until a function finds a handler or
|
---|
219 | the end of the stack is reached. In the latter case raise exception will
|
---|
220 | return with \codeC{_URC_END_OF_STACK}.
|
---|
221 |
|
---|
222 | Once a handler has been found raise exception continues onto the the cleanup
|
---|
223 | phase. Once again it will call the personality functins of each stack frame
|
---|
224 | from newest to oldest. This pass will stop at the stack frame that found the
|
---|
225 | handler last time, if that personality function does not install the handler
|
---|
226 | it is an error.
|
---|
227 |
|
---|
228 | If an error is encountered raise exception will return either
|
---|
229 | \codeC{_URC_FATAL_PHASE1_ERROR} or \codeC{_URC_FATAL_PHASE2_ERROR} depending
|
---|
230 | on when the error occured.
|
---|
231 |
|
---|
232 | \subsection{Forced Unwind}
|
---|
233 | This is the second big function in libunwind. It also unwinds a stack but it
|
---|
234 | does not use the search phase. Instead another function, the stop function,
|
---|
235 | is used to decide when to stop.
|
---|
236 |
|
---|
237 | \begin{lstlisting}
|
---|
238 | _Unwind_Reason_Code _Unwind_ForcedUnwind(
|
---|
239 | _Unwind_Exception *, _Unwind_Stop_Fn, void *);
|
---|
240 | \end{lstlisting}
|
---|
241 |
|
---|
242 | The exception is the same as the one passed to raise exception. The extra
|
---|
243 | arguments are the stop function and the stop parameter. The stop function has
|
---|
244 | a similar interface as a personality function, except it is also passed the
|
---|
245 | stop parameter.
|
---|
246 |
|
---|
247 | \begin{lstlisting}
|
---|
248 | typedef _Unwind_Reason_Code (*_Unwind_Stop_Fn)(
|
---|
249 | int version,
|
---|
250 | _Unwind_Action action,
|
---|
251 | _Unwind_Exception_Class exception_class,
|
---|
252 | _Unwind_Exception * exception,
|
---|
253 | struct _Unwind_Context * context,
|
---|
254 | void * stop_parameter);
|
---|
255 | \end{lstlisting}
|
---|
256 |
|
---|
257 | The stop function is called at every stack frame before the personality
|
---|
258 | function is called and then once more once after all frames of the stack have
|
---|
259 | been unwound.
|
---|
260 |
|
---|
261 | Each time it is called the stop function should return \codeC{_URC_NO_REASON}
|
---|
262 | or transfer control directly to other code outside of libunwind. The
|
---|
263 | framework does not provide any assistance here.
|
---|
264 |
|
---|
265 | Its arguments are the same as the paired personality function.
|
---|
266 | The actions \codeC{_UA_CLEANUP_PHASE} and \codeC{_UA_FORCE_UNWIND} are always
|
---|
267 | set when it is called. By the official standard that is all but both GCC and
|
---|
268 | Clang add an extra action on the last call at the end of the stack:
|
---|
269 | \codeC{_UA_END_OF_STACK}.
|
---|
270 |
|
---|
271 | \section{Exception Context}
|
---|
272 | % Should I have another independent section?
|
---|
273 | % There are only two things in it, top_resume and current_exception. How it is
|
---|
274 | % stored changes depending on wheither or not the thread-library is linked.
|
---|
275 |
|
---|
276 | The exception context is a piece of global storage used to maintain data
|
---|
277 | across different exception operations and to communicate between different
|
---|
278 | components.
|
---|
279 |
|
---|
280 | Each stack has its own exception context. In a purely sequental program, using
|
---|
281 | only core Cforall, there is only one stack and the context is global. However
|
---|
282 | if the library \texttt{libcfathread} is linked then there can be multiple
|
---|
283 | stacks so they will each need their own.
|
---|
284 |
|
---|
285 | To handle this code always gets the exception context from the function
|
---|
286 | \codeC{this_exception_context}. The main exception handling code is in
|
---|
287 | \texttt{libcfa} and that library also defines the function as a weak symbol
|
---|
288 | so it acts as a default. Meanwhile in \texttt{libcfathread} the function is
|
---|
289 | defined as a strong symbol that replaces it when the libraries are linked
|
---|
290 | together.
|
---|
291 |
|
---|
292 | The version of the function defined in \texttt{libcfa} is very simple. It
|
---|
293 | returns a pointer to a global static variable. With only one stack this
|
---|
294 | global instance is associated with the only stack.
|
---|
295 |
|
---|
296 | The version of the function defined in \texttt{libcfathread} has to handle
|
---|
297 | more as there are multiple stacks. The exception context is included as
|
---|
298 | part of the per-stack data stored as part of coroutines. In the cold data
|
---|
299 | section, stored at the base of each stack, is the exception context for that
|
---|
300 | stack. The \codeC{this_exception_context} uses the concurrency library to get
|
---|
301 | the current coroutine and through it the cold data section and the exception
|
---|
302 | context.
|
---|
303 |
|
---|
304 | \section{Termination}
|
---|
305 | % Memory management & extra information, the custom function used to implement
|
---|
306 | % catches. Talk about GCC nested functions.
|
---|
307 |
|
---|
308 | Termination exceptions use libunwind quite heavily because it matches the
|
---|
309 | intended use from \CPP exceptions very closely. The main complication is that
|
---|
310 | since the \CFA compiler works by translating to C code it cannot generate the
|
---|
311 | assembly to form the LSDA for try blocks or destructors.
|
---|
312 |
|
---|
313 | \subsection{Memory Management}
|
---|
314 | The first step of termination is to copy the exception into memory managed by
|
---|
315 | the exception system. Currently the system just uses malloc, without reserved
|
---|
316 | memory or and ``small allocation" optimizations. The exception handling
|
---|
317 | mechanism manages memory for the exception as well as memory for libunwind
|
---|
318 | and the system's own per-exception storage.
|
---|
319 |
|
---|
320 | Exceptions are stored in variable sized block. The first component is a fixed
|
---|
321 | sized data structure that contains the information for libunwind and the
|
---|
322 | exception system. The second component is a blob of memory that is big enough
|
---|
323 | to store the exception. Macros with pointer arthritic and type cast are
|
---|
324 | used to move between the components or go from the embedded
|
---|
325 | \codeC{_Unwind_Exception} to the entire node.
|
---|
326 |
|
---|
327 | All of these nodes are strung together in a linked list. One linked list per
|
---|
328 | stack, with the head stored in the exception context. Within each linked list
|
---|
329 | the most recently thrown exception is at the head and the older exceptions
|
---|
330 | are further down the list. This list format allows exceptions to be thrown
|
---|
331 | while a different exception is being handled. Only the exception at the head
|
---|
332 | of the list is currently being handled, the other will wait for the
|
---|
333 | exceptions before them to be removed.
|
---|
334 |
|
---|
335 | The virtual members in the exception's virtual table. The size of the
|
---|
336 | exception, the copy function and the free function are all in the virtual
|
---|
337 | table so they are decided per-exception type. The size and copy function are
|
---|
338 | used right away when the exception is copied in to managed memory. After the
|
---|
339 | exception is handled the free function is used to clean up the exception and
|
---|
340 | then the entire node is passed to free.
|
---|
341 |
|
---|
342 | \subsection{Try Statements \& Catch Clauses}
|
---|
343 | The try statements with termination handlers have a pretty complex conversion
|
---|
344 | to compensate for the lack of assembly generation. Libunwind requires an LSDA
|
---|
345 | (Language Specific Data Area) and personality function for a function to
|
---|
346 | unwind across it. The LSDA in particular is hard to generate at the level of
|
---|
347 | C which is what the \CFA compiler outputs so a work-around is used.
|
---|
348 |
|
---|
349 | This work around is a function called \codeC{__cfaehm_try_terminate} in the
|
---|
350 | standard library. The contents of a try block and the termination handlers
|
---|
351 | are converted into functions. These are then passed to the try terminate
|
---|
352 | function and it calls them. This puts the try statements in their own
|
---|
353 | functions so that no function has to deal with both termination handlers and
|
---|
354 | destructors.
|
---|
355 |
|
---|
356 | This function has some custom embedded assembly that defines its personality
|
---|
357 | function and LSDA. This is hand coded in C which is why there is only one
|
---|
358 | version of it, the compiler has no capability to generate it. The personality
|
---|
359 | function is structured so that it may be expanded, but really it only handles
|
---|
360 | this one function. Notably it does not handle any destructors so the function
|
---|
361 | is constructed so that it does need to run it.
|
---|
362 |
|
---|
363 | The three functions passed to try terminate are:
|
---|
364 | \begin{itemize}
|
---|
365 | \item The try function: This function is the try block, all the code inside
|
---|
366 | the try block is placed inside the try function. It takes no parameters and
|
---|
367 | has no return value. This function is called during regular execution to run
|
---|
368 | the try block.
|
---|
369 | \item The match function: This function decides if this try statement should
|
---|
370 | handle any given termination exception. It takes a pointer to the exception
|
---|
371 | and returns 0 if the exception is not handled here. Otherwise the return value
|
---|
372 | is the id of the handler that should handle the exception. It is called
|
---|
373 | during the search phase.
|
---|
374 | It is constructed from the conditional part of each handler. It runs each
|
---|
375 | check in turn, first checking to see if the object
|
---|
376 | \item The catch function: This function handles the exception. It takes a
|
---|
377 | pointer to the exception and the handler's id and returns nothing. It is
|
---|
378 | called after the clean-up phase.
|
---|
379 | It is constructed by stitching together the bodies of each handler
|
---|
380 | \end{itemize}
|
---|
381 | All three are created with GCC nested functions. GCC nested functions can be
|
---|
382 | used to create closures, functions that can refer to the state of other
|
---|
383 | functions on the stack. This allows the functions to refer to the main
|
---|
384 | function and all the variables in scope.
|
---|
385 |
|
---|
386 | These nested functions and all other functions besides
|
---|
387 | \codeC{__cfaehm_try_terminate} in \CFA use the GCC personality function and
|
---|
388 | the \texttt{-fexceptions} flag to generate the LSDA. This allows destructors
|
---|
389 | to be implemented with the cleanup attribute.
|
---|
390 |
|
---|
391 | \section{Resumption}
|
---|
392 | % The stack-local data, the linked list of nodes.
|
---|
393 |
|
---|
394 | Resumption uses a list of nodes for its stack traversal. The head of the list
|
---|
395 | is stored in the exception context. The nodes in the list just have a pointer
|
---|
396 | to the next node and a pointer to the handler function.
|
---|
397 |
|
---|
398 | The on a resumption throw the this list is traversed. At each node the
|
---|
399 | handler function is called and is passed the exception by pointer. It returns
|
---|
400 | true if the exception was handled and false otherwise.
|
---|
401 |
|
---|
402 | The handler function does both the matching and catching. It tries each
|
---|
403 | the condition of \codeCFA{catchResume} in order, top-to-bottom and until it
|
---|
404 | finds a handler that matches. If no handler matches then the function returns
|
---|
405 | false. Otherwise the matching handler is run, if it completes successfully
|
---|
406 | the function returns true. Rethrows, through the \codeCFA{throwResume;}
|
---|
407 | statement, cause the function to return true.
|
---|
408 |
|
---|
409 | \subsection{Libunwind Compatibility}
|
---|
410 | Resumption does not use libunwind for two simple reasons. The first is that
|
---|
411 | it does not have to unwind anything so would never need to use the clean-up
|
---|
412 | phase. Still the search phase could be used to make it free to enter or exit
|
---|
413 | a try statement with resumption handlers in the same way termination handlers
|
---|
414 | are for the same trade off in the cost of the throw. This is where the second
|
---|
415 | reason comes in, there is no way to return from a search without installing
|
---|
416 | a handler or raising an error.
|
---|
417 |
|
---|
418 | Although work arounds could be created none seemed to be worth it for the
|
---|
419 | prototype. This implementation has no difference in behaviour and is much
|
---|
420 | simpler.
|
---|
421 | % Seriously, just compare the size of the two chapters and then consider
|
---|
422 | % that unwind is required knowledge for that chapter.
|
---|
423 |
|
---|
424 | \section{Finally}
|
---|
425 | % Uses destructors and GCC nested functions.
|
---|
426 | Finally clauses are a simple decomposition to some of the existing features.
|
---|
427 | The code in the block is placed into a GCC nested function with a unique name,
|
---|
428 | no arguments or return values. This nested function is then set as the
|
---|
429 | clean-up function of an empty object that is declared at the beginning of a
|
---|
430 | block placed around the contexts of the try statement.
|
---|
431 |
|
---|
432 | The rest is handled by GCC. The try block and all handlers are inside the
|
---|
433 | block. When they are complete control exits the block and the empty object
|
---|
434 | is cleaned up, which runs the function that contains the finally code.
|
---|
435 |
|
---|
436 | \section{Cancellation}
|
---|
437 | % Stack selections, the three internal unwind functions.
|
---|
438 |
|
---|
439 | Cancellation also uses libunwind to do its stack traversal and unwinding,
|
---|
440 | however it uses a different primary function \codeC{_Unwind_ForcedUnwind}.
|
---|
441 | Details of its interface can be found in the unwind section.
|
---|
442 |
|
---|
443 | The first step of cancellation is to find the stack was cancelled and which
|
---|
444 | type of stack it is. Luckily the threads library stores the main thread
|
---|
445 | pointer and the current thread pointer and every thread stores a pointer to
|
---|
446 | its main coroutine and the coroutine it is currently executing.
|
---|
447 |
|
---|
448 | So if the the current thread's main and current coroutine do not match, it is
|
---|
449 | a coroutine cancellation. Otherwise if the main and current thread do not
|
---|
450 | match, it is a thread cancellation. Otherwise it is a main thread
|
---|
451 | cancellation.
|
---|
452 |
|
---|
453 | However if the threading library is not linked then execution must be on the
|
---|
454 | main stack as that is the only one that exists. So the entire check is skipped
|
---|
455 | using the linker and weak symbols. Instead the main thread cancellation is
|
---|
456 | unconditionally preformed.
|
---|
457 |
|
---|
458 | Regardless of how they are choosen afterwords the stop function and the stop
|
---|
459 | parameter are passed to the forced unwind functon. The general pattern of all
|
---|
460 | three stop functions is the same, they continue unwinding until the end of
|
---|
461 | stack when they do there primary work.
|
---|
462 |
|
---|
463 | Main stack cancellation it is very simple. The ``transfer" is just an abort,
|
---|
464 | the program stops executing.
|
---|
465 |
|
---|
466 | The coroutine cancellation stores the exception on the coroutine and then
|
---|
467 | does a coroutine context switch. The rest is handled inside resume. Every time
|
---|
468 | control returns from a resumed thread there is a check to see if it is
|
---|
469 | cancelled. If it is the exception is retrieved and the CoroutineCancelled
|
---|
470 | exception is constructed and loaded. It is then thrown as a regular exception
|
---|
471 | with the default handler coming from the context of the resumption call.
|
---|
472 |
|
---|
473 | The thread cancellation stores the exception on the thread's main stack and
|
---|
474 | then returns to the scheduler. The rest is handled by the joiner. The wait
|
---|
475 | for the joined thread to finish works the same but after that it checks
|
---|
476 | to see if there was a cancellation. If there was the exception is retrieved
|
---|
477 | and the ThreadCancelled exception is constructed. The default handler is
|
---|
478 | passed in as a function pointer. If it is null (as it is for the
|
---|
479 | auto-generated joins on destructor call) it a default is used that simply
|
---|
480 | calls abort; which gives the required handling on implicate join.
|
---|