source: doc/theses/andrew_beach_MMath/features.tex @ 3b8acfb

Last change on this file since 3b8acfb was 3b8acfb, checked in by Andrew Beach <ajbeach@…>, 3 years ago

Merge branch 'master' into 'andrew-mmath'. Mostly discarded changes to my thesis, I'll fold them in myself.

  • Property mode set to 100644
File size: 34.2 KB
1\chapter{Exception Features}
4This chapter covers the design and user interface of the \CFA EHM
5and begins with a general overview of EHMs. It is not a strict
6definition of all EHMs nor an exhaustive list of all possible features.
7However it does cover the most common structure and features found in them.
9\section{Overview of EHMs}
10% We should cover what is an exception handling mechanism and what is an
11% exception before this. Probably in the introduction. Some of this could
12% move there.
13\subsection{Raise / Handle}
14An exception operation has two main parts: raise and handle.
15These terms are sometimes known as throw and catch but this work uses
16throw/catch as a particular kind of raise/handle.
17These are the two parts that the user writes and may
18be the only two pieces of the EHM that have any syntax in the language.
21The raise is the starting point for exception handling. It marks the beginning
22of exception handling by raising an exception, which passes it to
23the EHM.
25Some well known examples include the @throw@ statements of \Cpp and Java and
26the \code{Python}{raise} statement from Python. In real systems a raise may
27preform some other work (such as memory management) but for the
28purposes of this overview that can be ignored.
31The purpose of most exception operations is to run some user code to handle
32that exception. This code is given, with some other information, in a handler.
34A handler has three common features: the previously mentioned user code, a
35region of code they guard and an exception label/condition that matches
36certain exceptions.
37Only raises inside the guarded region and raising exceptions that match the
38label can be handled by a given handler.
39If multiple handlers could can handle an exception,
40EHMs will define a rule to pick one, such as ``best match" or ``first found".
42The @try@ statements of \Cpp, Java and Python are common examples. All three
43also show another common feature of handlers, they are grouped by the guarded
47After an exception is raised comes what is usually the biggest step for the
48EHM: finding and setting up the handler. The propagation from raise to
49handler can be broken up into three different tasks: searching for a handler,
50matching against the handler and installing the handler.
53The EHM begins by searching for handlers that might be used to handle
54the exception. Searching is usually independent of the exception that was
55thrown as it looks for handlers that have the raise site in their guarded
57The search includes handlers in the current function, as well as any in
58callers on the stack that have the function call in their guarded region.
61Each handler found has to be matched with the raised exception. The exception
62label defines a condition that is used with exception and decides if
63there is a match or not.
65In languages where the first match is used, this step is intertwined with
66searching; a match check is preformed immediately after the search finds
67a possible handler.
70After a handler is chosen it must be made ready to run.
71The implementation can vary widely to fit with the rest of the
72design of the EHM. The installation step might be trivial or it could be
73the most expensive step in handling an exception. The latter tends to be the
74case when stack unwinding is involved.
76If a matching handler is not guaranteed to be found, the EHM needs a
77different course of action for the case where no handler matches.
78This situation only occurs with unchecked exceptions as checked exceptions
79(such as in Java) can make the guarantee.
80This unhandled action is usually very general, such as aborting the program.
83A common way to organize exceptions is in a hierarchical structure.
84This pattern comes from object-orientated languages where the
85exception hierarchy is a natural extension of the object hierarchy.
87Consider the following hierarchy of exceptions:
92A handler labeled with any given exception can handle exceptions of that
93type or any child type of that exception. The root of the exception hierarchy
94(here \code{C}{exception}) acts as a catch-all, leaf types catch single types
95and the exceptions in the middle can be used to catch different groups of
96related exceptions.
98This system has some notable advantages, such as multiple levels of grouping,
99the ability for libraries to add new exception types and the isolation
100between different sub-hierarchies.
101This design is used in \CFA even though it is not a object-orientated
102language; so different tools are used to create the hierarchy.
104% Could I cite the rational for the Python IO exception rework?
107After the handler has finished, the entire exception operation has to complete
108and continue executing somewhere else. This step is usually simple,
109both logically and in its implementation, as the installation of the handler
110is usually set up to do most of the work.
112The EHM can return control to many different places,
113the most common are after the handler definition (termination)
114and after the raise (resumption).
117For effective exception handling, additional information is often passed
118from the raise to the handler and back again.
119So far only communication of the exceptions' identity has been covered.
120A common communication method is putting fields into the exception instance
121and giving the handler access to them.
122Passing the exception by reference instead of by value can allow data to be
123passed in both directions.
127Virtual types and casts are not part of \CFA's EHM nor are they required for
128any EHM.
129However, it is one of the best ways to support an exception hierarchy
130is via a virtual hierarchy and dispatch system.
132Ideally, the virtual system would have been part of \CFA before the work
133on exception handling began, but unfortunately it was not.
134Hence, only the features and framework needed for the EHM were
135designed and implemented. Other features were considered to ensure that
136the structure could accommodate other desirable features in the future
137but they were not implemented.
138The rest of this section will only discuss the implemented subset of the
139virtual system design.
141The virtual system supports multiple ``trees" of types. Each tree is
142a simple hierarchy with a single root type. Each type in a tree has exactly
143one parent -- except for the root type which has zero parents -- and any
144number of children.
145Any type that belongs to any of these trees is called a virtual type.
147% A type's ancestors are its parent and its parent's ancestors.
148% The root type has no ancestors.
149% A type's descendants are its children and its children's descendants.
151Every virtual type also has a list of virtual members. Children inherit
152their parent's list of virtual members but may add new members to it.
153It is important to note that these are virtual members, not virtual methods
154of object-orientated programming, and can be of any type.
156\CFA still supports virtual methods as a special case of virtual members.
157Function pointers that take a pointer to the virtual type are modified
158with each level of inheritance so that refers to the new type.
159This means an object can always be passed to a function in its virtual table
160as if it were a method.
161\todo{Clarify (with an example) virtual methods.}
163Each virtual type has a unique id.
164This id and all the virtual members are combined
165into a virtual table type. Each virtual type has a pointer to a virtual table
166as a hidden field.
167\todo{Might need a diagram for virtual structure.}
169Up until this point the virtual system is similar to ones found in
170object-orientated languages but this where \CFA diverges. Objects encapsulate a
171single set of behaviours in each type, universally across the entire program,
172and indeed all programs that use that type definition. In this sense, the
173types are ``closed" and cannot be altered.
175In \CFA, types do not encapsulate any behaviour. Traits are local and
176types can begin to satisfy a trait, stop satisfying a trait or satisfy the same
177trait in a different way at any lexical location in the program.
178In this sense, they are ``open" as they can change at any time.
179This capability means it is impossible to pick a single set of functions
180that represent the type's implementation across the program.
182\CFA side-steps this issue by not having a single virtual table for each
183type. A user can define virtual tables that are filled in at their
184declaration and given a name. Anywhere that name is visible, even if it is
185defined locally inside a function (although that means it does not have a
186static lifetime), it can be used.
187Specifically, a virtual type is ``bound" to a virtual table that
188sets the virtual members for that object. The virtual members can be accessed
189through the object.
191While much of the virtual infrastructure is created, it is currently only used
192internally for exception handling. The only user-level feature is the virtual
193cast, which is the same as the \Cpp \code{C++}{dynamic_cast}.
198Note, the syntax and semantics matches a C-cast, rather than the function-like
199\Cpp syntax for special casts. Both the type of @EXPRESSION@ and @TYPE@ must be
200a pointer to a virtual type.
201The cast dynamically checks if the @EXPRESSION@ type is the same or a sub-type
202of @TYPE@, and if true, returns a pointer to the
203@EXPRESSION@ object, otherwise it returns @0p@ (null pointer).
206% Leaving until later, hopefully it can talk about actual syntax instead
207% of my many strange macros. Syntax aside I will also have to talk about the
208% features all exceptions support.
210Exceptions are defined by the trait system; there are a series of traits, and
211if a type satisfies them, then it can be used as an exception. The following
212is the base trait all exceptions need to match.
214trait is_exception(exceptT &, virtualT &) {
215        // Numerous imaginary assertions.
218The trait is defined over two types, the exception type and the virtual table
219type. Each exception type should have a single virtual table type.
220There are no actual assertions in this trait because the trait system
221cannot express them yet (adding such assertions would be part of
222completing the virtual system). The imaginary assertions would probably come
223from a trait defined by the virtual system, and state that the exception type
224is a virtual type, is a descendant of @exception_t@ (the base exception type)
225and note its virtual table type.
227% I did have a note about how it is the programmer's responsibility to make
228% sure the function is implemented correctly. But this is true of every
229% similar system I know of (except Agda's I guess) so I took it out.
231There are two more traits for exceptions defined as follows:
233trait is_termination_exception(
234                exceptT &, virtualT & | is_exception(exceptT, virtualT)) {
235        void defaultTerminationHandler(exceptT &);
238trait is_resumption_exception(
239                exceptT &, virtualT & | is_exception(exceptT, virtualT)) {
240        void defaultResumptionHandler(exceptT &);
243Both traits ensure a pair of types are an exception type, its virtual table
245and defines one of the two default handlers. The default handlers are used
246as fallbacks and are discussed in detail in \vref{s:ExceptionHandling}.
248However, all three of these traits can be tricky to use directly.
249While there is a bit of repetition required,
250the largest issue is that the virtual table type is mangled and not in a user
251facing way. So these three macros are provided to wrap these traits to
252simplify referring to the names:
255All three take one or two arguments. The first argument is the name of the
256exception type. The macro passes its unmangled and mangled form to the trait.
257The second (optional) argument is a parenthesized list of polymorphic
258arguments. This argument is only used with polymorphic exceptions and the
259list is be passed to both types.
260In the current set-up, the two types always have the same polymorphic
261arguments so these macros can be used without losing flexibility.
263For example consider a function that is polymorphic over types that have a
264defined arithmetic exception:
266forall(Num | IS_EXCEPTION(Arithmetic, (Num)))
267void some_math_function(Num & left, Num & right);
270\section{Exception Handling}
272As stated,
273\CFA provides two kinds of exception handling: termination and resumption.
274These twin operations are the core of \CFA's exception handling mechanism.
275This section will cover the general patterns shared by the two operations and
276then go on to cover the details each individual operation.
278Both operations follow the same set of steps.
279Both start with the user preforming a raise on an exception.
280Then the exception propagates up the stack.
281If a handler is found the exception is caught and the handler is run.
282After that control continues at a raise-dependent location.
283If the search fails a default handler is run and, if it returns, then control
284continues after the raise.
286This general description covers what the two kinds have in common.
287Differences include how propagation is preformed, where exception continues
288after an exception is caught and handled and which default handler is run.
292Termination handling is the familiar kind and used in most programming
293languages with exception handling.
294It is a dynamic, non-local goto. If the raised exception is matched and
295handled, the stack is unwound and control (usually) continues in the function
296on the call stack that defined the handler.
297Termination is commonly used when an error has occurred and recovery is
298impossible locally.
300% (usually) Control can continue in the current function but then a different
301% control flow construct should be used.
303A termination raise is started with the @throw@ statement:
305throw EXPRESSION;
307The expression must return a reference to a termination exception, where the
308termination exception is any type that satisfies the trait
309@is_termination_exception@ at the call site.
310Through \CFA's trait system, the trait functions are implicitly passed into the
311throw code and the EHM.
312A new @defaultTerminationHandler@ can be defined in any scope to
313change the throw's behaviour (see below).
315The throw copies the provided exception into managed memory to ensure
316the exception is not destroyed if the stack is unwound.
317It is the user's responsibility to ensure the original exception is cleaned
318up whether the stack is unwound or not. Allocating it on the stack is
319usually sufficient.
321% How to say propagation starts, its first sub-step is the search.
322Then propagation starts with the search. \CFA uses a ``first match" rule so
323matching is preformed with the copied exception as the search continues.
324It starts from the throwing function and proceeds towards base of the stack,
325from callee to caller.
326At each stack frame, a check is made for resumption handlers defined by the
327@catch@ clauses of a @try@ statement.
329try {
330        GUARDED_BLOCK
331} catch (EXCEPTION_TYPE$\(_1\)$ * [NAME$\(_1\)$]) {
332        HANDLER_BLOCK$\(_1\)$
333} catch (EXCEPTION_TYPE$\(_2\)$ * [NAME$\(_2\)$]) {
334        HANDLER_BLOCK$\(_2\)$
337When viewed on its own, a try statement simply executes the statements
338in \snake{GUARDED_BLOCK} and when those are finished,
339the try statement finishes.
341However, while the guarded statements are being executed, including any
342invoked functions, all the handlers in these statements are included in the
343search path.
344Hence, if a termination exception is raised these handlers may be matched
345against the exception and may handle it.
347Exception matching checks the handler in each catch clause in the order
348they appear, top to bottom. If the representation of the raised exception type
349is the same or a descendant of @EXCEPTION_TYPE@$_i$ then @NAME@$_i$
350(if provided) is
351bound to a pointer to the exception and the statements in @HANDLER_BLOCK@$_i$
352are executed. If control reaches the end of the handler, the exception is
353freed and control continues after the try statement.
355If no termination handler is found during the search then the default handler
356(\defaultTerminationHandler) visible at the raise statement is run.
357Through \CFA's trait system the best match at the raise statement will be used.
358This function is run and is passed the copied exception.
359If the default handler is run control continues after the raise statement.
361There is a global @defaultTerminationHandler@ that is polymorphic over all
362termination exception types.
363Since it is so general a more specific handler can be
364defined and is used for those types, effectively overriding the handler
365for a particular exception type.
366The global default termination handler performs a cancellation
367(see \vref{s:Cancellation}) on the current stack with the copied exception.
372Resumption exception handling is less common than termination but is
373just as old~\cite{Goodenough75} and is simpler in many ways.
374It is a dynamic, non-local function call. If the raised exception is
375matched a closure is taken from up the stack and executed,
376after which the raising function continues executing.
377The common uses for resumption exceptions include
378potentially repairable errors, where execution can continue in the same
379function once the error is corrected, and
380ignorable events, such as logging where nothing needs to happen and control
381should always continue from the same place.
383A resumption raise is started with the @throwResume@ statement:
385throwResume EXPRESSION;
387\todo{Decide on a final set of keywords and use them everywhere.}
388It works much the same way as the termination throw.
389The expression must return a reference to a resumption exception,
390where the resumption exception is any type that satisfies the trait
391@is_resumption_exception@ at the call site.
392The assertions from this trait are available to
393the exception system while handling the exception.
395At run-time, no exception copy is made.
396Resumption does not unwind the stack nor otherwise remove values from the
397current scope, so there is no need to manage memory to keep things in scope.
399The EHM then begins propagation. The search starts from the raise in the
400resuming function and proceeds towards the base of the stack,
401from callee to caller.
402At each stack frame, a check is made for resumption handlers defined by the
403@catchResume@ clauses of a @try@ statement.
405try {
406        GUARDED_BLOCK
407} catchResume (EXCEPTION_TYPE$\(_1\)$ * [NAME$\(_1\)$]) {
408        HANDLER_BLOCK$\(_1\)$
409} catchResume (EXCEPTION_TYPE$\(_2\)$ * [NAME$\(_2\)$]) {
410        HANDLER_BLOCK$\(_2\)$
413% I wonder if there would be some good central place for this.
414Note that termination handlers and resumption handlers may be used together
415in a single try statement, intermixing @catch@ and @catchResume@ freely.
416Each type of handler only interacts with exceptions from the matching
417kind of raise.
418When a try statement is executed, it simply executes the statements in the
419@GUARDED_BLOCK@ and then finishes.
421However, while the guarded statements are being executed, including any
422invoked functions, all the handlers in these statements are included in the
423search path.
424Hence, if a resumption exception is raised these handlers may be matched
425against the exception and may handle it.
427Exception matching checks the handler in each catch clause in the order
428they appear, top to bottom. If the representation of the raised exception type
429is the same or a descendant of @EXCEPTION_TYPE@$_i$ then @NAME@$_i$
430(if provided) is bound to a pointer to the exception and the statements in
431@HANDLER_BLOCK@$_i$ are executed.
432If control reaches the end of the handler, execution continues after the
433the raise statement that raised the handled exception.
435Like termination, if no resumption handler is found during the search,
436the default handler (\defaultResumptionHandler) visible at the raise
437statement is called. It will use the best match at the raise sight according
438to \CFA's overloading rules. The default handler is
439passed the exception given to the raise. When the default handler finishes
440execution continues after the raise statement.
442There is a global \defaultResumptionHandler{} is polymorphic over all
443resumption exceptions and preforms a termination throw on the exception.
444The \defaultTerminationHandler{} can be overridden by providing a new
445function that is a better match.
447\subsubsection{Resumption Marking}
449A key difference between resumption and termination is that resumption does
450not unwind the stack. A side effect that is that when a handler is matched
451and run it's try block (the guarded statements) and every try statement
452searched before it are still on the stack. There presence can lead to
453the recursive resumption problem.
455The recursive resumption problem is any situation where a resumption handler
456ends up being called while it is running.
457Consider a trivial case:
459try {
460        throwResume (E &){};
461} catchResume(E *) {
462        throwResume (E &){};
465When this code is executed, the guarded @throwResume@ starts a
466search and matches the handler in the @catchResume@ clause. This
467call is placed on the stack above the try-block. The second raise then
468searches the same try block and puts another instance of the
469same handler on the stack leading to infinite recursion.
471While this situation is trivial and easy to avoid, much more complex cycles
472can form with multiple handlers and different exception types.
474To prevent all of these cases, a each try statement is ``marked" from the
475time the exception search reaches it to either when the exception is being
476handled completes the matching handler or when the search reaches the base
477of the stack.
478While a try statement is marked, its handlers are never matched, effectively
479skipping over it to the next try statement.
485There are other sets of marking rules that could be used,
486for instance, marking just the handlers that caught the exception,
487would also prevent recursive resumption.
488However, these rules mirror what happens with termination.
490The try statements that are marked are the ones that would be removed from
491the stack if this was a termination exception, that is those on the stack
492between the handler and the raise statement.
493This symmetry applies to the default handler as well, as both kinds of
494default handlers are run at the raise statement, rather than (physically
495or logically) at the bottom of the stack.
496% In early development having the default handler happen after
497% unmarking was just more useful. We assume that will continue.
499\section{Conditional Catch}
500Both termination and resumption handler clauses can be given an additional
501condition to further control which exceptions they handle:
505First, the same semantics is used to match the exception type. Second, if the
506exception matches, @CONDITION@ is executed. The condition expression may
507reference all names in scope at the beginning of the try block and @NAME@
508introduced in the handler clause. If the condition is true, then the handler
509matches. Otherwise, the exception search continues as if the exception type
510did not match.
512The condition matching allows finer matching by checking
513more kinds of information than just the exception type.
515try {
516        handle1 = open( f1, ... );
517        handle2 = open( f2, ... );
518        handle3 = open( f3, ... );
519        ...
520} catch( IOFailure * f ; fd( f ) == f1 ) {
521        // Only handle IO failure for f1.
522} catch( IOFailure * f ; fd( f ) == f3 ) {
523        // Only handle IO failure for f3.
525// Can't handle a failure relating to f2 here.
527In this example the file that experienced the IO error is used to decide
528which handler should be run, if any at all.
531% I know I actually haven't got rid of them yet, but I'm going to try
532% to write it as if I had and see if that makes sense:
535Within the handler block or functions called from the handler block, it is
536possible to reraise the most recently caught exception with @throw@ or
537@throwResume@, respectively.
539try {
540        ...
541} catch( ... ) {
542        ... throw;
543} catchResume( ... ) {
544        ... throwResume;
547The only difference between a raise and a reraise is that reraise does not
548create a new exception; instead it continues using the current exception, \ie
549no allocation and copy. However the default handler is still set to the one
550visible at the raise point, and hence, for termination could refer to data that
551is part of an unwound stack frame. To prevent this problem, a new default
552handler is generated that does a program-level abort.
555\subsection{Comparison with Reraising}
556A more popular way to allow handlers to match in more detail is to reraise
557the exception after it has been caught, if it could not be handled here.
558On the surface these two features seem interchangeable.
560If @throw;@ (no argument) starts a termination reraise,
561which is the same as a raise but reuses the last caught exception,
562then these two statements have the same behaviour:
564try {
565    do_work_may_throw();
566} catch(exception_t * exc ; can_handle(exc)) {
567    handle(exc);
572try {
573    do_work_may_throw();
574} catch(exception_t * exc) {
575    if (can_handle(exc)) {
576        handle(exc);
577    } else {
578        throw;
579    }
582That is, they will have the same behaviour in isolation.
583Two things can expose differences between these cases.
585One is the existence of multiple handlers on a single try statement.
586A reraise skips all later handlers on this try statement but a conditional
587catch does not.
588Hence, if an earlier handler contains a reraise later handlers are
589implicitly skipped, with a conditional catch they are not.
590Still, they are equivalently powerful,
591both can be used two mimic the behaviour of the other,
592as reraise can pack arbitrary code in the handler and conditional catches
593can put arbitrary code in the predicate.
594% I was struggling with a long explanation about some simple solutions,
595% like repeating a condition on later handlers, and the general solution of
596% merging everything together. I don't think it is useful though unless its
597% for a proof.
600The question then becomes ``Which is a better default?"
601We believe that not skipping possibly useful handlers is a better default.
602If a handler can handle an exception it should and if the handler can not
603handle the exception then it is probably safer to have that explicitly
604described in the handler itself instead of implicitly described by its
605ordering with other handlers.
606% Or you could just alter the semantics of the throw statement. The handler
607% index is in the exception so you could use it to know where to start
608% searching from in the current try statement.
609% No place for the `goto else;` metaphor.
611The other issue is all of the discussion above assumes that the only
612way to tell apart two raises is the exception being raised and the remaining
613search path.
614This is not true generally, the current state of the stack can matter in
615a number of cases, even only for a stack trace after an program abort.
616But \CFA has a much more significant need of the rest of the stack, the
617default handlers for both termination and resumption.
619% For resumption it turns out it is possible continue a raise after the
620% exception has been caught, as if it hadn't been caught in the first place.
621This becomes a problem combined with the stack unwinding used in termination
622exception handling.
623The stack is unwound before the handler is installed, and hence before any
624reraises can run. So if a reraise happens the previous stack is gone,
625the place on the stack where the default handler was supposed to run is gone,
626if the default handler was a local function it may have been unwound too.
627There is no reasonable way to restore that information, so the reraise has
628to be considered as a new raise.
629This is the strongest advantage conditional catches have over reraising,
630they happen before stack unwinding and avoid this problem.
632% The one possible disadvantage of conditional catch is that it runs user
633% code during the exception search. While this is a new place that user code
634% can be run destructors and finally clauses are already run during the stack
635% unwinding.
638%   `exception_ptr current_exception() noexcept;`
641\section{Finally Clauses}
643Finally clauses are used to preform unconditional clean-up when leaving a
644scope and are placed at the end of a try statement after any handler clauses:
646try {
647        GUARDED_BLOCK
648} ... // any number or kind of handler clauses
649... finally {
650        FINALLY_BLOCK
653The @FINALLY_BLOCK@ is executed when the try statement is removed from the
654stack, including when the @GUARDED_BLOCK@ finishes, any termination handler
655finishes or during an unwind.
656The only time the block is not executed is if the program is exited before
657the stack is unwound.
659Execution of the finally block should always finish, meaning control runs off
660the end of the block. This requirement ensures control always continues as if
661the finally clause is not present, \ie finally is for cleanup not changing
662control flow.
663Because of this requirement, local control flow out of the finally block
664is forbidden. The compiler precludes any @break@, @continue@, @fallthru@ or
665@return@ that causes control to leave the finally block. Other ways to leave
666the finally block, such as a long jump or termination are much harder to check,
667and at best requiring additional run-time overhead, and so are only
670Not all languages with unwinding have finally clauses. Notably \Cpp does
671without it as descructors, and the RAII design pattern, serve a similar role.
672Although destructors and finally clauses can be used in the same cases,
673they have their own strengths, similar to top-level function and lambda
674functions with closures.
675Destructors take more work for their first use, but if there is clean-up code
676that needs to be run every time a type is used they soon become much easier
677to set-up.
678On the other hand finally clauses capture the local context, so is easy to
679use when the clean-up is not dependent on the type of a variable or requires
680information from multiple variables.
681% To Peter: I think these are the main points you were going for.
685Cancellation is a stack-level abort, which can be thought of as as an
686uncatchable termination. It unwinds the entire current stack, and if
687possible forwards the cancellation exception to a different stack.
689Cancellation is not an exception operation like termination or resumption.
690There is no special statement for starting a cancellation; instead the standard
691library function @cancel_stack@ is called passing an exception. Unlike a
692raise, this exception is not used in matching only to pass information about
693the cause of the cancellation.
694(This also means matching cannot fail so there is no default handler.)
696After @cancel_stack@ is called the exception is copied into the EHM's memory
697and the current stack is unwound.
698The behaviour after that depends on the kind of stack being cancelled.
700\paragraph{Main Stack}
701The main stack is the one used by the program main at the start of execution,
702and is the only stack in a sequential program.
703After the main stack is unwound there is a program-level abort.
705There are two reasons for these semantics.
706The first is that it had to do this abort.
707in a sequential program as there is nothing else to notify and the simplicity
708of keeping the same behaviour in sequential and concurrent programs is good.
709Also, even in concurrent programs there may not currently be any other stacks
710and even if other stacks do exist, main has no way to know where they are.
712\paragraph{Thread Stack}
713A thread stack is created for a \CFA @thread@ object or object that satisfies
714the @is_thread@ trait.
715After a thread stack is unwound, the exception is stored until another
716thread attempts to join with it. Then the exception @ThreadCancelled@,
717which stores a reference to the thread and to the exception passed to the
718cancellation, is reported from the join to the joining thread.
719There is one difference between an explicit join (with the @join@ function)
720and an implicit join (from a destructor call). The explicit join takes the
721default handler (@defaultResumptionHandler@) from its calling context while
722the implicit join provides its own; which does a program abort if the
723@ThreadCancelled@ exception cannot be handled.
725The communication and synchronization are done here because threads only have
726two structural points (not dependent on user-code) where
727communication/synchronization happens: start and join.
728Since a thread must be running to perform a cancellation (and cannot be
729cancelled from another stack), the cancellation must be after start and
730before the join, so join is used.
732% TODO: Find somewhere to discuss unwind collisions.
733The difference between the explicit and implicit join is for safety and
734debugging. It helps prevent unwinding collisions by avoiding throwing from
735a destructor and prevents cascading the error across multiple threads if
736the user is not equipped to deal with it.
737It is always possible to add an explicit join if that is the desired behaviour.
739With explicit join and a default handler that triggers a cancellation, it is
740possible to cascade an error across any number of threads, cleaning up each
741in turn, until the error is handled or the main thread is reached.
743\paragraph{Coroutine Stack}
744A coroutine stack is created for a @coroutine@ object or object that
745satisfies the @is_coroutine@ trait.
746After a coroutine stack is unwound, control returns to the @resume@ function
747that most recently resumed it. @resume@ reports a
748@CoroutineCancelled@ exception, which contains a references to the cancelled
749coroutine and the exception used to cancel it.
750The @resume@ function also takes the \defaultResumptionHandler{} from the
751caller's context and passes it to the internal report.
753A coroutine knows of two other coroutines, its starter and its last resumer.
754The starter has a much more distant connection, while the last resumer just
755(in terms of coroutine state) called resume on this coroutine, so the message
756is passed to the latter.
758With a default handler that triggers a cancellation, it is possible to
759cascade an error across any number of coroutines, cleaning up each in turn,
760until the error is handled or a thread stack is reached.
Note: See TracBrowser for help on using the repository browser.