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 |
|
---|
12 | Virtual types are organizied in simple hierarchies. Each virtual type may have
|
---|
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
|
---|
19 | update the values of the virtual members.
|
---|
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 |
|
---|
29 | This has the same precidence as a traditional C-cast and can be used in the
|
---|
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 |
|
---|
43 | \section{Termination}
|
---|
44 |
|
---|
45 | Termination exception throws are likely the most framilar kind, as they are
|
---|
46 | used in several popular programming languages. A termination will throw an
|
---|
47 | exception, search the stack for a handler, unwind the stack to where the
|
---|
48 | handler is defined, execute the handler and then continue execution after
|
---|
49 | the handler. They are used when execution cannot continue here.
|
---|
50 |
|
---|
51 | Termination has two pieces of syntax it uses. The first is the throw:
|
---|
52 | \begin{lstlisting}
|
---|
53 | throw EXPRESSION;
|
---|
54 | \end{lstlisting}
|
---|
55 |
|
---|
56 | The expression must evaluate to a reference to a termination exception. A
|
---|
57 | termination exception is any exception with a
|
---|
58 | \codeCFA{void defaultTerminationHandler(T &);} (the default handler) defined
|
---|
59 | on it. The handler is taken from the call sight with \CFA's trait system and
|
---|
60 | passed into the exception system along with the exception itself.
|
---|
61 |
|
---|
62 | The exception passed into the system is then copied into managed memory.
|
---|
63 | This is to ensure it remains in scope during unwinding. It is the user's
|
---|
64 | responsibility to make sure the original exception is freed when it goes out
|
---|
65 | of scope. Being allocated on the stack is sufficient for this.
|
---|
66 |
|
---|
67 | Then the exception system will search the stack starting from the throw and
|
---|
68 | proceding towards the base of the stack, from callee to caller. As it goes
|
---|
69 | it will check any termination handlers it finds:
|
---|
70 |
|
---|
71 | \begin{lstlisting}
|
---|
72 | try {
|
---|
73 | TRY_BLOCK
|
---|
74 | } catch (EXCEPTION_TYPE * NAME) {
|
---|
75 | HANDLER
|
---|
76 | }
|
---|
77 | \end{lstlisting}
|
---|
78 |
|
---|
79 | This shows a try statement with a single termination handler. The statements
|
---|
80 | in TRY\_BLOCK will be executed when control reaches this statement. While
|
---|
81 | those statements are being executed if a termination exception is thrown and
|
---|
82 | it is not handled by a try statement further up the stack the EHM will check
|
---|
83 | all of the terminations handlers attached to the try block, top to bottom.
|
---|
84 |
|
---|
85 | At each handler the EHM will check to see if the thrown exception is a
|
---|
86 | descendant of EXCEPTION\_TYPE. If it is the pointer to the exception is
|
---|
87 | bound to NAME and the statements in HANDLER are executed. If control reaches
|
---|
88 | the end of the handler then it exits the block, the exception is freed and
|
---|
89 | control continues after the try statement.
|
---|
90 |
|
---|
91 | The default handler is only used if no handler for the exception is found
|
---|
92 | after the entire stack is searched. When that happens the default handler
|
---|
93 | is called with a reference to the exception as its only argument. If the
|
---|
94 | handler returns control continues from after the throw statement.
|
---|
95 |
|
---|
96 | \paragraph{Conditional Catches}
|
---|
97 |
|
---|
98 | Catch clauses may also be written as:
|
---|
99 | \begin{lstlisting}
|
---|
100 | catch (EXCEPTION_TYPE * NAME ; CONDITION)
|
---|
101 | \end{lstlisting}
|
---|
102 | This has the same behaviour as a regular catch clause except that if the
|
---|
103 | exception matches the given type the condition is also run. If the result is
|
---|
104 | true only then is this considered a matching handler. If the result is false
|
---|
105 | then the handler does not match and the search continues with the next clause
|
---|
106 | in the try block.
|
---|
107 |
|
---|
108 | The condition considers all names in scope at the beginning of the try block
|
---|
109 | to be in scope along with the name introduce in the catch clause itself.
|
---|
110 |
|
---|
111 | \paragraph{Re-Throwing}
|
---|
112 |
|
---|
113 | You can also rethrow the most recent termination exception with
|
---|
114 | \codeCFA{throw;}. % This is terrible and you should never do it.
|
---|
115 | This can be done in a handler or any function that could be called from a
|
---|
116 | handler.
|
---|
117 |
|
---|
118 | This will start another termination throw reusing the exception, meaning it
|
---|
119 | does not copy the exception or allocated any more memory for it. However the
|
---|
120 | default handler is still at the original through and could refer to data that
|
---|
121 | was on the unwound section of the stack. So instead a new default handler that
|
---|
122 | does a program level abort is used.
|
---|
123 |
|
---|
124 | \section{Resumption}
|
---|
125 |
|
---|
126 | Resumption exceptions are less popular then termination but in many
|
---|
127 | regards are simpler and easier to understand. A resumption throws an exception,
|
---|
128 | searches for a handler on the stack, executes that handler on top of the stack
|
---|
129 | and then continues execution from the throw. These are used when a problem
|
---|
130 | needs to be fixed before execution continues.
|
---|
131 |
|
---|
132 | A resumption is thrown with a throw resume statement:
|
---|
133 | \begin{lstlisting}
|
---|
134 | throwResume EXPRESSION;
|
---|
135 | \end{lstlisting}
|
---|
136 | The result of EXPRESSION must be a resumption exception type. A resumption
|
---|
137 | exception type is any type that satifies the assertion
|
---|
138 | \codeCFA{void defaultResumptionHandler(T &);} (the default handler). When the
|
---|
139 | statement is executed the expression is evaluated and the result is thrown.
|
---|
140 |
|
---|
141 | Handlers are declared using clauses in try statements:
|
---|
142 | \begin{lstlisting}
|
---|
143 | try {
|
---|
144 | TRY_BLOCK
|
---|
145 | } catchResume (EXCEPTION_TYPE * NAME) {
|
---|
146 | HANDLER
|
---|
147 | }
|
---|
148 | \end{lstlisting}
|
---|
149 | This is a simple example with the try block and a single resumption handler.
|
---|
150 | Multiple resumption handlers can be put in a try statement and they can be
|
---|
151 | mixed with termination handlers.
|
---|
152 |
|
---|
153 | When a resumption begins it will start searching the stack starting from
|
---|
154 | the throw statement and working its way to the callers. In each try statement
|
---|
155 | handlers will be tried top to bottom. Each handler is checked by seeing if
|
---|
156 | the thrown exception is a descendant of EXCEPTION\_TYPE. If not the search
|
---|
157 | continues. Otherwise NAME is bound to a pointer to the exception and the
|
---|
158 | HANDLER statements are executed. After they are finished executing control
|
---|
159 | continues from the throw statement.
|
---|
160 |
|
---|
161 | If no approprate handler is found then the default handler is called. The
|
---|
162 | throw statement acts as a regular function call passing the exception to
|
---|
163 | the default handler and after the handler finishes executing control continues
|
---|
164 | from the throw statement.
|
---|
165 |
|
---|
166 | The exception system also tracks the position of a search on the stack. If
|
---|
167 | another resumption exception is thrown while a resumption handler is running
|
---|
168 | it will first check handlers pushed to the stack by the handler and any
|
---|
169 | functions it called, then it will continue from the try statement that the
|
---|
170 | handler is a part of; except for the default handler where it continues from
|
---|
171 | the throw the default handler was passed to.
|
---|
172 |
|
---|
173 | This makes the search pattern for resumption reflect the one for termination,
|
---|
174 | which is what most users expect.
|
---|
175 |
|
---|
176 | % This might need a diagram. But it is an important part of the justifaction
|
---|
177 | % of the design of the traversal order.
|
---|
178 | It also avoids the recursive resumption problem. If the entire stack is
|
---|
179 | searched loops of resumption can form. Consider a handler that handles an
|
---|
180 | exception of type A by resuming an exception of type B and on the same stack,
|
---|
181 | later in the search path, is a second handler that handles B by resuming A.
|
---|
182 |
|
---|
183 | Assuming no other handlers on the stack handle A or B then in either traversal
|
---|
184 | system an A resumed from the top of the stack will be handled by the first
|
---|
185 | handler. A B resumed from the top or from the first handler it will be handled
|
---|
186 | by the second hander. The only difference is when A is thrown from the second
|
---|
187 | handler. The entire stack search will call the first handler again, creating a
|
---|
188 | loop. Starting from the position in the stack though will break this loop.
|
---|
189 |
|
---|
190 | \paragraph{Conditional Catches}
|
---|
191 |
|
---|
192 | Resumption supports conditional catch clauses like termination does. They
|
---|
193 | use the same syntax except the keyword is changed:
|
---|
194 | \begin{lstlisting}
|
---|
195 | catchResume (EXCEPTION_TYPE * NAME ; CONDITION)
|
---|
196 | \end{lstlisting}
|
---|
197 |
|
---|
198 | It also has the same behaviour, after the exception type has been matched
|
---|
199 | with the EXCEPTION\_TYPE the CONDITION is evaluated with NAME in scope. If
|
---|
200 | the result is true then the hander is run, otherwise the search continues
|
---|
201 | just as if there had been a type mismatch.
|
---|
202 |
|
---|
203 | \paragraph{Re-Throwing}
|
---|
204 |
|
---|
205 | You may also re-throw resumptions with a \codeCFA{throwResume;} statement.
|
---|
206 | This can only be done from inside of a \codeCFA{catchResume} block.
|
---|
207 |
|
---|
208 | Outside of any side effects of any code already run in the handler this will
|
---|
209 | have the same effect as if the exception had not been caught in the first
|
---|
210 | place.
|
---|
211 |
|
---|
212 | \section{Finally Clauses}
|
---|
213 |
|
---|
214 | A \codeCFA{finally} clause may be placed at the end of a try statement after
|
---|
215 | all the handler clauses. In the simply case, with no handlers, it looks like
|
---|
216 | this:
|
---|
217 |
|
---|
218 | \begin{lstlisting}
|
---|
219 | try {
|
---|
220 | TRY_BLOCK
|
---|
221 | } finally {
|
---|
222 | FINAL_STATEMENTS
|
---|
223 | }
|
---|
224 | \end{lstlisting}
|
---|
225 |
|
---|
226 | Any number of termination handlers and resumption handlers may proceed the
|
---|
227 | finally clause.
|
---|
228 |
|
---|
229 | The FINAL\_STATEMENTS, the finally block, are executed whenever the try
|
---|
230 | statement is removed from the stack. This includes: the TRY\_BLOCK finishes
|
---|
231 | executing, a termination exception finishes executing and the stack unwinds.
|
---|
232 |
|
---|
233 | Execution of the finally block should finish by letting control run off
|
---|
234 | the end of the block. This is because after the finally block is complete
|
---|
235 | control will continue to where ever it would if the finally clause was not
|
---|
236 | present.
|
---|
237 |
|
---|
238 | Because of this local control flow out of the finally block is forbidden.
|
---|
239 | The compiler rejects uses of \codeCFA{break}, \codeCFA{continue},
|
---|
240 | \codeCFA{fallthru} and \codeCFA{return} that would cause control to leave
|
---|
241 | the finally block. Other ways to leave the finally block - such as a long
|
---|
242 | jump or termination - are much harder to check, at best requiring additional
|
---|
243 | runtime overhead, and so are merely discouraged.
|
---|
244 |
|
---|
245 | \section{Cancellation}
|
---|
246 |
|
---|
247 | Cancellation can be thought of as a stack-level abort or as an uncatchable
|
---|
248 | termination. It unwinds the entirety of the current exception and if possible
|
---|
249 | passes an exception to a different stack as a message.
|
---|
250 |
|
---|
251 | There is no special statement for starting a cancellation, instead you call
|
---|
252 | the standard libary function \codeCFA{cancel\_stack} which takes an exception.
|
---|
253 | Unlike in a throw this exception is not used in control flow but is just there
|
---|
254 | to pass information about why the cancellation happened.
|
---|
255 |
|
---|
256 | The handler is decided entirely by which stack is being cancelled. There are
|
---|
257 | three handlers that apply to three different groups of stacks:
|
---|
258 | \begin{itemize}
|
---|
259 | \item Main Stack:
|
---|
260 | The main stack is the one on which the program main is called at the beginning
|
---|
261 | of your program. It is also the only stack you have without the libcfathreads.
|
---|
262 |
|
---|
263 | Because of this there is no other stack ``above" (or possibly at all) for main
|
---|
264 | to notify when a cancellation occurs. So after the stack is unwound we do a
|
---|
265 | program level abort.
|
---|
266 |
|
---|
267 | \item Thread Stack:
|
---|
268 | Thread stacks are those created \codeCFA{thread} or otherwise satify the
|
---|
269 | \codeCFA{is\_thread} trait.
|
---|
270 |
|
---|
271 | Threads only have two structural points of communication that must happen,
|
---|
272 | start and join. As the thread must be running to preform a cancellation it
|
---|
273 | will be after start and before join, so join is one cancellation uses.
|
---|
274 |
|
---|
275 | After the stack is unwound the thread will halt as if had completed normally
|
---|
276 | and wait for another thread to join with it. The other thread, when it joins,
|
---|
277 | checks for a cancellation. If so it will throw the resumption exception
|
---|
278 | \codeCFA{ThreadCancelled}.
|
---|
279 |
|
---|
280 | There is a difference here in how explicate joins (with the \codeCFA{join}
|
---|
281 | function) and implicate joins (from a destructor call). Explicate joins will
|
---|
282 | take the default handler (\codeCFA{defaultResumptionHandler}) from the context
|
---|
283 | and use like a regular through does if the exception is not caught. The
|
---|
284 | implicate join does a program abort instead.
|
---|
285 |
|
---|
286 | This is for safety. One of the big problems in exceptions is you cannot handle
|
---|
287 | two terminations or cancellations on the same stack as either can destroy the
|
---|
288 | context required for the other. This can happen with join but as the
|
---|
289 | destructors will always be run when the stack is being unwound and one
|
---|
290 | termination/cancellation is already active. Also since they are implicite they
|
---|
291 | are easier to forget about.
|
---|
292 |
|
---|
293 | \item Coroutine Stack:
|
---|
294 | Coroutine stacks are those created with \codeCFA{coroutine} or otherwise
|
---|
295 | satify the \codeCFA{is\_coroutine} trait.
|
---|
296 |
|
---|
297 | A coroutine knows of two other coroutines, its starter and its last resumer.
|
---|
298 | The last resumer is ``closer" so that is the one notified.
|
---|
299 |
|
---|
300 | After the stack is unwound control goes to the last resumer.
|
---|
301 | Resume will resume throw a \codeCFA{CoroutineCancelled} exception, which is
|
---|
302 | polymorphic over the coroutine type and has a pointer to the coroutine being
|
---|
303 | cancelled and the cancelling exception. The resume function also has an
|
---|
304 | assertion that the \codeCFA{defaultResumptionHandler} for the exception. So it
|
---|
305 | will use the default handler like a regular throw.
|
---|
306 |
|
---|
307 | \end{itemize}
|
---|