\chapter{Implementation} \label{c:implement} The implementation work for this thesis covers the two components: virtual system and exceptions. Each component is discussed in detail. \section{Virtual System} \label{s:VirtualSystem} % Virtual table rules. Virtual tables, the pointer to them and the cast. While the \CFA virtual system currently has only one public feature, virtual cast (see the virtual cast feature \vpageref{p:VirtualCast}), substantial structure is required to support it, and provide features for exception handling and the standard library. \subsection{Virtual Type} Virtual types only have one change to their structure: the addition of a pointer to the virtual table, which is called the \emph{virtual-table pointer}. Internally, the field is called \snake{virtual_table}. The field is fixed after construction and is the first field in the structure so that its location is always known. \todo{Talk about constructors for virtual types (after they are working).} The virtual-table pointer is what binds an instance of a virtual type to its virtual table. This pointer is used as an identity check, and to access the virtual table and the virtual members there. \subsection{Type Id} Every virtual type has a unique id. Type ids can be compared for equality (\ie the types represented are the same) or used to access the type's type information. The type information currently is only the parent's type id or, if the type has no parent, @0p@. The id's are implemented as pointers to the type's type information instance. Dereferencing the pointer gets the type information. The ancestors of a virtual type are found by traversing the type id through the type information. An id also pushes the issue of creating a unique value (for the type id) to the problem of creating a unique instance (for type information), which the linker can solve. Advanced linker support is required because there is no place that appears only once to attach the type information to. There should be one structure definition but it is included in multiple translation units because of separate compilation. Each virtual table definition should be unique but there are an arbitrary number of these, so the special section prefix \texttt{.gnu.linkonce} is used. With a generated unique suffix (making the entire section name unique) the linker removes multiple definition ensuring only one version exists after linking. Then it is just a matter of making sure there is a unique name for each type. These steps are done in three phases. \begin{enumerate} \item The first phase is to generate a new structure definition to store the type information. The layout is the same in each case, just the parent's type id, but the types are changed. The structure's name is change, it is based off the virtual type's name, and the type of the parent's type id. If the virtual type is polymorphic, then the type information structure is polymorphic as well, with the same polymorphic arguments. \item The second phase is to generate an instance of the type information with a almost unique name, generated by mangling the virtual type name. \item The third phase is implicit with \CFA's overloading scheme. \CFA mangles names with type information so that all of the symbols exported to the linker are unique even if in the \CFA code they are the same. Having two declarations with the same name and same type is forbidden because it is impossible for overload resolution to pick between them. This is the reason why a unique type is generated for each virtual type. Polymorphic information is included in this mangling so polymorphic types have separate instances for each set of polymorphic arguments. \end{enumerate} The following example shows the components for a generated virtual type. \begin{cfa} struct TYPE_ID_TYPE { PARENT_ID_TYPE const * parent; }; __attribute__((cfa_linkonce)) TYPE_ID_TYPE const TYPE_ID_NAME = { &PARENT_ID_NAME, }; \end{cfa} \subsubsection{\lstinline{cfa_linkonce} Attribute} Another feature added to \CFA is a new attribute: \texttt{cfa\_linkonce}. This attribute is attached to an object or function definition (any global declaration with a name and a type) allowing it to be defined multiple times. All matching definitions must have the link-once attribute on them and should be identical. This attributed prototype is placed in a header file with other forward declaration. This technique is used for type-id instances, as there is no unique location associated with a type, except for the type definition in a header. The result is the unique type-id object generated by the linker. Internally, @cfa_linkonce@ is replaced with @section(".gnu.linkonce.NAME")@ where \texttt{NAME} is replaced by the mangled name of the object. Any other @section@ attributes are also removed from the declaration. The prefix \texttt{.gnu.linkonce} in section names is recognized by the linker. If two of these sections appear with the same name, including everything that comes after the special prefix, then only one is used and the other discarded. \subsection{Virtual Table} Each virtual type has a virtual table type that stores its type id and virtual members. Each virtual type instance is bound to a table instance that is filled with the values of virtual members. Both the layout of the fields and their value are decided by the rules given below. Figure~\ref{f:VirtualTableLayout} shows the layout is in three parts. \PAB{Number the parts in the figure.} \begin{enumerate} \item The first section is just the type id at the head of the table. It is always there to ensure that \PAB{... missing text to end this sentence} \item The second section are all the virtual members of the parent, in the same order as they appear in the parent's virtual table. Note that the type may change slightly as references to the @this@ change. This structure is limited to inside pointers/references and via function pointers so that the size (and hence the offsets) are the same. \item The third section is similar to the second except that it is the new virtual members introduced at this level in the hierarchy. \end{enumerate} \begin{figure} \input{vtable-layout} \caption{Virtual Table Layout} \label{f:VirtualTableLayout} \todo*{Improve the Virtual Table Layout diagram.} \end{figure} The first and second sections together mean that every virtual table has a prefix that has the same layout and types as its parent virtual table. This, combined with the fixed offset to the virtual table pointer, means that for any virtual type, it or any of its descendants can be accessed through the virtual table pointer. From there, it is safe to check the type id to identify the exact type of the underlying object, access any of the virtual members, and pass the object to any of the method-like virtual members. When a virtual table is declared, the user decides where to declare it and its name. The initialization of the virtual table is entirely automatic based on the context of the declaration. The type id is always fixed with each virtual table type having exactly one possible type id. The virtual members are usually filled in during type resolution. The best match for a given name and type at the declaration site is used. There are two exceptions to that rule: the @size@ field is the type's size set using a @sizeof@ expression, and the @align@ field is the type's alignment set using an @alignof@ expression. \subsubsection{Concurrency Integration} Coroutines and threads need instances of @CoroutineCancelled@ and @ThreadCancelled@ respectively to use all of their functionality. When a new data type is declared with @coroutine@ or @thread@, a forward declaration for the instance is created as well. The definition of the virtual table is created at the definition of the main function. Figure~\ref{f:ConcurrencyTransformations} shows ... \todo{Improve Concurrency Transformations figure.} \begin{figure} \begin{cfa} coroutine Example { // fields }; \end{cfa} \begin{cfa} __attribute__((cfa_linkonce)) struct __cfatid_struct_CoroutineCancelled(Example) __cfatid_CoroutineCancelled = { &EXCEPTION_TYPE_ID, }; extern CoroutineCancelled_vtable _default_vtable_object_declaration; extern CoroutineCancelled_vtable & _default_vtable; \end{cfa} \begin{cfa} void main(Example & this) { // body } \end{cfa} \begin{cfa} CoroutineCancelled_vtable _default_vtable_object_declaration = { __cfatid_CoroutineCancelled, // Virtual member initialization. }; CoroutineCancelled_vtable & _default_vtable = &_default_vtable_object_declaration; \end{cfa} \caption{Concurrency Transformations} \label{f:ConcurrencyTransformations} \end{figure} \subsection{Virtual Cast} Virtual casts are implemented as a function call that does the subtype check and a C coercion-cast to do the type conversion. % The C-cast is just to make sure the generated code is correct so the rest of % the section is about that function. The function is implemented in the standard library and has the following signature: \begin{cfa} void * __cfa__virtual_cast( struct __cfavir_type_td parent, struct __cfavir_type_id const * child ); \end{cfa} The type id of target type of the virtual cast is passed in as @parent@ and the cast target is passed in as @child@. The generated C code wraps both arguments and the result with type casts. There is also an internal check inside the compiler to make sure that the target type is a virtual type. % It also checks for conflicting definitions. The virtual cast either returns the original pointer as a new type or 0p. So the function just does the parent check and returns the appropriate value. The parent check is a simple linear search of child's ancestors using the type information. \section{Exceptions} % Anything about exception construction. \section{Unwinding} % Adapt the unwind chapter, just describe the sections of libunwind used. % Mention that termination and cancellation use it. Maybe go into why % resumption doesn't as well. % Many modern languages work with an internal stack that function push and pop % their local data to. Stack unwinding removes large sections of the stack, % often across functions. Stack unwinding is the process of removing stack frames (activations) from the stack. On function entry and return, unwinding is handled directly by the call/return code embedded in the function. In many cases, the position of the instruction pointer (relative to parameter and local declarations) is enough to know the current size of the stack frame. Usually, the stack-frame size is known statically based on parameter and local variable declarations. Even with dynamic stack-size, the information to determine how much of the stack has to be removed is still contained within the function. Allocating/deallocating stack space is usually an $O(1)$ operation achieved by bumping the hardware stack-pointer up or down as needed. In fact, constructing/destructing values within a stack frame is of similar complexity but often takes longer. Unwinding across multiple stack frames is more complex because that information is no longer contained within the current function. With separate compilation a function has no way of knowing what its callers so it can not know how large those frames are. Without altering the main code path, it is also hard to pass that work off to the caller. The traditional unwinding mechanism for C is implemented by saving a snap-shot of a function's state with @setjmp@ and restoring that snap-shot with @longjmp@. This approach bypasses the need to know stack details by simply reseting to a snap-shot of an arbitrary but existing function frame on the stack. It is up to the programmer to ensure the snap-shot is valid when it is reset and that all required clean-up from the unwound stacks is performed. This approach is fragile and forces extra work in the surrounding code. With respect to the extra work in the surrounding code, many languages define clean-up actions that must be taken when certain sections of the stack are removed. Such as when the storage for a variable is removed from the stack or when a @try@ statement with a finally clause is (conceptually) popped from the stack. None of these should be handled explicitly by the user --- that would contradict the intention of these features --- so they need to be handled automatically. To safely remove sections of the stack, the language must be able to find and run these clean-up actions even when removing multiple functions unknown at the beginning of the unwinding. One of the most popular tools for stack management is libunwind, a low-level library that provides tools for stack walking, handler execution, and unwinding. What follows is an overview of all the relevant features of libunwind needed for this work, and how \CFA uses them to implement exception handling. \subsection{libunwind Usage} Libunwind, accessed through @unwind.h@ on most platforms, is a C library that provides \Cpp-style stack-unwinding. Its operation is divided into two phases: search and cleanup. The dynamic target search -- phase 1 -- is used to scan the stack and decide where unwinding should stop (but no unwinding occurs). The cleanup -- phase 2 -- does the unwinding and also runs any cleanup code. To use libunwind, each function must have a personality function and a Language Specific Data Area (LSDA). The LSDA has the unique information for each function to tell the personality function where a function is executing, its current stack frame, and what handlers should be checked. Theoretically, the LSDA can contain any information but conventionally it is a table with entries representing regions of a function and what has to be done there during unwinding. These regions are bracketed by instruction addresses. If the instruction pointer is within a region's start/end, then execution is currently executing in that region. Regions are used to mark out the scopes of objects with destructors and @try@ blocks. % Libunwind actually does very little, it simply moves down the stack from % function to function. Most of the actions are implemented by the personality % function which libunwind calls on every function. Since this is shared across % many functions or even every function in a language it will need a bit more % information. The GCC compilation flag @-fexceptions@ causes the generation of an LSDA and attaches a personality function to each function. In plain C (which \CFA currently compiles down to) this flag only handles the cleanup attribute: \begin{cfa} void clean_up( int * var ) { ... } int avar __attribute__(( cleanup(clean_up) )); \end{cfa} The attribute is used on a variable and specifies a function, in this case @clean_up@, run when the variable goes out of scope. This capability is enough to mimic destructors, but not @try@ statements which can effect the unwinding. To get full unwinding support, all of these components must done directly with assembly and assembler directives, particularly the cfi directives \snake{.cfi_Leda} and \snake{.cfi_personality}. \subsection{Personality Functions} Personality functions have a complex interface specified by libunwind. This section covers some of the important parts of the interface. A personality function can perform different actions depending on how it is called. \begin{lstlisting} typedef _Unwind_Reason_Code (*_Unwind_Personality_Fn) ( _Unwind_Action action, _Unwind_Exception_Class exception_class, _Unwind_Exception * exception, struct _Unwind_Context * context); \end{lstlisting} The @action@ argument is a bitmask of possible actions: \begin{enumerate}[topsep=5pt] \item @_UA_SEARCH_PHASE@ specifies a search phase and tells the personality function to check for handlers. If there is a handler in a stack frame, as defined by the language, the personality function returns @_URC_HANDLER_FOUND@; otherwise it return @_URC_CONTINUE_UNWIND@. \item @_UA_CLEANUP_PHASE@ specifies a cleanup phase, where the entire frame is unwound and all cleanup code is run. The personality function does whatever cleanup the language defines (such as running destructors/finalizers) and then generally returns @_URC_CONTINUE_UNWIND@. \item \begin{sloppypar} @_UA_HANDLER_FRAME@ specifies a cleanup phase on a function frame that found a handler. The personality function must prepare to return to normal code execution and return @_URC_INSTALL_CONTEXT@. \end{sloppypar} \item @_UA_FORCE_UNWIND@ specifies a forced unwind call. Forced unwind only performs the cleanup phase and uses a different means to decide when to stop (see \vref{s:ForcedUnwind}). \end{enumerate} The @exception_class@ argument is a copy of the \code{C}{exception}'s @exception_class@ field, which is a number that identifies the exception handling mechanism that created the \PAB{... missing text to end this sentence} The \code{C}{exception} argument is a pointer to a user provided storage object. It has two public fields: the @exception_class@, which is described above, and the @exception_cleanup@ function. The clean-up function is used by the EHM to clean-up the exception, if it should need to be freed at an unusual time, it takes an argument that says why it had to be cleaned up. The @context@ argument is a pointer to an opaque type passed to helper functions called inside the personality function. The return value, @_Unwind_Reason_Code@, is an enumeration of possible messages that can be passed several places in libunwind. It includes a number of messages for special cases (some of which should never be used by the personality function) and error codes. However, unless otherwise noted, the personality function always return @_URC_CONTINUE_UNWIND@. \subsection{Raise Exception} Raising an exception is the central function of libunwind and it performs the two-staged unwinding. \begin{cfa} _Unwind_Reason_Code _Unwind_RaiseException(_Unwind_Exception *); \end{cfa} First, the function begins the search phase, calling the personality function of the most recent stack frame. It continues to call personality functions traversing the stack from newest to oldest until a function finds a handler or the end of the stack is reached. In the latter case, raise exception returns @_URC_END_OF_STACK@. Second, when a handler is matched, raise exception moves to the clean-up phase and walks the stack a second time. Once again, it calls the personality functions of each stack frame from newest to oldest. This pass stops at the stack frame containing the matching handler. If that personality function has not install a handler, it is an error. If an error is encountered, raise exception returns either @_URC_FATAL_PHASE1_ERROR@ or @_URC_FATAL_PHASE2_ERROR@ depending on when the error occurred. \subsection{Forced Unwind} \label{s:ForcedUnwind} Forced Unwind is the other central function in libunwind. \begin{cfa} _Unwind_Reason_Code _Unwind_ForcedUnwind(_Unwind_Exception *, _Unwind_Stop_Fn, void *); \end{cfa} It also unwinds the stack but it does not use the search phase. Instead another function, the stop function, is used to stop searching. The exception is the same as the one passed to raise exception. The extra arguments are the stop function and the stop parameter. The stop function has a similar interface as a personality function, except it is also passed the stop parameter. \begin{lstlisting} typedef _Unwind_Reason_Code (*_Unwind_Stop_Fn)( _Unwind_Action action, _Unwind_Exception_Class exception_class, _Unwind_Exception * exception, struct _Unwind_Context * context, void * stop_parameter); \end{lstlisting} The stop function is called at every stack frame before the personality function is called and then once more after all frames of the stack are unwound. Each time it is called, the stop function should return @_URC_NO_REASON@ or transfer control directly to other code outside of libunwind. The framework does not provide any assistance here. \begin{sloppypar} Its arguments are the same as the paired personality function. The actions \snake{_UA_CLEANUP_PHASE} and \snake{_UA_FORCE_UNWIND} are always set when it is called. Beyond the libunwind standard, both GCC and Clang add an extra action on the last call at the end of the stack: \snake{_UA_END_OF_STACK}. \end{sloppypar} \section{Exception Context} % Should I have another independent section? % There are only two things in it, top_resume and current_exception. How it is % stored changes depending on whether or not the thread-library is linked. The exception context is global storage used to maintain data across different exception operations and to communicate among different components. Each stack must have its own exception context. In a sequential \CFA program, there is only one stack with a single global exception-context. However, when the library @libcfathread@ is linked, there are multiple stacks and each needs its own exception context. The exception context should be retrieved by calling the function \snake{this_exception_context}. For sequential execution, this function is defined as a weak symbol in the \CFA system-library, @libcfa@. When a \CFA program is concurrent, it links with @libcfathread@, where this function is defined with a strong symbol replacing the sequential version. The sequential @this_exception_context@ returns a hard-coded pointer to the global exception context. The concurrent version adds the exception context to the data stored at the base of each stack. When @this_exception_context@ is called, it retrieves the active stack and returns the address of the context saved there. \section{Termination} % Memory management & extra information, the custom function used to implement % catches. Talk about GCC nested functions. \CFA termination exceptions use libunwind heavily because they match \Cpp exceptions closely. The main complication for \CFA is that the compiler generates C code, making it very difficult to generate the assembly to form the LSDA for @try@ blocks or destructors. \subsection{Memory Management} The first step of a termination raise is to copy the exception into memory managed by the exception system. Currently, the system uses @malloc@, rather than reserved memory or the stack top. The exception handling mechanism manages memory for the exception as well as memory for libunwind and the system's own per-exception storage. \begin{figure} \centering \input{exception-layout} \caption{Exception Layout} \label{f:ExceptionLayout} \end{figure} \todo*{Convert the exception layout to an actual diagram.} Exceptions are stored in variable-sized blocks (see Figure~\vref{f:ExceptionLayout}). The first component is a fixed-sized data structure that contains the information for libunwind and the exception system. The second component is an area of memory big enough to store the exception. Macros with pointer arthritic and type cast are used to move between the components or go from the embedded @_Unwind_Exception@ to the entire node. Multiple exceptions can exist at the same time because exceptions can be raised inside handlers, destructors and finally blocks. Figure~\vref{f:MultipleExceptions} shows a program that has multiple exceptions active at one time. Each time an exception is thrown and caught the stack unwinds and the finally clause runs. This handler throws another exception (until @num_exceptions@ gets high enough), which must be allocated. The previous exceptions may not be freed because the handler/catch clause has not been run. Therefore, the EHM must keep all of these exceptions alive while it allocates exceptions for new throws. \begin{figure} \centering \newsavebox{\codeBox} \newsavebox{\stackBox} \begin{lrbox}{\codeBox} \begin{lstlisting}[language=CFA,{moredelim=**[is][\color{red}]{@}{@}}] unsigned num_exceptions = 0; void throws() { try { try { ++num_exceptions; throw (Example){table}; } finally { if (num_exceptions < 3) { throws(); } } } catch (exception_t *) { --num_exceptions; } } int main() { throws(); } \end{lstlisting} \end{lrbox} \begin{lrbox}{\stackBox} \begin{lstlisting} | try-finally | try-catch (Example) throws() | try-finally | try-catch (Example) throws() | try-finally | try-catch (Example) throws() main() \end{lstlisting} \end{lrbox} {\usebox\codeBox} \hspace{25pt} {\usebox\stackBox} \caption{Multiple Exceptions} \label{f:MultipleExceptions} \end{figure} \todo*{Work on multiple exceptions code sample.} All exceptions are stored in nodes, which are then linked together in lists one list per stack, with the list head stored in the exception context. Within each linked list, the most recently thrown exception is at the head followed by older thrown exceptions. This format allows exceptions to be thrown, while a different exception is being handled. The exception at the head of the list is currently being handled, while other exceptions wait for the exceptions before them to be handled and removed. The virtual members in the exception's virtual table provide the size of the exception, the copy function, and the free function, so they are specific to an exception type. The size and copy function are used immediately to copy an exception into managed memory. After the exception is handled, the free function is used to clean up the exception and then the entire node is passed to free so the memory is returned to the heap. \subsection{Try Statements and Catch Clauses} The @try@ statement with termination handlers is complex because it must compensate for the C code-generation versus assembly-code generation from \CFA. Libunwind requires an LSDA and personality function for control to unwind across a function. The LSDA in particular is hard to mimic in generated C code. The workaround is a function called @__cfaehm_try_terminate@ in the standard library. The contents of a @try@ block and the termination handlers are converted into functions. These are then passed to the try terminate function and it calls them. Because this function is known and fixed (and not an arbitrary function that happens to contain a @try@ statement), the LSDA can be generated ahead of time. Both the LSDA and the personality function are set ahead of time using embedded assembly. This assembly code is handcrafted using C @asm@ statements and contains enough information for a single @try@ statement the function represents. The three functions passed to try terminate are: \begin{description} \item[try function:] This function is the @try@ block, where all the code inside the @try@ block is wrapped inside the function. It takes no parameters and has no return value. This function is called during regular execution to run the try block. \item[match function:] This function is called during the search phase and decides if a catch clause matches the termination exception. It is constructed from the conditional part of each handler and runs each check, top to bottom, in turn, first checking to see if the exception type matches and then if the condition is true. It takes a pointer to the exception and returns 0 if the exception is not handled here. Otherwise the return value is the id of the handler that matches the exception. \item[handler function:] This function handles the exception, where the code inside is constructed by stitching together the bodies of each handler of a @try@ statement and dispatches to the selected handler. It takes a pointer to the exception and the handler's id and returns nothing. It is called after the cleanup phase. \end{description} All three functions are created with GCC nested functions. GCC nested functions can be used to create closures, \ie functions that can refer to the state of other functions on the stack. This approach allows the functions to refer to all the variables in scope for the function containing the @try@ statement. These nested functions and all other functions besides @__cfaehm_try_terminate@ in \CFA use the GCC personality function and the @-fexceptions@ flag to generate the LSDA. Using this pattern, \CFA implements destructors with the cleanup attribute. Figure~\ref{f:TerminationTransformation} shows an example transformation for a \CFA @try@ statement with @catch@ clauses into corresponding C functions. \PAB{Walk the reader through the example code.} \begin{figure} \begin{cfa} try { // TRY BLOCK } catch (Exception1 * name1 ; check(name1)) { // CATCH BLOCK 1 } catch (Exception2 * name2) { // CATCH BLOCK 2 } \end{cfa} \medskip \hrule \medskip \begin{cfa} void try(void) { // TRY BLOCK } int match(exception_t * __exception_inst) { { Exception1 * name1; if (name1 = (virtual Exception1 *)__exception_inst && check(name1)) { return 1; } } { Exception2 * name2; if (name2 = (virtual Exception2 *)__exception_inst) { return 2; } } return 0; } void catch(exception_t * __exception_inst, int __handler_index) { switch (__handler_index) { case 1: { Exception1 * name1 = (virtual Exception1 *)__exception_inst; // CATCH BLOCK 1 } return; case 2: { Exception2 * name2 = (virtual Exception2 *)__exception_inst; // CATCH BLOCK 2 } return; } } { __cfaehm_try_terminate(try, catch, match); } \end{cfa} \caption{Termination Transformation} \label{f:TerminationTransformation} \todo*{Improve (compress?) Termination Transformations.} \end{figure} \section{Resumption} % The stack-local data, the linked list of nodes. Resumption is simpler to implement than termination because there is no stack unwinding. Instead of storing the data in a special area using assembly, there is just a linked list of possible handlers for each stack, with each list node representing a @try@ statement on the stack. The head of the list is stored in the exception context. The nodes are stored in order, with the more recent @try@ statements closer to the head of the list. Instead of traversing the stack, resumption handling traverses the list. At each node, the EHM checks to see if the @try@ statement it represents can handle the exception. If it can, then the exception is handled and the operation finishes, otherwise the search continues to the next node. If the search reaches the end of the list without finding a @try@ statement that can handle the exception, the default handler is executed and the operation finishes. Each node has a handler function that does most of the work. The handler function is passed the raised exception and returns true if the exception is handled and false otherwise. For each @catchResume@ clause, the handler function: \begin{itemize} \item checks to see if the raised exception is a descendant type of the declared exception type, \item if it is and there is a conditional expression then it runs the test, \item if both checks pass the handling code for the clause is run and the function returns true, \item otherwise it moves onto the next clause. \end{itemize} If this is the last @catchResume@ clause then instead of moving onto the next clause the function returns false as no handler could be found. Figure~\ref{f:ResumptionTransformation} shows an example transformation for a \CFA @try@ statement with @catchResume@ clauses into corresponding C functions. \PAB{Walk the reader through the example code.} \begin{figure} \begin{cfa} try { // TRY BLOCK } catchResume (Exception1 * name1 ; check(name1)) { // CATCH BLOCK 1 } catchResume (Exception2 * name2) { // CATCH BLOCK 2 } \end{cfa} \begin{cfa} bool handle(exception_t * __exception_inst) { { Exception1 * name1; if (name1 = (virtual Exception1 *)__exception_inst && check(name1)) { // CATCH BLOCK 1 return 1; } } { Exception2 * name2; if (name2 = (virtual Exception2 *)__exception_inst) { // CATCH BLOCK 2 return 2; } } return false; } struct __try_resume_node __resume_node __attribute__((cleanup( __cfaehm_try_resume_cleanup ))); __cfaehm_try_resume_setup( &__resume_node, handler ); \end{cfa} \caption{Resumption Transformation} \label{f:ResumptionTransformation} \todo*{Improve (compress?) Resumption Transformations.} \end{figure} % Recursive Resumption Stuff: Figure~\ref{f:ResumptionMarking} shows the search skipping (see \vpageref{s:ResumptionMarking}), which ignores parts of the stack already examined, is accomplished by updating the front of the list as the search continues. Before the handler at a node is called, the head of the list is updated to the next node of the current node. After the search is complete, successful or not, the head of the list is reset. This mechanism means the current handler and every handler that has already been checked are not on the list while a handler is run. If a resumption is thrown during the handling of another resumption, the active handlers and all the other handler checked up to this point are not checked again. This structure also supports new handlers added while the resumption is being handled. These are added to the front of the list, pointing back along the stack -- the first one points over all the checked handlers -- and the ordering is maintained. \PAB{Maybe number the figure and use the numbers in the description to help the reader follow.} \begin{figure} \input{resumption-marking} \caption{Resumption Marking} \label{f:ResumptionMarking} \todo*{Convert Resumption Marking into a line figure.} \end{figure} \label{p:zero-cost} Finally, the resumption implementation has a cost for entering/exiting a @try@ statement with @catchResume@ clauses, whereas a @try@ statement with @catch@ clauses has zero-cost entry/exit. While resumption does not need the stack unwinding and cleanup provided by libunwind, it could use the search phase to providing zero-cost enter/exit using the LSDA. Unfortunately, there is no way to return from a libunwind search without installing a handler or raising an error. Although workarounds might be possible, they are beyond the scope of this thesis. The current resumption implementation has simplicity in its favour. % Seriously, just compare the size of the two chapters and then consider % that unwind is required knowledge for that chapter. \section{Finally} % Uses destructors and GCC nested functions. A finally clause is placed into a GCC nested-function with a unique name, and no arguments or return values. This nested function is then set as the cleanup function of an empty object that is declared at the beginning of a block placed around the context of the associated @try@ statement. The rest is handled by GCC. The @try@ block and all handlers are inside this block. At completion, control exits the block and the empty object is cleaned up, which runs the function that contains the finally code. \section{Cancellation} % Stack selections, the three internal unwind functions. Cancellation also uses libunwind to do its stack traversal and unwinding, however it uses a different primary function: @_Unwind_ForcedUnwind@. Details of its interface can be found in the Section~\vref{s:ForcedUnwind}. The first step of cancellation is to find the cancelled stack and its type: coroutine or thread. Fortunately, the thread library stores the main thread pointer and the current thread pointer, and every thread stores a pointer to its coroutine and the coroutine it is currently executing. If the current thread's main and current coroutines are the same then the current stack is a thread stack, otherwise it is a coroutine stack. Note, the runtime considers a thread as a coroutine with an associated user-level thread; hence, for many operations a thread and coroutine are treated uniformly. %\todo*{Consider adding a description of how threads are coroutines.} % Furthermore it is easy to compare the % current thread to the main thread to see if they are the same. And if this % is not a thread stack then it must be a coroutine stack. However, if the threading library is not linked, the sequential execution is on the main stack. Hence, the entire check is skipped because the weak-symbol function is loaded. Therefore, main thread cancellation is unconditionally performed. Regardless of how the stack is chosen, the stop function and parameter are passed to the forced-unwind function. The general pattern of all three stop functions is the same: continue unwinding until the end of stack and then perform the appropriate transfer. For main stack cancellation, the transfer is just a program abort. For coroutine cancellation, the exception is stored on the coroutine's stack, and the coroutine context switches to its last resumer. The rest is handled on the backside of the resume, which checks if the resumed coroutine is cancelled. If cancelled, the exception is retrieved from the resumed coroutine, and a @CoroutineCancelled@ exception is constructed and loaded with the cancelled exception. It is then resumed as a regular exception with the default handler coming from the context of the resumption call. This semantics allows a cancellation to cascade through an arbitrary set of resumed coroutines back to the thread's coroutine, performing cleanup along the way. For thread cancellation, the exception is stored on the thread's main stack and then context switched to the scheduler. The rest is handled by the thread joiner. When the join is complete, the joiner checks if the joined thread is cancelled. If cancelled, the exception is retrieved and the joined thread, and a @ThreadCancelled@ exception is constructed and loaded with the cancelled exception. The default handler is passed in as a function pointer. If it is null (as it is for the auto-generated joins on destructor call), the default is used, which is a program abort. This semantics allows a cancellation to cascade through an arbitrary set of joining threads back to the program's main, performing cleanup along the way. %; which gives the required handling on implicate join.