Ignore:
Timestamp:
Feb 16, 2021, 1:07:56 PM (3 years ago)
Author:
Andrew Beach <ajbeach@…>
Branches:
ADT, arm-eh, ast-experimental, enum, forall-pointer-decay, jacob/cs343-translation, master, new-ast-unique-expr, pthread-emulation, qualifiedEnum
Children:
d0502a3, f6664bf2
Parents:
c6640a3
Message:

Andrew MMath: Updated exception features chapter.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • doc/theses/andrew_beach_MMath/features.tex

    rc6640a3 r1830a86  
    113113virtual table type; which usually has a mangled name.
    114114% Also \CFA's trait system handles functions better than constants and doing
    115 % it this way
     115% it this way reduce the amount of boiler plate we need.
    116116
    117117% I did have a note about how it is the programmer's responsibility to make
     
    119119% similar system I know of (except Agda's I guess) so I took it out.
    120120
    121 \section{Raise}
    122 \CFA provides two kinds of exception raise: termination
    123 \see{\VRef{s:Termination}} and resumption \see{\VRef{s:Resumption}}, which are
    124 specified with the following traits.
     121There are two more traits for exceptions @is_termination_exception@ and
     122@is_resumption_exception@. They are defined as follows:
     123
    125124\begin{cfa}
    126125trait is_termination_exception(
     
    128127        void defaultTerminationHandler(exceptT &);
    129128};
    130 \end{cfa}
    131 The function is required to allow a termination raise, but is only called if a
    132 termination raise does not find an appropriate handler.
    133 
    134 Allowing a resumption raise is similar.
    135 \begin{cfa}
     129
    136130trait is_resumption_exception(
    137131                exceptT &, virtualT & | is_exception(exceptT, virtualT)) {
     
    139133};
    140134\end{cfa}
    141 The function is required to allow a resumption raise, but is only called if a
    142 resumption raise does not find an appropriate handler.
    143 
    144 Finally there are three convenience macros for referring to the these traits:
     135
     136In other words they make sure that a given type and virtual type is an
     137exception and defines one of the two default handlers. These default handlers
     138are used in the main exception handling operations \see{Exception Handling}
     139and their use will be detailed there.
     140
     141However all three of these traits can be trickly to use directly.
     142There is a bit of repetition required but
     143the largest issue is that the virtual table type is mangled and not in a user
     144facing way. So there are three macros that can be used to wrap these traits
     145when you need to refer to the names:
    145146@IS_EXCEPTION@, @IS_TERMINATION_EXCEPTION@ and @IS_RESUMPTION_EXCEPTION@.
    146 All three traits are hard to use while naming the virtual table as it has an
    147 internal mangled name. These macros take the exception name as their first
    148 argument and do the mangling. They all take a second argument for polymorphic
    149 types which is the parenthesized list of polymorphic arguments. These
    150 arguments are passed to both the exception type and the virtual table type as
    151 the arguments do have to match.
     147
     148All take one or two arguments. The first argument is the name of the
     149exception type. Its unmangled and mangled form are passed to the trait.
     150The second (optional) argument is a parenthesized list of polymorphic
     151arguments. This argument should only with polymorphic exceptions and the
     152list will be passed to both types.
     153In the current set-up the base name and the polymorphic arguments have to
     154match so these macros can be used without losing flexability.
    152155
    153156For example consider a function that is polymorphic over types that have a
     
    158161\end{cfa}
    159162
     163\section{Exception Handling}
     164\CFA provides two kinds of exception handling, termination and resumption.
     165These twin operations are the core of the exception handling mechanism and
     166are the reason for the features of exceptions.
     167This section will cover the general patterns shared by the two operations and
     168then go on to cover the details each individual operation.
     169
     170Both operations follow the same set of steps to do their operation. They both
     171start with the user preforming a throw on an exception.
     172Then there is the search for a handler, if one is found than the exception
     173is caught and the handler is run. After that control returns to normal
     174execution.
     175
     176If the search fails a default handler is run and then control
     177returns to normal execution immediately. That is where the default handlers
     178@defaultTermiationHandler@ and @defaultResumptionHandler@ are used.
     179
    160180\subsection{Termination}
    161181\label{s:Termination}
    162182
    163 Termination raise, called ``throw'', is familiar and used in most programming
    164 languages with exception handling. The semantics of termination is: search the
    165 stack for a matching handler, unwind the stack frames to the matching handler,
    166 execute the handler, and continue execution after the handler. Termination is
    167 used when execution \emph{cannot} return to the throw. To continue execution,
    168 the program must \emph{recover} in the handler from the failed (unwound)
    169 execution at the raise to safely proceed after the handler.
    170 
    171 A termination raise is started with the @throw@ statement:
     183Termination handling is more familiar kind and used in most programming
     184languages with exception handling.
     185It is dynamic, non-local goto. If a throw is successful then the stack will
     186be unwound and control will (usually) continue in a different function on
     187the call stack. They are commonly used when an error has occured and recovery
     188is impossible in the current function.
     189
     190% (usually) Control can continue in the current function but then a different
     191% control flow construct should be used.
     192
     193A termination throw is started with the @throw@ statement:
    172194\begin{cfa}
    173195throw EXPRESSION;
     
    180202change the throw's behavior (see below).
    181203
    182 At runtime, the exception returned by the expression
    183 is copied into managed memory (heap) to ensure it remains in
    184 scope during unwinding. It is the user's responsibility to ensure the original
    185 exception object at the throw is freed when it goes out of scope. Being
    186 allocated on the stack is sufficient for this.
    187 
    188 Then the exception system searches the stack starting from the throw and
    189 proceeding towards the base of the stack, from callee to caller. At each stack
    190 frame, a check is made for termination handlers defined by the @catch@ clauses
    191 of a @try@ statement.
     204The throw will copy the provided exception into managed memory. It is the
     205user's responcibility to ensure the original exception is cleaned up if the
     206stack is unwound (allocating it on the stack should be sufficient).
     207
     208Then the exception system searches the stack using the copied exception.
     209It starts starts from the throw and proceeds to the base of the stack,
     210from callee to caller.
     211At each stack frame, a check is made for resumption handlers defined by the
     212@catch@ clauses of a @try@ statement.
    192213\begin{cfa}
    193214try {
    194215        GUARDED_BLOCK
    195 } catch (EXCEPTION_TYPE$\(_1\)$ * NAME$\(_1\)$) { // termination handler 1
     216} catch (EXCEPTION_TYPE$\(_1\)$ * NAME$\(_1\)$) {
    196217        HANDLER_BLOCK$\(_1\)$
    197 } catch (EXCEPTION_TYPE$\(_2\)$ * NAME$\(_2\)$) { // termination handler 2
     218} catch (EXCEPTION_TYPE$\(_2\)$ * NAME$\(_2\)$) {
    198219        HANDLER_BLOCK$\(_2\)$
    199220}
    200221\end{cfa}
    201 The statements in the @GUARDED_BLOCK@ are executed. If those statements, or any
    202 functions invoked from those statements, throws an exception, and the exception
     222When viewed on its own a try statement will simply exceute the statements in
     223@GUARDED_BLOCK@ and when those are finished the try statement finishes.
     224
     225However, while the guarded statements are being executed, including any
     226functions they invoke, all the handlers following the try block are now
     227or any functions invoked from those
     228statements, throws an exception, and the exception
    203229is not handled by a try statement further up the stack, the termination
    204230handlers are searched for a matching exception type from top to bottom.
     
    211237freed and control continues after the try statement.
    212238
    213 The default handler visible at the throw statement is used if no matching
    214 termination handler is found after the entire stack is searched. At that point,
    215 the default handler is called with a reference to the exception object
    216 generated at the throw. If the default handler returns, control continues
    217 from after the throw statement. This feature allows
    218 each exception type to define its own action, such as printing an informative
    219 error message, when an exception is not handled in the program.
    220 However the default handler for all exception types triggers a cancellation
    221 using the exception.
     239If no handler is found during the search then the default handler is run.
     240Through \CFA's trait system the best match at the throw sight will be used.
     241This function is run and is passed the copied exception. After the default
     242handler is run control continues after the throw statement.
     243
     244There is a global @defaultTerminationHandler@ that cancels the current stack
     245with the copied exception. However it is generic over all exception types so
     246new default handlers can be defined for different exception types and so
     247different exception types can have different default handlers.
    222248
    223249\subsection{Resumption}
    224250\label{s:Resumption}
    225251
    226 Resumption raise, called ``resume'', is as old as termination
    227 raise~\cite{Goodenough75} but is less popular. In many ways, resumption is
    228 simpler and easier to understand, as it is simply a dynamic call.
    229 The semantics of resumption is: search the stack for a matching handler,
    230 execute the handler, and continue execution after the resume. Notice, the stack
    231 cannot be unwound because execution returns to the raise point. Resumption is
    232 used used when execution \emph{can} return to the resume. To continue
    233 execution, the program must \emph{correct} in the handler for the failed
    234 execution at the raise so execution can safely continue after the resume.
     252Resumption exception handling is a less common form than termination but is
     253just as old~\cite{Goodenough75} and is in some sense simpler.
     254It is a dynamic, non-local function call. If the throw is successful a
     255closure will be taken from up the stack and executed, after which the throwing
     256function will continue executing.
     257These are most often used when an error occured and if the error is repaired
     258then the function can continue.
    235259
    236260A resumption raise is started with the @throwResume@ statement:
     
    240264The semantics of the @throwResume@ statement are like the @throw@, but the
    241265expression has return a reference a type that satifies the trait
    242 @is_resumption_exception@. Like with termination the exception system can
    243 use these assertions while (throwing/raising/handling) the exception.
     266@is_resumption_exception@. The assertions from this trait are available to
     267the exception system while handling the exception.
    244268
    245269At runtime, no copies are made. As the stack is not unwound the exception and
    246270any values on the stack will remain in scope while the resumption is handled.
    247271
    248 Then the exception system searches the stack starting from the resume and
    249 proceeding to the base of the stack, from callee to caller. At each stack
    250 frame, a check is made for resumption handlers defined by the @catchResume@
    251 clauses of a @try@ statement.
     272Then the exception system searches the stack using the provided exception.
     273It starts starts from the throw and proceeds to the base of the stack,
     274from callee to caller.
     275At each stack frame, a check is made for resumption handlers defined by the
     276@catchResume@ clauses of a @try@ statement.
    252277\begin{cfa}
    253278try {
     
    259284}
    260285\end{cfa}
    261 The statements in the @GUARDED_BLOCK@ are executed. If those statements, or any
    262 functions invoked from those statements, resumes an exception, and the
    263 exception is not handled by a try statement further up the stack, the
    264 resumption handlers are searched for a matching exception type from top to
    265 bottom. (Note, termination and resumption handlers may be intermixed in a @try@
    266 statement but the kind of raise (throw/resume) only matches with the
    267 corresponding kind of handler clause.)
    268 
    269 The exception search and matching for resumption is the same as for
    270 termination, including exception inheritance. The difference is when control
    271 reaches the end of the handler: the resumption handler returns after the resume
    272 rather than after the try statement. The resume point assumes the handler has
    273 corrected the problem so execution can safely continue.
     286If the handlers are not involved in a search this will simply execute the
     287@GUARDED_BLOCK@ and then continue to the next statement.
     288Its purpose is to add handlers onto the stack.
     289(Note, termination and resumption handlers may be intermixed in a @try@
     290statement but the kind of throw must be the same as the handler for it to be
     291considered as a possible match.)
     292
     293If a search for a resumption handler reaches a try block it will check each
     294@catchResume@ clause, top-to-bottom.
     295At each handler if the thrown exception is or is a child type of
     296@EXCEPTION_TYPE@$_i$ then the a pointer to the exception is bound to
     297@NAME@$_i$ and then @HANDLER_BLOCK@$_i$ is executed. After the block is
     298finished control will return to the @throwResume@ statement.
    274299
    275300Like termination, if no resumption handler is found, the default handler
    276 visible at the resume statement is called, and the system default action is
    277 executed.
    278 
    279 For resumption, the exception system uses stack marking to partition the
    280 resumption search. If another resumption exception is raised in a resumption
    281 handler, the second exception search does not start at the point of the
    282 original raise. (Remember the stack is not unwound and the current handler is
    283 at the top of the stack.) The search for the second resumption starts at the
    284 current point on the stack because new try statements may have been pushed by
    285 the handler or functions called from the handler. If there is no match back to
    286 the point of the current handler, the search skips\label{p:searchskip} the
    287 stack frames already searched by the first resume and continues after
    288 the try statement. The default handler always continues from default
    289 handler associated with the point where the exception is created.
     301visible at the throw statement is called. It will use the best match at the
     302call sight according to \CFA's overloading rules. The default handler is
     303passed the exception given to the throw. When the default handler finishes
     304execution continues after the throw statement.
     305
     306There is a global @defaultResumptionHandler@ is polymorphic over all
     307termination exceptions and preforms a termination throw on the exception.
     308The @defaultTerminationHandler@ for that throw is matched at the original
     309throw statement (the resumption @throwResume@) and it can be customized by
     310introducing a new or better match as well.
     311
     312% \subsubsection?
     313
     314A key difference between resumption and termination is that resumption does
     315not unwind the stack. A side effect that is that when a handler is matched
     316and run it's try block (the guarded statements) and every try statement
     317searched before it are still on the stack. This can lead to the recursive
     318resumption problem.
     319
     320The recursive resumption problem is any situation where a resumption handler
     321ends up being called while it is running.
     322Consider a trivial case:
     323\begin{cfa}
     324try {
     325        throwResume (E &){};
     326} catchResume(E *) {
     327        throwResume (E &){};
     328}
     329\end{cfa}
     330When this code is executed the guarded @throwResume@ will throw, start a
     331search and match the handler in the @catchResume@ clause. This will be
     332call and placed on the stack on top of the try-block. The second throw then
     333throws and will seach the same try block and put call another instance of the
     334same handler leading to an infinite loop.
     335
     336This situation is trivial and easy to avoid, but much more complex cycles
     337can form with multiple handlers and different exception types.
     338
     339To prevent all of these cases we mask sections of the stack, or equvilantly
     340the try statements on the stack, so that the resumption seach skips over
     341them and continues with the next unmasked section of the stack.
     342
     343A section of the stack is marked when it is searched to see if it contains
     344a handler for an exception and unmarked when that exception has been handled
     345or the search was completed without finding a handler.
    290346
    291347% This might need a diagram. But it is an important part of the justification
     
    306362\end{verbatim}
    307363
    308 This resumption search pattern reflects the one for termination, and so
    309 should come naturally to most programmers.
    310 However, it avoids the \emph{recursive resumption} problem.
    311 If parts of the stack are searched multiple times, loops
    312 can easily form resulting in infinite recursion.
    313 
    314 Consider the trivial case:
    315 \begin{cfa}
    316 try {
    317         throwResume (E &){}; // first
    318 } catchResume(E *) {
    319         throwResume (E &){}; // second
    320 }
    321 \end{cfa}
    322 If this handler is ever used it will be placed on top of the stack above the
    323 try statement. If the stack was not masked than the @throwResume@ in the
    324 handler would always be caught by the handler, leading to an infinite loop.
    325 Masking avoids this problem and other more complex versions of it involving
    326 multiple handlers and exception types.
    327 
    328 Other masking stratagies could be used; such as masking the handlers that
    329 have caught an exception. This one was choosen because it creates a symmetry
    330 with termination (masked sections of the stack would be unwound with
    331 termination) and having only one pattern to learn is easier.
     364The rules can be remembered as thinking about what would be searched in
     365termination. So when a throw happens in a handler; a termination handler
     366skips everything from the original throw to the original catch because that
     367part of the stack has been unwound, a resumption handler skips the same
     368section of stack because it has been masked.
     369A throw in a default handler will preform the same search as the original
     370throw because; for termination nothing has been unwound, for resumption
     371the mask will be the same.
     372
     373The symmetry with termination is why this pattern was picked. Other patterns,
     374such as marking just the handlers that caught, also work but lack the
     375symmetry whih means there is more to remember.
    332376
    333377\section{Conditional Catch}
     
    335379condition to further control which exceptions they handle:
    336380\begin{cfa}
    337 catch (EXCEPTION_TYPE * NAME ; @CONDITION@)
     381catch (EXCEPTION_TYPE * NAME ; CONDITION)
    338382\end{cfa}
    339383First, the same semantics is used to match the exception type. Second, if the
     
    341385reference all names in scope at the beginning of the try block and @NAME@
    342386introduced in the handler clause. If the condition is true, then the handler
    343 matches. Otherwise, the exception search continues at the next appropriate kind
    344 of handler clause in the try block.
     387matches. Otherwise, the exception search continues as if the exception type
     388did not match.
    345389\begin{cfa}
    346390try {
     
    356400remaining handlers in the current try statement.
    357401
    358 \section{Reraise}
    359 \color{red}{From Andrew: I recomend we talk about why the language doesn't
     402\section{Rethrowing}
     403\colour{red}{From Andrew: I recomend we talk about why the language doesn't
    360404have rethrows/reraises instead.}
    361405
    362 \label{s:Reraise}
     406\label{s:Rethrowing}
    363407Within the handler block or functions called from the handler block, it is
    364408possible to reraise the most recently caught exception with @throw@ or
    365 @throwResume@, respective.
     409@throwResume@, respectively.
    366410\begin{cfa}
    367411try {
    368412        ...
    369413} catch( ... ) {
    370         ... throw; // rethrow
     414        ... throw;
    371415} catchResume( ... ) {
    372         ... throwResume; // reresume
     416        ... throwResume;
    373417}
    374418\end{cfa}
     
    381425
    382426\section{Finally Clauses}
    383 A @finally@ clause may be placed at the end of a @try@ statement.
     427Finally clauses are used to preform unconditional clean-up when leaving a
     428scope. They are placed at the end of a try statement:
    384429\begin{cfa}
    385430try {
     
    391436\end{cfa}
    392437The @FINALLY_BLOCK@ is executed when the try statement is removed from the
    393 stack, including when the @GUARDED_BLOCK@ or any handler clause finishes or
    394 during an unwind.
     438stack, including when the @GUARDED_BLOCK@ finishes, any termination handler
     439finishes or during an unwind.
    395440The only time the block is not executed is if the program is exited before
    396 that happens.
     441the stack is unwound.
    397442
    398443Execution of the finally block should always finish, meaning control runs off
     
    403448@return@ that causes control to leave the finally block. Other ways to leave
    404449the finally block, such as a long jump or termination are much harder to check,
    405 and at best requiring additional run-time overhead, and so are discouraged.
     450and at best requiring additional run-time overhead, and so are mearly
     451discouraged.
     452
     453Not all languages with exceptions have finally clauses. Notably \Cpp does
     454without it as descructors serve a similar role. Although destructors and
     455finally clauses can be used in many of the same areas they have their own
     456use cases like top-level functions and lambda functions with closures.
     457Destructors take a bit more work to set up but are much easier to reuse while
     458finally clauses are good for once offs and can include local information.
    406459
    407460\section{Cancellation}
     
    413466There is no special statement for starting a cancellation; instead the standard
    414467library function @cancel_stack@ is called passing an exception. Unlike a
    415 raise, this exception is not used in matching only to pass information about
     468throw, this exception is not used in matching only to pass information about
    416469the cause of the cancellation.
    417 
    418 Handling of a cancellation depends on which stack is being cancelled.
     470(This also means matching cannot fail so there is no default handler either.)
     471
     472After @cancel_stack@ is called the exception is copied into the exception
     473handling mechanism's memory. Then the entirety of the current stack is
     474unwound. After that it depends one which stack is being cancelled.
    419475\begin{description}
    420476\item[Main Stack:]
     
    447503happen in an implicate join inside a destructor. So there is an error message
    448504and an abort instead.
     505\todo{Perhaps have a more general disucssion of unwind collisions before
     506this point.}
    449507
    450508The recommended way to avoid the abort is to handle the intial resumption
     
    455513\item[Coroutine Stack:] A coroutine stack is created for a @coroutine@ object
    456514or object that satisfies the @is_coroutine@ trait. A coroutine only knows of
    457 two other coroutines, its starter and its last resumer. The last resumer has
    458 the tightest coupling to the coroutine it activated. Hence, cancellation of
    459 the active coroutine is forwarded to the last resumer after the stack is
    460 unwound, as the last resumer has the most precise knowledge about the current
    461 execution. When the resumer restarts, it resumes exception
     515two other coroutines, its starter and its last resumer. Of the two the last
     516resumer has the tightest coupling to the coroutine it activated and the most
     517up-to-date information.
     518
     519Hence, cancellation of the active coroutine is forwarded to the last resumer
     520after the stack is unwound. When the resumer restarts, it resumes exception
    462521@CoroutineCancelled@, which is polymorphic over the coroutine type and has a
    463522pointer to the cancelled coroutine.
Note: See TracChangeset for help on using the changeset viewer.