source: doc/theses/andrew_beach_MMath/features.tex @ 8483c39a

Last change on this file since 8483c39a was 8483c39a, checked in by Peter A. Buhr <pabuhr@…>, 3 years ago

proofread chapter features

  • Property mode set to 100644
File size: 31.2 KB
1\chapter{Exception Features}
3This chapter covers the design and user interface of the \CFA
4exception-handling mechanism (EHM). % or exception system.
5While an EHM is free to add many features,
6the following overview covers the basic features that all EHMs use, but it is not an
7exhaustive list of everything an EHM can do.
9% We should cover what is an exception handling mechanism and what is an
10% exception before this. Probably in the introduction. Some of this could
11% move there.
12\paragraph{Raise / Handle}
13An exception operation has two main parts: raise and handle.
14These terms are sometimes also known as throw and catch but this work uses
15throw/catch as a particular kind of raise/handle.
16These are the two parts a programmer writes and so
17are the only two pieces of the EHM that have language syntax.
20The raise is the starting point for exception handling and usually how \PAB{This sentence is cut off.}
21Some well known examples include the @throw@ statement of \Cpp and Java and
22the \lstinline[language=Python]{raise} statement from Python.
24For this overview, a raise starts the handling of an
25exception, which is called \newterm{raising} an exception. This simple description is sufficient
26for the overview.
29The purpose of raising an exception is to run user code to address (handle) the
30issue found at the raise point.
31The @try@ statement of \Cpp illustrates a common approach for specifying multiple handlers.
32A handler has three common features: the scope in which it applies, an
33exception label that describes what exceptions it can handle, and code to run
34that deals with the raised issue.
35Each handler can handle exceptions raised in the region matching its
36exception label. For multiple matches, different EHMs have different rules for matching an exception to a handler label,
37such as ``best match" or ``first found".
40After an exception is raised, comes the most complex step for the
41EHM: finding and setting up the handler. This propagation of exception from raise to handler can be broken up into three
42different tasks: searching, matching, and
43installing the handler so it can execute.
46The EHM searches for possible handlers that can be used to handle
47the exception. Searching is usually independent of the exception that is
48thrown and instead depends on the call stack: current function, its caller
49and repeating down the stack.
52For each handler found, it compares the raised exception with the handler label to see which one is the
53best match, and hence, which one should be used to handle the exception.
54In languages where the best match is the first match, these two steps are often
55intertwined, \ie a match check is performed immediately after the search finds
56a possible handler.
59After a handler is chosen, it must be made ready to run.
60This step varies widely to fit with the rest of the
61design of the EHM. The installation step might be trivial or it can be
62the most expensive step in handling an exception. The latter tends to be the
63case when stack unwinding is involved.
64An alternate action occurs if no appropriate handler is found, then some implicit action
65is performed. This step is only required with unchecked
66exceptions as checked exceptions (Java) promise a handler is always found. The implicit action
67also installs a handler but it is a default handle that may be
68installed differently.
71Some EHM (\CFA, Java) organize exceptions in a hierarchical structure.
72This strategy is borrowed from object-orientated languages where the
73exception hierarchy is a natural extension of the object hierarchy.
75Consider the following hierarchy of exceptions:
79A handler labelled with any given exception can handle exceptions of that
80type or any child type of that exception. The root of the exception hierarchy
81(here \lstinline[language=C++]{exception}) acts as a catch-all, leaf types catch single types
82and the exceptions in the middle can be used to catch different groups of
83related exceptions.
85This system has some notable advantages, such as multiple levels of grouping,
86the ability for libraries to add new exception types, and the isolation
87between different sub-hierarchies. This capability had to be adapted for \CFA, which is a
88non-object-orientated language.
90% Could I cite the rational for the Python IO exception rework?
93After the handler has returned, the entire exception operation has to complete
94and continue executing somewhere. This step is usually simple,
95both logically and in its implementation, as the installation of the handler
96usually does the preparation.
97The EHM can return control to different places,
98where the most common are after the handler definition or after the raise.
101For effective exception handling, additional information is usually passed from the raise,
102where this basic model only communicates the exception's identity. A common
103methods for communication is putting fields into an exception and
104allowing a handler to access these fields via an exception instance in the handler's scope.
107Virtual types and casts are not part of an EHM nor are they
108required for an EHM. But as pointed out, an object-oriented-style hierarchy is an
109excellent way of organizing exceptions. Hence, a minimal virtual system has been added
110to \CFA to support hierarchical exceptions.
112The virtual system supports multiple ``trees" of types. Each tree is
113a simple hierarchy with a single root type. Each type in a tree has exactly
114one parent -- except for the root type with zero parents -- and any
115number of children.
116Any type that belongs to any of these trees is called a virtual type.
118% A type's ancestors are its parent and its parent's ancestors.
119% The root type has no ancestors.
120% A type's descendents are its children and its children's descendents.
122Every virtual type has a list of virtual members. Children inherit
123their parent's virtual members but may add new members to it.
124It is important to note that these are virtual members, not virtual methods of an object type.
125However, as \CFA has function pointers, they can be used to mimic virtual
128Each virtual type has a unique id.
129The unique id for the virtual type and all its virtual members are combined
130into a virtual-table type. Each virtual type has a pointer to a virtual table
131as a hidden field.
133Up to this point, a virtual system is similar to ones found in object-oriented
134languages but this is where \CFA diverges. Objects encapsulate a
135single set of behaviours in each type, universally across the entire program,
136and indeed all programs that use that type definition. In this sense, the
137types are ``closed" and cannot be altered.
138However, \CFA types do not encapsulate any behaviour. Instead, traits are used and
139types can satisfy a trait, stop satisfying a trait, or satisfy the same
140trait in a different way depending on the lexical context. In this sense, the types are
141``open" as their behaviour can change in different scopes. This capability means it is impossible to pick
142a single set of functions that represent the type's virtual members.
144Hence, \CFA does not have a single virtual table for a type. A user can define different virtual tables,
145which are filled in at their declaration and given a name.
146That name is used as the virtual table, even if it is defined locally
147inside a function, although lifetime issues must be considered.
148Specifically, an object of a virtual type is ``bound" to a virtual table instance, which
149sets the virtual members for that object. The virtual members can be accessed
150through the object.
152While much of the virtual infrastructure is created, it is currently only used
153internally for exception handling. The only user-level feature is the virtual
154cast, which is the same as the \Cpp \lstinline[language=C++]|dynamic_cast|.
159Note, the syntax and semantics matches a C-cast, rather than the function-like
160\Cpp syntax for special casts. Both the type of @EXPRESSION@ and @TYPE@ must be
161a pointer to a virtual type.
162The cast dynamically checks if the @EXPRESSION@ type is the same or a subtype
163of @TYPE@, and if true, returns a pointer to the
164@EXPRESSION@ object, otherwise it returns @0p@ (null pointer).
167% Leaving until later, hopefully it can talk about actual syntax instead
168% of my many strange macros. Syntax aside I will also have to talk about the
169% features all exceptions support.
171Exceptions are defined by the trait system; there are a series of traits, and
172if a type satisfies them, then it can be used as an exception. The following
173is the base trait all exceptions need to match.
175trait is_exception(exceptT &, virtualT &) {
176        virtualT const & get_exception_vtable(exceptT *);
179The trait is defined over two types, the exception type and the virtual table
180type. These type should have a one-to-one relationship: each exception type has only one virtual
181table type and vice versa. The only assertion in the trait is
182@get_exception_vtable@, which takes a pointer of the exception type and
183returns a reference to the virtual-table type-instance.
185The function @get_exception_vtable@ is actually a constant function.
186Regardless of the value passed in (including the null pointer) it
187returns a reference to the virtual-table instance for that type.
188The reason it is a function instead of a constant is to make type
189annotations easier to write using the exception type rather than the
190virtual-table type, which usually has a mangled name because it is an internal component of the EHM.
191% Also \CFA's trait system handles functions better than constants and doing
192% it this way reduce the amount of boiler plate we need.
194% I did have a note about how it is the programmer's responsibility to make
195% sure the function is implemented correctly. But this is true of every
196% similar system I know of (except Ada's I guess) so I took it out.
198There are two more exception traits defined as follows:
200trait is_termination_exception(
201                exceptT &, virtualT & | is_exception(exceptT, virtualT)) {
202        void defaultTerminationHandler(exceptT &);
205trait is_resumption_exception(
206                exceptT &, virtualT & | is_exception(exceptT, virtualT)) {
207        void defaultResumptionHandler(exceptT &);
210These traits ensure a given type and virtual type are an
211exception type and defines one of the two default handlers. The default handlers
212are used in the main exception-handling operations and discussed in detail in \VRef{s:ExceptionHandling}.
214However, all three of these traits are tricky to use directly.
215While there is a bit of repetition required,
216the largest issue is that the virtual-table type is mangled and not in a user
217facing way. So three macros are provided to wrap these traits
218to simplify referring to the names:
221These macros take one or two arguments. The first argument is the name of the
222exception type. The macro passes the unmangled and mangled form to the trait.
223The second (optional) argument is a parenthesized list of polymorphic
224arguments. This argument is only used with polymorphic exceptions and the
225list is passed to both types.
226In the current set-up, the base name and the polymorphic arguments have to
227match so these macros can be used without losing flexibility.
229For example consider a function that is polymorphic over types that have a
230defined arithmetic exception:
232forall(Num | @IS_EXCEPTION(Arithmetic, Num)@)
233void some_math_function(Num & left, Num & right);
235where the function may raise exception @Arithmetic@ or any of its decedents.
237\section{Exception Handling}
239\CFA provides two kinds of exception handling: termination and resumption.
240These twin mechanisms are the core of the \CFA EHM and
241multiple features are provided to support them.
242This section covers the general patterns shared by the two kinds of exceptions and
243then covers the individual detail operations.
245Both mechanisms follow the same set of steps to do their operations. Both
246start with the user performing an exception raise.
247Then there is the handler search. If one is found, than the exception
248is caught and the handler is run. When the handler returns, control returns to an
249location appropriate for each kind of exception.
252If the search fails, an appropriate default handler, @defaultTermiationHandler@
253or @defaultResumptionHandler@, is run and  control returns to the
254appropriate location.
259Termination handling is familiar and used in most programming
260languages with exception handling.
261It is a dynamic, non-local goto. The raise starts searching, and if matched and handled, the stack is
262unwound and control (usually) continues in the function on
263the call stack containing the handler. Terminate is commonly used for an error where recovery
264is impossible in the function performing the raise.
266% (usually) Control can continue in the current function but then a different
267% control flow construct should be used.
269A termination raise is started with the @throw@ statement:
271throw EXPRESSION;
273The expression must return a reference to a termination exception, where the
274termination exception is any type that satisfies trait
275@is_termination_exception@ at the call site.  Through \CFA's trait system, the
276trait functions are implicitly passed into the hidden throw code and available
277to the exception system while handling the exception. A new
278@defaultTerminationHandler@ can be defined in any scope to change the throw's
279unhandled behaviour (see below).
281The throw must copy the provided exception into managed memory because the stack is unwounded.
282The lifetime of the exception copy is managed by the exception runtime.
283It is the user's responsibility to ensure the original exception is cleaned up, where allocating it on the unwound stack is sufficient.
285The exception search walks the stack matching with the copied exception.
286It starts from the throwing function and proceeds to the base of the stack,
287from callee to caller.
288At each stack frame, a check is made for termination handlers defined by the
289@catch@ clauses of a @try@ statement.
291try {
292        GUARDED_BLOCK
293} catch (EXCEPTION_TYPE$\(_1\)$ [* NAME$\(_1\)$]) {
294        HANDLER_BLOCK$\(_1\)$
295} catch (EXCEPTION_TYPE$\(_2\)$ [* NAME$\(_2\)$]) {
296        HANDLER_BLOCK$\(_2\)$
299When viewed on its own, a @try@ statement with @catch@ clauses simply executes the statements in
300the @GUARDED_BLOCK@, and when those are finished, the try statement finishes.
302However, while the guarded statements are being executed, including any invoked
303functions, a termination exception may be thrown. If that exception is not handled by a try
304statement further up the stack, the handlers following the try block are now
305searched for a matching termination exception-type from top to bottom.
307Exception matching checks each @catch@ clasue from top to bottom, if the representation of the thrown exception-type is
308the same or a descendant type of the exception types in the @catch@ clauses. If
309it is the same or a descendant of @EXCEPTION_TYPE@$_i$, then the optional @NAME@$_i$ is
310bound to a pointer to the exception and the statements in @HANDLER_BLOCK@$_i$
311are executed. If control reaches the end of the handler, the exception is
312freed and control continues after the @try@ statement.
314If no termination handler is found during the search, the default termination
315handler visible at the raise is called.  Through \CFA's trait-system the best
316default-handler match at the throw sight is used.  This function is
317passed the copied exception given to the raise. After the default handler is
318run, control continues after the @throw@ statement.
320There is a global @defaultTerminationHandler@ function that that is polymorphic
321over all exception types allowing new default handlers to be defined for
322different exception types and so different exception types can have different
323default handlers.  The global default termination-handler performs a
324cancellation \see{\VRef{s:Cancellation}} on the current stack with the copied
329Resumption exception-handling is a less common counterpart to termination but is
330just as old~\cite{Goodenough75} and is simpler to understand.
331It is a dynamic, non-local function call (like Lisp). If the throw is successful, a
332closure is taken from up the stack and executed, after which the throwing
333function continues executing.
334Resumption is used when an error occurred, and if the error is repaired,
335then the function can continue.
337An alternative approach is explicitly passing fixup functions with local
338closures up the stack to be called when an error occurs. However, fixup
339functions significantly expand the parameters list of functions, even when the
340fixup function is not used by a function but must be passed to other called
343A resumption raise is started with the @throwResume@ statement:
345throwResume EXPRESSION;
347Like termination, the expression must return a reference to a resumption
348exception, where the resumption exception is any type that satisfies the trait
349@is_termination_exception@ at the call site.
350The assertions for this trait are available to
351the exception system while handling the exception.
353At runtime, no exception copy is made, as the stack is not unwound. Hence, the exception and
354any values on the stack remain in scope while the resumption is handled.
356The exception searches walks the stack matching with the provided exception.
357It starts from the resuming function and proceeds to the base of the stack,
358from callee to caller.
359At each stack frame, a check is made for resumption handlers defined by the
360@catchResume@ clauses of a @try@ statement.
362try {
363        GUARDED_BLOCK
364} catchResume (EXCEPTION_TYPE$\(_1\)$ [* NAME$\(_1\)$]) {
365        HANDLER_BLOCK$\(_1\)$
366} catchResume (EXCEPTION_TYPE$\(_2\)$ [* NAME$\(_2\)$]) {
367        HANDLER_BLOCK$\(_2\)$
370Termination and resumption handlers may be intermixed in a @try@
371statement but the kind of throw must match with kind of handler for it to be
372considered as a possible match.
373Like termination, when viewed on its own, a @try@ statement with
374@catchResume@ clauses simply executes the statements in the @GUARDED_BLOCK@,
375and when those are finished, the try statement finishes.
377However, while the guarded statements are being executed, including any invoked
378functions, a resumption exception may be thrown. If that exception is not handled by a try
379statement further up the stack, the handlers following the try block are now
380searched for a matching resumption exception-type from top to bottom.
382Like termination, exception matching checks each @catch@ clasue from top to bottom, if the representation of the thrown exception-type is
383the same or a descendant type of the exception types in the @catchResume@ clauses. If
384it is the same or a descendant of @EXCEPTION_TYPE@$_i$, then the optional @NAME@$_i$ is
385bound to a pointer to the exception and the statements in @HANDLER_BLOCK@$_i$
386are executed. If control reaches the end of the handler, the exception is
387freed and control continues after the @throwResume@ statement.
389Like termination, if no resumption handler is found during the search, the
390default resumption handler visible at the raise is called, which is the best
391match at the according to \CFA's overloading rules. This function is passed the
392exception given to the raise. After the default handler is run, execution
393continues after the @throwResume@ statement.
395There is a global @defaultResumptionHandler@ that is polymorphic over all
396resumption and preforms a termination throw on the exception.
397The @defaultTerminationHandler@ for that throw is matched at the original
398throw statement (the resumption @throwResume@) and it can be customized by
399introducing a new or better match as well.
401\subsection{Resumption Marking}
402A key difference between resumption and termination is that resumption does
403not unwind the stack. A side effect is that when a handler is matched
404and run its try block (the guarded statements) and every try statement
405searched before it are still on the stack. This can lead to the recursive
406resumption problem.
408The recursive resumption problem is any situation where a resumption handler
409ends up being called while it is running.
410Consider a trivial case:
412try {
413        throwResume (E &){};
414} catchResume(E *) {
415        throwResume (E &){};
418When this code is executed the guarded @throwResume@ starts a
419search and matches the handler in the @catchResume@ clause. The handler is
420called and placed on the stack on top of the try-block. The second throw in the handler
421searches the same try block and calls another instance of the
422same handler leading to an infinite loop.
424While this situation is trivial and easy to avoid, much more complex cycles
425can form with multiple handlers and different exception types.
427To prevent this case, examined try statements on the stack are marked, so that
428subsequent resumption searches skip over them and continue with the next unmarked section
429of the stack.
430Unmarking occurs when that exception is handled
431or the search completes without finding a handler.
433% This might need a diagram. But it is an important part of the justification
434% of the design of the traversal order.
438%       throwResume2 ----------.
439%            |                 |
440% generated from handler       |
441%            |                 |
442%         handler              |
443%            |                 |
444%        throwResume1 -----.   :
445%            |             |   :
446%           try            |   : search skip
447%            |             |   :
448%        catchResume  <----'   :
449%            |                 |
454The resulting search can be understood by thinking about what is searched for
455termination. When a throw happens in a handler, a termination handler
456skips everything from the original throw to the original catch because that
457part of the stack is unwound. A resumption handler skips the same
458section of stack because it is marked.
459A throw in a resumption default-handler performs the same search as the original
460@throwResume@ because for resumption nothing has been unwound.
462The symmetry between resumption masking and termination searching is why this pattern was picked. Other patterns,
463such as marking just the handlers that caught, also work but the
464symmetry seems to match programmer intuition.
466\section{Conditional Catch}
467Both termination and resumption handler-clauses can be given an additional
468condition to further control which exceptions is handled:
472First, the same semantics is used to match the exception type. Second, if the
473exception matches, @CONDITION@ is executed. The condition expression may
474reference all names in the scope of the try block and @NAME@
475introduced in the handler clause. If the condition is true, then the handler
476matches. Otherwise, the exception search continues as if the exception type
477did not match.
479Conditional catch allows fine-gain matching based on object values as well as exception types.
480For example, assume the exception hierarchy @OpenFailure@ $\rightarrow$ @CreateFailure@ and these exceptions are raised by function @open@.
482try {
483        f1 = open( ... ); // open raises CreateFailure/OpenFailure
484        f2 = open( ... ); //    with the associate file
485        ...
486} catch( CreateFailure * f ; @fd( f ) == f1@ ) {
487        // only handle IO failure for f1
488} catch( OpenFailure * f ; @fd( f ) == f2@ ) {
489        // only handle IO failure for f2
492Here, matching is very precise on the I/O exception and particular file with an open problem.
493This capability cannot be easily mimiced within the handler.
495try {
496        f1 = open( ... );
497        f2 = open( ... );
498        ...
499} catch( CreateFailure * f ) {
500        if ( @fd( f ) == f1@ ) ... else // reraise
501} catch( OpenFailure * f ) {
502        if ( @fd( f ) == f2@ ) ... else // reraise
505When an exception @CreateFailure@ is raised, the first handler catches the
506derived exception and reraises it if the object is inappropriate. The reraise
507immediately terminates the current guarded block, which precludes the handler
508for the base exception @OpenFailure@ from consideration for object
509@f2@. Therefore, the ``catch first, then reraise'' approach is an incomplete
510substitute for conditional catch.
514\colour{red}{From Andrew: I recommend we talk about why the language doesn't
515have rethrows/reraises instead.}
517Within the handler block or functions called from the handler block, it is
518possible to reraise the most recently caught exception with @throw@ or
519@throwResume@, respectively.
521try {
522        ...
523} catch( ... ) {
524        ... throw;
525} catchResume( ... ) {
526        ... throwResume;
529The only difference between a raise and a reraise is that reraise does not
530create a new exception; instead it continues using the current exception, \ie
531no allocation and copy. However the default handler is still set to the one
532visible at the raise point, and hence, for termination could refer to data that
533is part of an unwound stack frame. To prevent this problem, a new default
534handler is generated that does a program-level abort.
535\PAB{I don't see how this is different from the normal throw/throwResume.}
537\section{Finally Clauses}
538Finally clauses are used to perform unconditional clean-up when leaving a
539scope and appear at the end of a try statement after any catch clauses:
541try {
542        GUARDED_BLOCK
543} ... // any number or kind of handler clauses
544... finally {
545        FINALLY_BLOCK
548The @FINALLY_BLOCK@ is executed when the try statement is removed from the
549stack, including when the @GUARDED_BLOCK@ finishes, any termination handler
550finishes, or during an unwind.
551The only time the block is not executed is if the program is exited before
552the stack is unwound.
554Execution of the finally block should always finish, meaning control runs off
555the end of the block. This requirement ensures execution always continues as if the
556finally clause is not present, \ie @finally@ is for cleanup not changing control
557flow. Because of this requirement, local control flow out of the finally block
558is forbidden. The compiler precludes any @break@, @continue@, @fallthru@ or
559@return@ that causes control to leave the finally block. Other ways to leave
560the finally block, such as a long jump or termination are much harder to check,
561and at best require additional run-time overhead, and so are
564Not all languages with exceptions have finally clauses. Notably \Cpp does
565without it as destructors serve a similar role. Although destructors and
566finally clauses can be used in many of the same areas, they have their own
567use cases like top-level functions and lambda functions with closures.
568Destructors take a bit more work to set up but are much easier to reuse while
569finally clauses are good for one-off situations and can easily include local information.
573Cancellation is a stack-level abort, which can be thought of as an
574uncatchable termination. It unwinds the entire stack, and when
575possible, forwards the cancellation exception to a different stack.
577Cancellation is not an exception operation like termination or resumption.
578There is no special statement for starting a cancellation; instead the standard
579library function @cancel_stack@ is called passing an exception. Unlike a
580throw, this exception is not used in matching only to pass information about
581the cause of the cancellation.
582(This semantics also means matching cannot fail so there is no default handler.)
584After @cancel_stack@ is called, the exception is copied into the EHM's
585memory and the current stack is
586unwound. After that it depends one which stack is being cancelled.
588\item[Main Stack:]
589The main stack is the one used by the program main at the start of execution,
590and is the only stack in a sequential program. Even in a concurrent program,
591the main stack is often used as the environment to start the concurrent threads.
592Hence, when the main stack is cancelled there is nowhere else in the program
593to go. Hence, after the main stack is unwound, there is a program-level abort.
595\item[Thread Stack:]
596A thread stack is created for a \CFA @thread@ object or object that satisfies the
597@is_thread@ trait. A thread only has two points of communication that must
598happen: start and join. A thread must be running to perform a
599cancellation (a thread cannot cancel another thread). Therefore, a cancellation must
600occur after start and before join, so join is used
601for cancellation communication.
602After the stack is unwound, the thread halts and waits for
603another thread to join with it. The joining thread checks for a cancellation,
604and if present, resumes exception @ThreadCancelled@.
607There is a subtle difference between the explicit join (@join@ function) and
608implicit join (from a @thread@'s destructor call). The explicit join takes the default
609handler (@defaultResumptionHandler@) from its calling context, which is used if
610the exception is not caught. The implicit join does a program abort instead.
613\PAB{uC++ does not have these issues, but catch(...) is not working.}
615#include <iostream>
616using namespace std;
618struct Cl {
619        ~Cl() { cout << "C" << endl; }
621_Coroutine C {
622        void main() {
623                Cl c;
624                try {
625                        cancel();
626                } catch( ... ) {
627                        cout << "..." << endl;
628                } _Finally {
629                        cout << "F" << endl;
630                }
631                }
632  public:
633        void mem() { resume(); }
635_Task T {
636        void main() {
637                Cl c;
638                try {
639                        cancel();
640                } catch( ... ) {
641                        cout << "..." << endl;
642                } _Finally {
643                        cout << "F" << endl;
644                }
645        }
647int main() {
648        C c;
649        cout << "here1" << endl;
650        c.mem();
651        cout << "here2" << endl;
652        {
653                T t;
654        }
655        cout << "here3" << endl;
659\PAB{This discussion should be its own section.}
660This semantics is for safety. If an unwind is triggered while another unwind
661is underway only one of them can proceed as they both want to ``consume" the
662stack. Letting both try to proceed leads to very undefined behaviour.
663Both termination and cancellation involve unwinding and, since the default
664@defaultResumptionHandler@ preforms a termination that could more easily
665happen in an implicate join inside a destructor. So there is an error message
666and an abort instead.
668\todo{Perhaps have a more general disucssion of unwind collisions before
669this point.}
671The recommended way to avoid the abort is to handle the initial resumption
672from the implicate join. If required you may put an explicate join inside a
673finally clause to disable the check and use the local
674@defaultResumptionHandler@ instead.
676\item[Coroutine Stack:] A coroutine stack is created for a @coroutine@ object
677or object that satisfies the @is_coroutine@ trait. A coroutine only knows of
678two other coroutines, its starter and its last resumer. Of the two the last
679resumer has the tightest coupling to the coroutine it activated and the most
680up-to-date information.
682Hence, cancellation of the active coroutine is forwarded to the last resumer
683after the stack is unwound. When the resumer restarts, it resumes exception
684@CoroutineCancelled@, which is polymorphic over the coroutine type and has a
685pointer to the cancelled coroutine.
687The resume function also has an assertion that the @defaultResumptionHandler@
688for the exception. So it will use the default handler like a regular throw.
691\PAB{You should have more test programs that compare \CFA EHM to uC++ EHM.}
Note: See TracBrowser for help on using the repository browser.