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. |
---|