[4a36b344] | 1 | \chapter{Features} |
---|
| 2 | |
---|
| 3 | This chapter covers the design and user interface of the \CFA exception |
---|
| 4 | handling mechanism. |
---|
| 5 | |
---|
| 6 | \section{Virtual Casts} |
---|
| 7 | |
---|
| 8 | Virtual casts and virtual types are not truly part of the exception system but |
---|
| 9 | they did not exist in \CFA and are useful in exceptions. So a minimal version |
---|
| 10 | of they virtual system was designed and implemented. |
---|
| 11 | |
---|
[02b73ea] | 12 | Virtual types are organized in simple hierarchies. Each virtual type may have |
---|
[4a36b344] | 13 | a parent and can have any number of children. A type's descendants are its |
---|
| 14 | children and its children's descendants. A type may not be its own descendant. |
---|
| 15 | |
---|
| 16 | Each virtual type has an associated virtual table type. A virtual table is a |
---|
| 17 | structure that has fields for all the virtual members of a type. A virtual |
---|
| 18 | type has all the virtual members of its parent and can add more. It may also |
---|
[02b73ea] | 19 | update the values of the virtual members and should in many cases. |
---|
[4a36b344] | 20 | |
---|
| 21 | Except for virtual casts, this is only used internally in the exception |
---|
| 22 | system. There is no general purpose interface for the other features. A |
---|
| 23 | a virtual cast has the following syntax: |
---|
| 24 | |
---|
| 25 | \begin{lstlisting} |
---|
| 26 | (virtual TYPE)EXPRESSION |
---|
| 27 | \end{lstlisting} |
---|
| 28 | |
---|
[02b73ea] | 29 | This has the same precedence as a traditional C-cast and can be used in the |
---|
[4a36b344] | 30 | same places. This will convert the result of EXPRESSION to the type TYPE. Both |
---|
| 31 | the type of EXPRESSION and TYPE must be pointers to virtual types. |
---|
| 32 | |
---|
| 33 | The cast is checked and will either return the original value or null, based |
---|
| 34 | on the result of the check. The check is does the object pointed at have a |
---|
| 35 | type that is a descendant of the target type. If it is the result is the |
---|
| 36 | pointer, otherwise the result is null. |
---|
| 37 | |
---|
| 38 | \section{Exceptions} |
---|
| 39 | % Leaving until later, hopefully it can talk about actual syntax instead |
---|
| 40 | % of my many strange macros. Syntax aside I will also have to talk about the |
---|
| 41 | % features all exceptions support. |
---|
| 42 | |
---|
[02b73ea] | 43 | \subsection{Exception Traits} |
---|
| 44 | Exceptions are defined by the trait system; there are a series of traits and |
---|
| 45 | if a type satisfies them then they can be used as exceptions. |
---|
| 46 | |
---|
| 47 | \begin{lstlisting} |
---|
| 48 | trait is_exception(dtype exceptT, dtype virtualT) { |
---|
| 49 | virtualT const & get_exception_vtable(exceptT *); |
---|
| 50 | }; |
---|
| 51 | \end{lstlisting} |
---|
| 52 | This is the base trait that all exceptions need to match. |
---|
| 53 | The single function takes any pointer (including the null pointer) and |
---|
| 54 | returns a reference to the virtual table instance. Defining this function |
---|
| 55 | also establishes the virtual type and virtual table pair to the resolver |
---|
[f28fdee] | 56 | and promises that @exceptT@ is a virtual type and a child of the |
---|
[02b73ea] | 57 | base exception type. |
---|
| 58 | |
---|
[f28fdee] | 59 | One odd thing about @get_exception_vtable@ is that it should always |
---|
[02b73ea] | 60 | be a constant function, returning the same value regardless of its argument. |
---|
| 61 | A pointer or reference to the virtual table instance could be used instead, |
---|
| 62 | however using a function has some ease of implementation advantages and |
---|
| 63 | allows for easier disambiguation because the virtual type name (or the |
---|
| 64 | address of an instance that is in scope) can be used instead of the mangled |
---|
| 65 | virtual table name. |
---|
| 66 | |
---|
| 67 | Also note the use of the word ``promise" in the trait description. \CFA |
---|
[f28fdee] | 68 | cannot currently check to see if either @exceptT@ or |
---|
| 69 | @virtualT@ match the layout requirements. Currently this is |
---|
| 70 | considered part of @get_exception_vtable@'s correct implementation. |
---|
[02b73ea] | 71 | |
---|
| 72 | \begin{lstlisting} |
---|
| 73 | trait is_termination_exception( |
---|
| 74 | dtype exceptT, dtype virtualT | is_exception(exceptT, virtualT)) { |
---|
| 75 | void defaultTerminationHandler(exceptT &); |
---|
| 76 | }; |
---|
| 77 | \end{lstlisting} |
---|
| 78 | The only additional function required to make the exception usable with |
---|
| 79 | termination is a default handler. This function is called whenever a |
---|
| 80 | termination throw on an exception of this type is preformed and no handler |
---|
| 81 | is found. |
---|
| 82 | |
---|
| 83 | \begin{lstlisting} |
---|
| 84 | trait is_resumption_exception( |
---|
| 85 | dtype exceptT, dtype virtualT | is_exception(exceptT, virtualT)) { |
---|
| 86 | void defaultResumptionHandler(exceptT &); |
---|
| 87 | }; |
---|
| 88 | \end{lstlisting} |
---|
| 89 | Creating a resumption exception is exactly the same except for resumption. |
---|
| 90 | The name change reflects that and the function is called when a resumption |
---|
| 91 | throw on an exception of this type is preformed and no handler is found. |
---|
| 92 | |
---|
| 93 | Finally there are three additional macros that can be used to refer to the |
---|
[f28fdee] | 94 | these traits. They are @IS_EXCEPTION@, |
---|
| 95 | @IS_TERMINATION_EXCEPTION@ and @IS_RESUMPTION_EXCEPTION@. |
---|
[02b73ea] | 96 | Each takes the virtual type's name and, for polymorphic types only, the |
---|
| 97 | parenthesized list of polymorphic arguments. These do the name mangling to |
---|
| 98 | get the virtual table name and provide the arguments to both sides. |
---|
| 99 | |
---|
[4a36b344] | 100 | \section{Termination} |
---|
| 101 | |
---|
[02b73ea] | 102 | Termination exception throws are likely the most familiar kind, as they are |
---|
[4a36b344] | 103 | used in several popular programming languages. A termination will throw an |
---|
| 104 | exception, search the stack for a handler, unwind the stack to where the |
---|
| 105 | handler is defined, execute the handler and then continue execution after |
---|
| 106 | the handler. They are used when execution cannot continue here. |
---|
| 107 | |
---|
| 108 | Termination has two pieces of syntax it uses. The first is the throw: |
---|
| 109 | \begin{lstlisting} |
---|
| 110 | throw EXPRESSION; |
---|
| 111 | \end{lstlisting} |
---|
| 112 | |
---|
| 113 | The expression must evaluate to a reference to a termination exception. A |
---|
| 114 | termination exception is any exception with a |
---|
[f28fdee] | 115 | @void defaultTerminationHandler(T &);@ (the default handler) defined |
---|
[4a36b344] | 116 | on it. The handler is taken from the call sight with \CFA's trait system and |
---|
| 117 | passed into the exception system along with the exception itself. |
---|
| 118 | |
---|
| 119 | The exception passed into the system is then copied into managed memory. |
---|
| 120 | This is to ensure it remains in scope during unwinding. It is the user's |
---|
| 121 | responsibility to make sure the original exception is freed when it goes out |
---|
| 122 | of scope. Being allocated on the stack is sufficient for this. |
---|
| 123 | |
---|
| 124 | Then the exception system will search the stack starting from the throw and |
---|
[02b73ea] | 125 | proceeding towards the base of the stack, from callee to caller. As it goes |
---|
[4a36b344] | 126 | it will check any termination handlers it finds: |
---|
| 127 | |
---|
| 128 | \begin{lstlisting} |
---|
| 129 | try { |
---|
| 130 | TRY_BLOCK |
---|
| 131 | } catch (EXCEPTION_TYPE * NAME) { |
---|
| 132 | HANDLER |
---|
| 133 | } |
---|
| 134 | \end{lstlisting} |
---|
| 135 | |
---|
| 136 | This shows a try statement with a single termination handler. The statements |
---|
| 137 | in TRY\_BLOCK will be executed when control reaches this statement. While |
---|
| 138 | those statements are being executed if a termination exception is thrown and |
---|
| 139 | it is not handled by a try statement further up the stack the EHM will check |
---|
| 140 | all of the terminations handlers attached to the try block, top to bottom. |
---|
| 141 | |
---|
| 142 | At each handler the EHM will check to see if the thrown exception is a |
---|
| 143 | descendant of EXCEPTION\_TYPE. If it is the pointer to the exception is |
---|
| 144 | bound to NAME and the statements in HANDLER are executed. If control reaches |
---|
| 145 | the end of the handler then it exits the block, the exception is freed and |
---|
| 146 | control continues after the try statement. |
---|
| 147 | |
---|
| 148 | The default handler is only used if no handler for the exception is found |
---|
| 149 | after the entire stack is searched. When that happens the default handler |
---|
| 150 | is called with a reference to the exception as its only argument. If the |
---|
| 151 | handler returns control continues from after the throw statement. |
---|
| 152 | |
---|
| 153 | \paragraph{Conditional Catches} |
---|
| 154 | |
---|
| 155 | Catch clauses may also be written as: |
---|
| 156 | \begin{lstlisting} |
---|
| 157 | catch (EXCEPTION_TYPE * NAME ; CONDITION) |
---|
| 158 | \end{lstlisting} |
---|
| 159 | This has the same behaviour as a regular catch clause except that if the |
---|
| 160 | exception matches the given type the condition is also run. If the result is |
---|
| 161 | true only then is this considered a matching handler. If the result is false |
---|
| 162 | then the handler does not match and the search continues with the next clause |
---|
| 163 | in the try block. |
---|
| 164 | |
---|
| 165 | The condition considers all names in scope at the beginning of the try block |
---|
| 166 | to be in scope along with the name introduce in the catch clause itself. |
---|
| 167 | |
---|
| 168 | \paragraph{Re-Throwing} |
---|
| 169 | |
---|
[02b73ea] | 170 | You can also re-throw the most recent termination exception with |
---|
[f28fdee] | 171 | @throw;@. % This is terrible and you should never do it. |
---|
[4a36b344] | 172 | This can be done in a handler or any function that could be called from a |
---|
| 173 | handler. |
---|
| 174 | |
---|
| 175 | This will start another termination throw reusing the exception, meaning it |
---|
| 176 | does not copy the exception or allocated any more memory for it. However the |
---|
| 177 | default handler is still at the original through and could refer to data that |
---|
| 178 | was on the unwound section of the stack. So instead a new default handler that |
---|
| 179 | does a program level abort is used. |
---|
| 180 | |
---|
| 181 | \section{Resumption} |
---|
| 182 | |
---|
| 183 | Resumption exceptions are less popular then termination but in many |
---|
| 184 | regards are simpler and easier to understand. A resumption throws an exception, |
---|
| 185 | searches for a handler on the stack, executes that handler on top of the stack |
---|
| 186 | and then continues execution from the throw. These are used when a problem |
---|
| 187 | needs to be fixed before execution continues. |
---|
| 188 | |
---|
| 189 | A resumption is thrown with a throw resume statement: |
---|
| 190 | \begin{lstlisting} |
---|
| 191 | throwResume EXPRESSION; |
---|
| 192 | \end{lstlisting} |
---|
| 193 | The result of EXPRESSION must be a resumption exception type. A resumption |
---|
[02b73ea] | 194 | exception type is any type that satisfies the assertion |
---|
[f28fdee] | 195 | @void defaultResumptionHandler(T &);@ (the default handler). When the |
---|
[4a36b344] | 196 | statement is executed the expression is evaluated and the result is thrown. |
---|
| 197 | |
---|
| 198 | Handlers are declared using clauses in try statements: |
---|
| 199 | \begin{lstlisting} |
---|
| 200 | try { |
---|
| 201 | TRY_BLOCK |
---|
| 202 | } catchResume (EXCEPTION_TYPE * NAME) { |
---|
| 203 | HANDLER |
---|
| 204 | } |
---|
| 205 | \end{lstlisting} |
---|
| 206 | This is a simple example with the try block and a single resumption handler. |
---|
| 207 | Multiple resumption handlers can be put in a try statement and they can be |
---|
| 208 | mixed with termination handlers. |
---|
| 209 | |
---|
| 210 | When a resumption begins it will start searching the stack starting from |
---|
| 211 | the throw statement and working its way to the callers. In each try statement |
---|
| 212 | handlers will be tried top to bottom. Each handler is checked by seeing if |
---|
| 213 | the thrown exception is a descendant of EXCEPTION\_TYPE. If not the search |
---|
| 214 | continues. Otherwise NAME is bound to a pointer to the exception and the |
---|
| 215 | HANDLER statements are executed. After they are finished executing control |
---|
| 216 | continues from the throw statement. |
---|
| 217 | |
---|
[02b73ea] | 218 | If no appropriate handler is found then the default handler is called. The |
---|
[4a36b344] | 219 | throw statement acts as a regular function call passing the exception to |
---|
| 220 | the default handler and after the handler finishes executing control continues |
---|
| 221 | from the throw statement. |
---|
| 222 | |
---|
| 223 | The exception system also tracks the position of a search on the stack. If |
---|
| 224 | another resumption exception is thrown while a resumption handler is running |
---|
| 225 | it will first check handlers pushed to the stack by the handler and any |
---|
| 226 | functions it called, then it will continue from the try statement that the |
---|
| 227 | handler is a part of; except for the default handler where it continues from |
---|
| 228 | the throw the default handler was passed to. |
---|
| 229 | |
---|
| 230 | This makes the search pattern for resumption reflect the one for termination, |
---|
| 231 | which is what most users expect. |
---|
| 232 | |
---|
[02b73ea] | 233 | % This might need a diagram. But it is an important part of the justification |
---|
[4a36b344] | 234 | % of the design of the traversal order. |
---|
| 235 | It also avoids the recursive resumption problem. If the entire stack is |
---|
| 236 | searched loops of resumption can form. Consider a handler that handles an |
---|
| 237 | exception of type A by resuming an exception of type B and on the same stack, |
---|
| 238 | later in the search path, is a second handler that handles B by resuming A. |
---|
| 239 | |
---|
| 240 | Assuming no other handlers on the stack handle A or B then in either traversal |
---|
| 241 | system an A resumed from the top of the stack will be handled by the first |
---|
| 242 | handler. A B resumed from the top or from the first handler it will be handled |
---|
[02b73ea] | 243 | by the second handler. The only difference is when A is thrown from the second |
---|
[4a36b344] | 244 | handler. The entire stack search will call the first handler again, creating a |
---|
| 245 | loop. Starting from the position in the stack though will break this loop. |
---|
| 246 | |
---|
| 247 | \paragraph{Conditional Catches} |
---|
| 248 | |
---|
| 249 | Resumption supports conditional catch clauses like termination does. They |
---|
| 250 | use the same syntax except the keyword is changed: |
---|
| 251 | \begin{lstlisting} |
---|
| 252 | catchResume (EXCEPTION_TYPE * NAME ; CONDITION) |
---|
| 253 | \end{lstlisting} |
---|
| 254 | |
---|
| 255 | It also has the same behaviour, after the exception type has been matched |
---|
| 256 | with the EXCEPTION\_TYPE the CONDITION is evaluated with NAME in scope. If |
---|
[02b73ea] | 257 | the result is true then the handler is run, otherwise the search continues |
---|
[4a36b344] | 258 | just as if there had been a type mismatch. |
---|
| 259 | |
---|
| 260 | \paragraph{Re-Throwing} |
---|
| 261 | |
---|
[f28fdee] | 262 | You may also re-throw resumptions with a @throwResume;@ statement. |
---|
| 263 | This can only be done from inside of a @catchResume@ block. |
---|
[4a36b344] | 264 | |
---|
| 265 | Outside of any side effects of any code already run in the handler this will |
---|
| 266 | have the same effect as if the exception had not been caught in the first |
---|
| 267 | place. |
---|
| 268 | |
---|
| 269 | \section{Finally Clauses} |
---|
| 270 | |
---|
[f28fdee] | 271 | A @finally@ clause may be placed at the end of a try statement after |
---|
[4a36b344] | 272 | all the handler clauses. In the simply case, with no handlers, it looks like |
---|
| 273 | this: |
---|
| 274 | |
---|
| 275 | \begin{lstlisting} |
---|
| 276 | try { |
---|
| 277 | TRY_BLOCK |
---|
| 278 | } finally { |
---|
| 279 | FINAL_STATEMENTS |
---|
| 280 | } |
---|
| 281 | \end{lstlisting} |
---|
| 282 | |
---|
| 283 | Any number of termination handlers and resumption handlers may proceed the |
---|
| 284 | finally clause. |
---|
| 285 | |
---|
| 286 | The FINAL\_STATEMENTS, the finally block, are executed whenever the try |
---|
| 287 | statement is removed from the stack. This includes: the TRY\_BLOCK finishes |
---|
| 288 | executing, a termination exception finishes executing and the stack unwinds. |
---|
| 289 | |
---|
| 290 | Execution of the finally block should finish by letting control run off |
---|
| 291 | the end of the block. This is because after the finally block is complete |
---|
| 292 | control will continue to where ever it would if the finally clause was not |
---|
| 293 | present. |
---|
| 294 | |
---|
| 295 | Because of this local control flow out of the finally block is forbidden. |
---|
[f28fdee] | 296 | The compiler rejects uses of @break@, @continue@, |
---|
| 297 | @fallthru@ and @return@ that would cause control to leave |
---|
[4a36b344] | 298 | the finally block. Other ways to leave the finally block - such as a long |
---|
| 299 | jump or termination - are much harder to check, at best requiring additional |
---|
[02b73ea] | 300 | run-time overhead, and so are merely discouraged. |
---|
[4a36b344] | 301 | |
---|
| 302 | \section{Cancellation} |
---|
| 303 | |
---|
| 304 | Cancellation can be thought of as a stack-level abort or as an uncatchable |
---|
| 305 | termination. It unwinds the entirety of the current exception and if possible |
---|
| 306 | passes an exception to a different stack as a message. |
---|
| 307 | |
---|
| 308 | There is no special statement for starting a cancellation, instead you call |
---|
[f28fdee] | 309 | the standard library function @cancel\_stack@ which takes an exception. |
---|
[4a36b344] | 310 | Unlike in a throw this exception is not used in control flow but is just there |
---|
| 311 | to pass information about why the cancellation happened. |
---|
| 312 | |
---|
[02b73ea] | 313 | The handler is decided entirely by which stack is being canceled. There are |
---|
[4a36b344] | 314 | three handlers that apply to three different groups of stacks: |
---|
| 315 | \begin{itemize} |
---|
| 316 | \item Main Stack: |
---|
| 317 | The main stack is the one on which the program main is called at the beginning |
---|
| 318 | of your program. It is also the only stack you have without the libcfathreads. |
---|
| 319 | |
---|
| 320 | Because of this there is no other stack ``above" (or possibly at all) for main |
---|
| 321 | to notify when a cancellation occurs. So after the stack is unwound we do a |
---|
| 322 | program level abort. |
---|
| 323 | |
---|
| 324 | \item Thread Stack: |
---|
[f28fdee] | 325 | Thread stacks are those created @thread@ or otherwise satisfy the |
---|
| 326 | @is\_thread@ trait. |
---|
[4a36b344] | 327 | |
---|
| 328 | Threads only have two structural points of communication that must happen, |
---|
| 329 | start and join. As the thread must be running to preform a cancellation it |
---|
| 330 | will be after start and before join, so join is one cancellation uses. |
---|
| 331 | |
---|
| 332 | After the stack is unwound the thread will halt as if had completed normally |
---|
| 333 | and wait for another thread to join with it. The other thread, when it joins, |
---|
| 334 | checks for a cancellation. If so it will throw the resumption exception |
---|
[f28fdee] | 335 | @ThreadCancelled@. |
---|
[4a36b344] | 336 | |
---|
[f28fdee] | 337 | There is a difference here in how explicate joins (with the @join@ |
---|
[4a36b344] | 338 | function) and implicate joins (from a destructor call). Explicate joins will |
---|
[f28fdee] | 339 | take the default handler (@defaultResumptionHandler@) from the context |
---|
[4a36b344] | 340 | and use like a regular through does if the exception is not caught. The |
---|
| 341 | implicate join does a program abort instead. |
---|
| 342 | |
---|
| 343 | This is for safety. One of the big problems in exceptions is you cannot handle |
---|
| 344 | two terminations or cancellations on the same stack as either can destroy the |
---|
| 345 | context required for the other. This can happen with join but as the |
---|
| 346 | destructors will always be run when the stack is being unwound and one |
---|
[02b73ea] | 347 | termination/cancellation is already active. Also since they are implicit they |
---|
[4a36b344] | 348 | are easier to forget about. |
---|
| 349 | |
---|
| 350 | \item Coroutine Stack: |
---|
[f28fdee] | 351 | Coroutine stacks are those created with @coroutine@ or otherwise |
---|
| 352 | satisfy the @is\_coroutine@ trait. |
---|
[4a36b344] | 353 | |
---|
| 354 | A coroutine knows of two other coroutines, its starter and its last resumer. |
---|
| 355 | The last resumer is ``closer" so that is the one notified. |
---|
| 356 | |
---|
| 357 | After the stack is unwound control goes to the last resumer. |
---|
[f28fdee] | 358 | Resume will resume throw a @CoroutineCancelled@ exception, which is |
---|
[4a36b344] | 359 | polymorphic over the coroutine type and has a pointer to the coroutine being |
---|
[02b73ea] | 360 | canceled and the canceling exception. The resume function also has an |
---|
[f28fdee] | 361 | assertion that the @defaultResumptionHandler@ for the exception. So it |
---|
[4a36b344] | 362 | will use the default handler like a regular throw. |
---|
| 363 | |
---|
| 364 | \end{itemize} |
---|