# Changeset fc1347d0

Ignore:
Timestamp:
May 17, 2021, 9:44:58 AM (21 months ago)
Branches:
arm-eh, enum, forall-pointer-decay, jacob/cs343-translation, master, new-ast-unique-expr, pthread-emulation, qualifiedEnum
Children:
8f910430
Parents:
58d471f (diff), 299b8b2 (diff)
Note: this is a merge changeset, the changes displayed below correspond to the merge itself.
Use the (diff) links above to see all the changes relative to each parent.
Message:

Merge branch 'andrew-mmath' into 'master', implement chapter updates.

Location:
doc/theses/andrew_beach_MMath
Files:
4 edited

Unmodified
Removed
• ## doc/theses/andrew_beach_MMath/cfalab.sty

 r58d471f % Package for CFA Research Lab. % (Now more a personal collection and testing grounds for common.sty.) % % This is a collection of commands everyone working on CFA related documents % space and '{}' to force remove one. % % \CFA % Cforall with the forall symbol. \newrobustcmd\CFA{\textsf{C\raisebox{\depth}{\rotatebox{180}{A}}}\xspace} % C++ with kerning. You may optionally append a standard number. % \Cpp[] % C++ symbol name. You may optionally provide to specify a standard. \newrobustcmd\Cpp[1][\xspace]{C++#1} \newcommand*\colour[2]{{\color{#1}#2}} % \code*{} % Use the listings package to format a snipit of . \newrobustcmd*\codeCFA[1]{\lstinline[language=CFA]{#1}} \newrobustcmd*\codeC[1]{\lstinline[language=C]{#1}} \newrobustcmd*\codeCpp[1]{\lstinline[language=C++]{#1}} \newrobustcmd*\codePy[1]{\lstinline[language=Python]{#1}} % \code{}{} % Use the listings package to format the snipit of in . \newrobustcmd*\code[2]{\lstinline[language=#1]{#2}} % \begin{cfa}[] % \end{cfa} % Use the listings package to format a block of CFA code. % Extra listings options can be passed in as an optional argument.
• ## doc/theses/andrew_beach_MMath/features.tex

 r58d471f Some well known examples include the @throw@ statements of \Cpp and Java and the \codePy{raise} statement from Python. In real systems a raise may preform some other work (such as memory management) but for the purposes of this overview that can be ignored. the \code{Python}{raise} statement from Python. In real systems a raise may preform some other work (such as memory management) but for the purposes of this overview that can be ignored. \subparagraph{Handle} A handler labelled with any given exception can handle exceptions of that type or any child type of that exception. The root of the exception hierarchy (here \codeC{exception}) acts as a catch-all, leaf types catch single types (here \code{C}{exception}) acts as a catch-all, leaf types catch single types and the exceptions in the middle can be used to catch different groups of related exceptions. While much of the virtual infrastructure is created, it is currently only used internally for exception handling. The only user-level feature is the virtual cast, which is the same as the \Cpp \codeCpp{dynamic_cast}. cast, which is the same as the \Cpp \code{C++}{dynamic_cast}. \label{p:VirtualCast} \begin{cfa}
• ## doc/theses/andrew_beach_MMath/implement.tex

 r58d471f \subsection{Virtual Type} Virtual types only have one change to their structure: the addition of a pointer to the virtual table, called the \emph{virtual-table pointer}. Internally, the field is called @virtual_table@. This constant pointer is always the first field of the table so when casting to a supertype, the field's location is always known. The field is initialized as part of all generated constructors. \todo{They only come as part exceptions and don't work.} %After the object is created the field is constant. Dereferencing it gives the virtual table and access to the type's virtual members. pointer to the virtual table, which is called the \emph{virtual-table pointer}. Internally, the field is called @virtual_table@. The field is fixed after construction. It is always the first field in the structure so that its location is always known. \todo{Talk about constructors for virtual types (after they are working).} This is what binds an instance of a virtual type to a virtual table. This pointer can be used as an identity check. It can also be used 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 (the types reperented 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, zero. The id's are implemented as pointers to the type's type information instance. Derefencing the pointer gets the type information. By going back-and-forth between the type id and the type info one can find every ancestor of a virtual type. It 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. Each virtual table definition should be unique but there are an arbitrary number of thoses. So the special section prefix \texttt{.gnu.linkonce} is used. With a unique suffix (making the entire section name unique) the linker will remove multiple definition making sure only one version exists after linking. Then it is just a matter of making sure there is a unique name for each type. This is done in three phases. 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. The second phase is to generate an instance of the type information with a almost unique name, generated by mangling the virtual type name. 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 \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 why a unique type is generated for each virtual type. Polymorphic information is included in this mangling so polymorphic types will have seperate instances for each set of polymorphic arguments. \begin{cfa} struct /* type name */ { /* parent type name */ const * parent; }; __attribute__((section(".gnu.linkonce./* instance name */"))) /* type name */ const /* instance name */ = { &/* parent instance name */, }; \end{cfa} \subsection{Virtual Table} % PAB: These 2 paragraphs are repeated below, and maybe some of the paragraph above, too. \begin{comment} Every time a virtual type is defined, a new virtual table-type is instantiated. The uniqueness of the virtual-table instance is important because its address is used as the identifier for the virtual type. Hence, a pointer to the virtual table and the ID for the virtual type are interchangeable. \todo{Unique instances might be going so we will have to talk about the new system instead.} The first step is creating the virtual-table type. The virtual-table type is a structure and is described in terms of its fields. The first field contains the parent-type ID (or a pointer to the parent virtual-table) or 0 (null pointer). Next are repeated fields from on the parent virtual-table. Finally, the fields used to store any new virtual members of the new the virtual type. \end{comment} %The virtual system is accessed through a private constant field inserted at the %beginning of every virtual type. This field The virtual-table pointer points at a type's virtual table (see Figure~\vref{f:VirtualTableLayout}). %and is assigned during the object's %construction. The address of a virtual table acts as the unique identifier for the virtual type, and the first field of a virtual table is a pointer to the parent virtual-table or @0p@ (null pointer). The remaining fields are duplicated from the parent tables in this type's inheritance chain, followed by any fields this type introduces. Parent fields are duplicated so they can be changed, \ie all virtual members are overridable, while the parent pointer allows access to the original values. Hence, references to the dispatched type are replaced with the current virtual type. % These are always taken by pointer or reference. 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. The layout always comes in three parts. The first section is just the type id at the head of the table. It is always there to ensure that 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" will change. This is limited to inside pointers/references and via function pointers so that the size (and hence the offsets) are the same. The third section is similar to the second except that it is the new virtual members introduced at this level in the hierarchy. \begin{figure} % Simple ascii diragram: \begin{cfa} parent_pointer  // \C{parent pointer to access its fields} parent_field0   // \C{same layout as parent to allow replacement} type_id parent_field0 ... parent_fieldN child_field0    // \C{new types for this virtual table} child_field0 ... child_fieldN size alignment \end{cfa} %\todo{Refine the diagram} \caption{Virtual Table Layout} \label{f:VirtualTableLayout} \todo*{Improve the Virtual Table Layout diagram.} \end{figure} % For each virtual type, a virtual table is constructed. This is both a new type % and an instance of that type. Other instances of the type could be created % but the system doesn't use them. So this section will go over the creation of % the type and the instance. \begin{comment} PAB: seems to be said already. A virtual table is created when a virtual type is created. The name of the type is created by mangling the name of the base type. The name of the instance is also generated by name mangling. The fields are initialized automatically. The parent field is initialized by getting the type of the parent field and using that to calculate the mangled name of the parent's virtual-table type. \end{comment} There are two special fields that are included like normal fields but have special initialization rules: the @size@ field is the type's size and is initialized with a @sizeof@ expression, the @align@ field is the type's alignment and uses an @alignof@ expression. The remaining fields are resolved to a name matching the field's name and type using the normal visibility and overload resolution rules of the type system. These operations are split up into several groups depending on where they take place, which varies for monomorphic and polymorphic types. The first devision is between the declarations and the definitions. Declarations, such as a function signature or an aggregate's name, must always be visible but may be repeated in the form of forward declarations in headers. Definitions, such as function bodies and a aggregate's layout, can be separately compiled but must occur exactly once in a source file. The declarations include the virtual-type definition and forward declarations of the virtual-table instance, constructor, message function and @get_exception_vtable@. The definition includes the storage and initialization of the virtual table instance and the bodies of the three functions. Monomorphic instances put all of these two groups in one place. Polymorphic instances split out the core declarations and definitions from the per-instance information. The virtual-table type and most of the functions are polymorphic so they are all part of the core. The virtual-table instance and the @get_exception_vtable@ function \PAB{ are ...}. 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 doesn't matter if we have it or any of its descendants, it is still always safe to access the virtual table 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. \todo{Introduce 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, each virtual table type will always have one exactly one possible type id. The virtual members are usually filled in by resolution. The best match for a given name and type at the declaration site is filled in. There are two exceptions to that rule: the @size@ field is the type's size and is set to the result of a @sizeof@ expression, the @align@ field is the type's alignment and similarly uses 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@, the forward declaration for data type is declared with @coroutine@ or @thread@ the forward declaration for the instance is created as well. The definition of the virtual table is created at the definition of the main function. \PAB{You need an example here to show what happens for this case.} \todo{Add an example with code snipits.} \subsection{Virtual Cast} % 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 The function is implemented in the standard library and has the following signature: \begin{cfa} void * __cfa__virtual_cast( struct __cfa__parent_vtable const * parent, void * __cfa__virtual_cast( struct __cfa__parent_vtable const * parent, struct __cfa__parent_vtable const * const * child ); \end{cfa} and it is implemented in the standard library. The structure represents the head of a virtual table, which is the pointer to the parent virtual table. The @parent@ points directly at the parent-type virtual-table, while the @child@ points at the object of the (possible) child type. \PAB{Need a figure to show this relationship.} In terms of the virtual-cast expression, @parent@ comes from looking up the type being cast to and @child@ is the result of the expression being cast. Because the complier outputs C code, some C-type casts are also used. The last bit of glue is a map that saves every virtual type the compiler sees. This table is used to check the type used in a virtual cast is a virtual type and to get its virtual table. (It also checks for conflicting definitions.) \PAB{Can this be rolled into the figure above?} Inside the function is a simple conditional. If the type represented by @parent@ is an ancestor of the type represented by @*child@ (it requires one more level of dereference to pass through the object) then @child@ is returned, otherwise the null pointer is returned. The check is a simple linear search (like \Cpp RTTI). If the child virtual table or any of its ancestors (which are retrieved through the first field of every virtual table) are the same as the parent virtual-table then the cast succeeds. \todo{Get rid of \_\_cfa\_\_parent\_vtable in the standard library and then the document.} The type id of target type of the virtual cast is passed in as @parent@ and the cast target is passed in as @child@. For C generation both arguments and the result are wrapped with type casts. There is also an internal store 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 null. So the function just does the parent check and returns the approprate value. The parent check is a simple linear search of child's ancestors using the type information. \section{Exceptions} % resumption doesn't as well. % Many modern languages work with an internal stack that function push and pop % Many modern languages work with an interal 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 a function. Usually, the stack-frame size is known statically based on parameter and local variable declarations. For dynamically-sized local variables. (Often called a variable-length array or VLA, even when the variable type is an aggregate.) For VLAs, a runtime computation is necessary to know the frame size. Finally, a function's frame-size may change during execution as local variables (static or dynamic sized) go in and out of scope, which is a form of VLA. 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 determain 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. Unwinding across multiple stack frames is more complex because individual stack-management code associated with each frame can be bypassed. That is, the location of a function's frame-management code is largely unknown and dispersed throughout the function, hence the current frame size managed by that code is also unknown. Hence, code unwinding across frames does not have direct knowledge about what is on the stack, and hence, how much of the stack needs to be removed. % At a very basic level this can be done with @setjmp@ \& @longjmp@ which simply % move the top of the stack, discarding everything on the stack above a certain % point. However this ignores all the cleanup code that should be run when % certain sections of the stack are removed (for \CFA these are from destructors % and finally clauses) and also requires that the point to which the stack is % being unwound is known ahead of time. libunwind is used to address both of % these problems. Constructing/destructing values on the stack takes longer put in terms of figuring out what needs to be done is of similar complexity. Unwinding across multiple stack frames is more complex because that information is no longer contained within the current function. With seperate compilation a function has no way of knowing what its callers are so it can't 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 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 unwound frames do not have side-effects. Hence, this unwinding approach is fragile with potential errors that are difficult to debug because the stack becomes corrupted. With respect to stack side-effects, many languages define cleanup actions that must be taken when objects are deallocated from the stack, when the function of blocks within the function end, such as running a variable's destructor or a @try@ statement's @finally@ clause. The purpose of these side-effects is to reestablish the global state of the program, such as dynamic memory-allocation or file access. Handling these side-effect mechanisms requires walking the stack and checking each stack frame for these potential actions, where a frame can be any block with declarations. In languages like \Cpp and Java, it must be possible to walk the stack frames in search of @try@ statements to match and execute a handler. For termination exceptions, it must also be possible to unwind all stack frames from the throw to the matching catch (including the @try@ block), and each of these frames must be checked for cleanup actions. Stack walking is where most of the complexity and expense of exception handling appears. reset and that all required clean-up from the unwound stacks is preformed. This approach is fragile and forces a work onto the surounding code. With respect to that work forced onto the surounding 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 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 The GCC compilation flag @-fexceptions@ causes the generation of an LSDA and attaches its personality function. It attaches a series of opaque directives (@.cfi_personality@ directive) used internally and not part of this work. However, this 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} int avar __attribute__(( cleanup(clean_up) )); \end{cfa} that is used on a variable and specifies a function, in this case @clean_up@, run when the variable goes out of scope, which is used to mimic destructors. However, this feature cannot be used to mimic @try@ statements as it cannot control the unwinding. The attribue is used on a variable and specifies a function, in this case @clean_up@, run when the variable goes out of scope. This is enough to mimic destructors, but not try statements which can effect the unwinding. To get full unwinding support all of this has to be done directly with assembly and assembler directives. Partiularly the cfi directives \texttt{.cfi\_lsda} and \texttt{.cfi\_personality}. \subsection{Personality Functions} section covers some of the important parts of the interface. A personality function can perform different actions depending on how it is A personality function can preform different actions depending on how it is called. \begin{lstlisting}[language=C,{moredelim=**[is][\color{red}]{@}{@}}] @_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 Section~\vref{s:ForcedUnwind}). (see \vref{s:ForcedUnwind}). \end{enumerate} The @exception_class@ argument is a copy of the \lstinline[language=C]|exception|'s @exception_class@ field. \PAB{Say more.} The \lstinline[language=C]|exception| argument is a pointer to the user provided storage object. It has two public fields, the exception class, which is just a number, identifying the exception handling mechanism that created it, and the cleanup function. The cleanup function is called if required by the exception. \code{C}{exception}'s @exception_class@ field. This a number that identifies the exception handling mechanism that created the The \code{C}{exception} argument is a pointer to the 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 @_URC_END_OF_STACK@. Second, when a handler is matched, raise exception walks the stack again performing the cleanup phase. 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. 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, where each the library @libcfathread@ is linked, there are multiple stacks and each needs its own exception context. The function @this_exception_context@ provides general access to the exception context. For sequential execution, this function is defined as The exception context should be retrieved by calling the function @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 % catches. Talk about GCC nested functions. Termination exceptions use libunwind heavily because \CFA termination exceptions match \CFA termination exceptions use libunwind heavily because they match \Cpp \Cpp exceptions closely. The main complication for \CFA is that the compiler generates C code, making it very difficult to generate the assembly to 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 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. \label{f:ExceptionLayout} \end{figure} Exceptions are stored in variable-sized blocks (see Figure~\vref{f:ExceptionLayout}). The first component is a fixed-sized data-structure that contains the \todo*{Convert the exception layout to an actual diagram.} Exceptions are stored in variable-sized blocks (see \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 @_Unwind_Exception@ to the entire node. Multiple exceptions can exist because handlers can call functions that raise exceptions.  Figure~\vref{f:MultipleExceptions} shows a \Cpp program where exceptions are handled, and then a function is called from the handler that raises a new exception. The previous exception must persist because it is unhandled, and hence, control can return to the handler and that exception is reraised. Multipe 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 will throw 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. So the EHM must keep them alive while it allocates exceptions for new throws. \begin{figure} \centering % Andrew: Figure out what these do and give them better names. \newsavebox{\myboxA} \newsavebox{\myboxB} \begin{lrbox}{\myboxA} \begin{lstlisting}[language=C++,{moredelim=**[is][\color{red}]{@}{@}}] struct E {}; int cnt = 3; void f( int i ) { if ( i == 0 ) @throw E();@ try { @f( i - 1 );@ } catch( E ) { // handler h cnt -= 1; if ( cnt > 0 ) @f( 2 );@ } \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() { @f( 2 );@ } int main() { throws(); } \end{lstlisting} \end{lrbox} \begin{lrbox}{\myboxB} \begin{lstlisting} h  $\makebox[0pt][l]{\textbackslash}f$ f f h  $\makebox[0pt][l]{\textbackslash}f$  throw E$$$_2$$$ f f h  $\makebox[0pt][l]{\textbackslash}f$  throw E$$$_1$$$ f f \end{lstlisting} \end{lrbox} \label{f:MultipleExceptions} \end{figure} In this case, the exception nodes are linked together in a list, one list per stack, with the \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 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 can be given back to the heap. 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 can be given back to the heap. \subsection{Try Statements and Catch Clauses} The try statement with termination handlers is complex because it must compensate for the lack of assembly code generated from \CFA. Libunwind compensate for the lack of assembly-code generated 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. calls them. Because this function is known and fixed (and not an arbitrary function that happens to contain a try statement), this means the LSDA can be generated ahead 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 the single try statement the function represents. embedded assembly. This assembly code is handcrafted using C @asm@ statements and contains enough information for the single try statement the function repersents. The three functions passed to try terminate are: 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. Through this mechanism, \CFA destructors are implemented via the cleanup attribute. \PAB{Try to put together an example try statement illustrating these components.} the LSDA. Using this pattern, \CFA implements destructors with the cleanup attribute. \todo{Add an example of the conversion from try statement to functions.} \section{Resumption} % The stack-local data, the linked list of nodes. Resumption is simpler to implement than termination because there is no stack unwinding.  \PAB{You need to explain how the \lstinline{catchResume} clauses are handled. Do you use the personality mechanism in libunwind or do you roll your own mechanism?} The resumption raise uses a list of nodes for its stack traversal. The head of the list is stored in the exception context. The nodes in the list have a pointer to the next node and a pointer to the handler function. A resumption raise traverses this list. At each node the handler function is called, passing the exception by pointer. It returns true if the exception is handled and false otherwise. The handler function does both the matching and handling. It computes the condition of each @catchResume@ in top-to-bottom order, until it finds a handler that matches. If no handler matches then the function returns false. Otherwise the matching handler is run; if it completes successfully, the function returns true. Rethrowing, through the @throwResume;@ statement, causes the function to return true. Resumption 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 node on the list reperenting 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 the node repersents 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. In each node is a handler function which does most of the work there. The handler function is passed the raised the exception and returns true if the exception is handled and false if it cannot be handled here. For each @catchResume@ clause the handler function will: check to see if the raised exception is a descendant type of the declared exception type, if it is and there is a conditional expression then it will run the test, if both checks pass the handling code for the clause is run and the function returns true, otherwise it moves onto the next clause. 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. \todo{Diagram showing a try statement being converted into resumption handlers.} % Recursive Resumption Stuff: the other handler checked up to this point are not checked again. This structure also supports new handlers added while the resumption is being This structure also supports new handler 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{Again, a figure to show how this works would be helpful.} \todo{Add a diagram for resumption marking.} \label{p:zero-cost} % that unwind is required knowledge for that chapter. \PAB{This paragraph needs to be moved to the start of this Section, where I have have my other comment.} \section{Finally} % Uses destructors and GCC nested functions. A finally clause is placed into a GCC nested-function with a unique mangled name, and no arguments or return values. This nested function is then set as the cleanup 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 an associated @try@ statement. around the context of the associated @try@ statement. The rest is handled by GCC. The try block and all handlers are inside this 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 Section~\vref{s:ForcedUnwind}. 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 program-main thread pointer and the current-thread pointer, and every thread stores a pointer to the current coroutine it is executing. \PAB{I don't know if my corrections in the previous paragraph are correct.} When the active thread and coroutine are the same, the current stack is the thread stack, otherwise it is a coroutine stack. % PAB: repeated? % If it is a thread stack, then an equality check with the stored main % thread pointer and current thread pointer is enough to tell if the current % thread is the main thread or not. 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 main coroutine and the coroutine it is currently executing. \todo*{Consider adding a description of how threads are coroutines.} If a the current thread's main and current coroutines are the same then the current stack is a thread stack. 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 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. %when they %do there primary work. functions is the same: they continue unwinding until the end of stack and then preform their transfer. For main stack cancellation, the transfer is just a program abort. For coroutine cancellation, the exception is stored in the coroutine's stack, 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 check if the resumed coroutine is
• ## doc/theses/andrew_beach_MMath/uw-ethesis.tex

 r58d471f % Removes large sections of the document. \usepackage{comment} % Adds todos (Must be included after comment.) \usepackage{todonotes} % Adds todo commands. \usepackage{todo} % cfa macros used in the document \usepackage{cfalab} % Exception to the rule of hyperref being the last add-on package \usepackage[automake,toc,abbreviations]{glossaries-extra} \usepackage[toc,abbreviations]{glossaries-extra} % If glossaries-extra is not in your LaTeX distribution, get it from CTAN % (http://ctan.org/pkg/glossaries-extra), although it's supposed to be in % Tip: Putting each sentence on a new line is a way to simplify later editing. %---------------------------------------------------------------------- \input{intro} \input{existing} \input{features} \input{implement} %\input{unwinding} \input{future} \phantomsection         % allows hyperref to link to the correct page \todos %---------------------------------------------------------------------- \end{document} % end of logical document
Note: See TracChangeset for help on using the changeset viewer.